JavaScript继承学习笔记【新手必看】


Posted in Javascript onMay 10, 2016

JavaScript作为一个面向对象语言(JS是基于对象的),可以实现继承是必不可少的,但是由于本身并没有类的概念,所以不会像真正的面向对象编程语言通过类实现继承,但可以通过其他方法实现继承。实现继承的方法很多,下面就只是其中的几种。

一. 原型链继承

function Person() {  //被继承的函数叫做超类型(父类,基类)
      this.name='mumu';
      this.age='18';
    }
    
    Person.prototype.name='susu';//当属性名相同时需就近原则,先在实例里面查找,没找到再到原型里找
       
    function Worker(){ //继承的函数叫做子类型(子类,派生类)
      this.job='student';
    }

    Worker.prototype=new Person();//通过原型链继承,超类型实例化后的对象实例,赋值给子类的原型属性
    var p2=new Worker(); 

    console.log(p2.name);
    console.log(p2 instanceof Object);//ture 所有的构造函数都继承自Object

以上实现继承关键在于:Worker.prototype=new Person();  将Worker的原型成为Person的一个实例,通过原型链继承。

注意:在使用原型链实现继承时,不能使用对象字面量创建原型方法,因为这样会中断关系而重写原型链。

原型链继承问题:

1.出现引用共享问题,他们还是共用一个空间,子类会影响父类

function Person() {  
      this.bodys=['eye','foot'];
    }
    
    function Worker(){ 
    }

    Worker.prototype=new Person();
    var p1=new Worker();
    p1.bodys.push('hand');
    var p2=new Worker(); 
    console.log(p1.bodys);
    console.log(p2.bodys);

JavaScript继承学习笔记【新手必看】

2.在创建子类型的实例时,不能像超类型的构造函数中传递参数。

那么如何解决原型链的两个问题呢?那就继续看下面的继承方式吧~

二. 借用构造函数继承(也叫对象冒充,伪造对象或经典继承)

function Person(name,age){
    this.name=name;
    this.age=age;
    this.bodys=['eye','foot'];
  }

  Person.prototype.showName=function(){
    console.log(this.name);
  }

  function Worker(name,age,job){
    Person.call(this,name,age);
    this.job=job;//子类添加属性
  }

  var p1=new Worker('mumu','18','学生'); 
  p1.bodys.push('hand') ;
  var p2=new Worker();

  console.log(p1.name);
  console.log(p2.bodys);
  console.log(p1.showName());

JavaScript继承学习笔记【新手必看】

简单分析下以上使用借用构造函数的原理:Person.call(this,name,age);这句代码调用父级构造函数,继承父级属性,使用call方法调用Person构造函数改变函数执行时候的this,  这里的this-> new出来的一个Worker对象  构造函数伪装方法:把Worker传给上面的Person。

当引用类型放在构造函数里面的时候就不会被共享,所以p2不受影响。

这里借用构造函数继承方式就解决了原型链不能传递参数以及引用类型共享的问题。

小知识:call()和apply()方法可以改变函数执行的作用域, 简言之就是改变函数中this指向的内容。

call()和apply()都接受两个参数:第一个是在其中运行函数的作用域,另一个是传递的参数。

call和apply的区别就是参数的不同.
call中的参数必须是一个个枚举出来的.
apply中的参数必须是数组或者是arguments对象

那么问题来了:为什么p1.showName()结果是错误的呢?----因为借用构造函数继承方式只能继承构造函数里的属性和方法。这里也就发现了借用构造函数的一个问题。

注意:由于把方法都放在构造函数里,每次我们实例化就会分配内存空间给它造成资源的浪费,所以一般我们都是把方法放在原型里,属性放在构造函数里。

借用构造函数继承问题:

因为借用构造函数只能继承构造函数里的属性和方法,在超类型的原型中定义的方法对子类而言是不可见的,所以就相当于没有了原型。结果所有的方法都只能在构造函数里定义,因此就没有函数复用了。

那么如何解决借用构造函数所产生的问题呢?那就要看下面这种继承方式了

三. 组合继承(伪经典继承)

function Person(name,age){
    this.name=name;
    this.age=age;
  }

  Person.prototype.showName=function(){
    console.log(this.name);
  }

  function Worker(name,age,job){
    Person.call(this,name,age);//借用构造函数
    this.job=job;
  }
  
  Worker.prototype=new Person();//原型链继承
  var p1=new Worker('mumu','18','学生'); 

  console.log(p1.age);
  p1.showName();

组合继承:将原型链与借用构造函数结合。

思路:通过使用原型链实现原型上的属性和方法继承,借用构造函数实现实例属性的继承

以上的例子Person.call(this,name,age);借用构造函数继承了属性

Worker.prototype=new Person();原型链继承了方法 , 避免了两者的缺点,融合了它们的优点,成为最常用的继承模式。

组合继承的问题:

调用两次超类型构造函数,一次是在创建子类型原型时,另一次是在子类型的构造函数内部。

要解决这个问题就要用到寄生组合式继承方式了。

四. 原型式继承

function object(proto) {
    function F() {}
    F.prototype = proto;
    return new F();
  }

  var person = {
    name: 'mumu',
    friends: ['xiaxia', 'susu']
  };

  var anotherPerson = object(person);
  anotherPerson.friends.push('wen');
  var yetAnotherPerson = object(person);
  anotherPerson.friends.push('tian');
  console.log(person.friends);//["xiaxia", "susu", "wen", "tian"]
console.log(anotherPerson.__proto__)//Object {name: "mumu", friends: Array[4]}

简单分析下:function object(proto)是一个临时中转函数,里面的参数proto表示将要传递进入的一个对象,F()构造函数是临时新建的对象,用来存储传递过来的对象,F.prototype = proto;将对象实例赋值给F构造函数的原型对象,最后返回传递过来的对象的对象实例。原型式继承还是会共享引用类型的属性。

五. 寄生式继承

//临时中转函数  
function object(proto) {
    function F() {}
    F.prototype = proto;
    return new F();
  }

  //寄生函数
  function create(proto){
    var f=object(proto);
    f.love=function(){
      return this.name;
    }
    return f;
  }

  var person = {
    name: 'mumu',
    friends: ['xiaxia', 'susu']
  };

  var anotherPerson = create(person);
  console.log(anotherPerson.love());寄生组合式继承

六. 寄生组合式继承

function object(proto) {
    function F() {}
    F.prototype = proto;
    return new F();
  }

  //寄生函数
  function create(Person,Worker){
    var f=object(Person.prototype);//创建对象
    f.constructor=Worker;//调整原型构造指针,增强对象
    Worker.prototype=f;//指定对象
  }
  
  function Person(name,age){
    this.name=name;
    this.age=age;
  }
  Person.prototype.showName=function(){
    console.log(this.name);
  }
  function Worker(name,age,job){
    Person.call(this,name,age);
    this.job=job;
  }
  
  create(Person,Worker);//寄生组合式继承
  var p1=new Person('mumu','18','学生');
p1.showName();

这种方法也是现在实现继承方法中最完美的,也是最理想的。

以上这篇JavaScript继承学习笔记【新手必看】就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js更优雅的兼容
Aug 12 Javascript
javascript中的关于类型转换的性能优化
Dec 14 Javascript
js报错 Object doesn't support this property or method的原因分析
Mar 31 Javascript
关于锚点跳转及jQuery下相关操作与插件
Oct 01 Javascript
javascript中setTimeout的问题解决方法
May 08 Javascript
属于你的jQuery提示框(Tip)插件
Jan 20 Javascript
js获取客户端操作系统类型的方法【测试可用】
May 27 Javascript
EXT中单击button按钮grid添加一行(光标位置可设置)的实例代码
Jun 02 Javascript
深入理解JavaScript中的块级作用域、私有变量与模块模式
Oct 31 Javascript
JS调用打印机功能简单示例
Nov 28 Javascript
Webpack中SplitChunksPlugin 配置参数详解
Mar 24 Javascript
Vue2项目中对百度地图的封装使用详解
Jun 16 Vue.js
实例讲解使用原生JavaScript处理AJAX请求的方法
May 10 #Javascript
深入剖析JavaScript:Object类型
May 10 #Javascript
JavaScript的React Web库的理念剖析及基础上手指南
May 10 #Javascript
快速解决Canvas.toDataURL 图片跨域的问题
May 10 #Javascript
jQuery事件的绑定、触发、及监听方法简单说明
May 10 #Javascript
网页前端登录js按Enter回车键实现登陆的两种方法
May 10 #Javascript
BootstrapTable与KnockoutJS相结合实现增删改查功能【二】
May 10 #Javascript
You might like
PHP之APC缓存详细介绍 apc模块安装
2014/01/13 PHP
thinkphp框架表单数组实现图片批量上传功能示例
2020/04/04 PHP
JCalendar 日历控件 v1.0 beta[兼容IE&Firefox] 有文档和例子
2007/05/30 Javascript
JS 动态加载脚本的4种方法
2009/05/05 Javascript
JQuery获取各种宽度、高度(format函数)实例
2013/03/04 Javascript
JS中把字符转成ASCII值的函数示例代码
2013/11/21 Javascript
JQuery处理json与ajax返回JSON实例代码
2014/01/03 Javascript
node.js中的fs.chmodSync方法使用说明
2014/12/18 Javascript
javascript表格的渲染组件
2015/07/03 Javascript
利用Vue.js指令实现全选功能
2016/09/08 Javascript
详解Angular.js的$q.defer()服务异步处理
2016/11/06 Javascript
解析ajaxFileUpload 异步上传文件简单使用
2016/12/30 Javascript
easyui关于validatebox实现多重规则验证的方法(必看)
2017/04/12 Javascript
通过封装scroll.js 获取滚动条的值
2018/07/13 Javascript
Vue动态获取width的方法
2018/08/22 Javascript
javascript设计模式 ? 备忘录模式原理与用法实例分析
2020/04/21 Javascript
原生JS生成指定位数的验证码
2020/10/28 Javascript
前端vue如何使用高德地图
2020/11/05 Javascript
Vue通过阿里云oss的url连接直接下载文件并修改文件名的方法
2020/12/25 Vue.js
[57:12]完美世界DOTA2联赛循环赛 Inki vs Matador BO2第一场 10.31
2020/11/02 DOTA
Python发送Email方法实例
2014/08/21 Python
Pandas实现数据类型转换的一些小技巧汇总
2018/05/07 Python
利用python numpy+matplotlib绘制股票k线图的方法
2019/06/26 Python
python读取并写入mat文件的方法
2019/07/12 Python
基于keras输出中间层结果的2种实现方式
2020/01/24 Python
Python+unittest+DDT实现数据驱动测试
2020/11/30 Python
HTML5 文件上传下载的实例代码
2017/07/03 HTML / CSS
HTML table 表格边框的实现思路
2019/10/12 HTML / CSS
Russell Stover巧克力官方网站:美国领先的精美巧克力制造商
2016/11/27 全球购物
BookOutlet加拿大:在网上书店购买廉价折扣图书和小说
2018/10/05 全球购物
升职自荐书范文
2013/11/28 职场文书
介绍信范文大全
2015/05/07 职场文书
2016年“我们的节日·重阳节”主题活动总结
2016/04/01 职场文书
导游词之香港-太平山顶
2019/10/18 职场文书
《敬重卑微》读后感3篇
2019/11/26 职场文书
Hive常用日期格式转换语法
2022/06/25 数据库