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 相关文章推荐
Javscript调用iframe框架页面中函数的方法
Nov 01 Javascript
jQuery实现图片局部放大镜效果
Mar 17 Javascript
[原创]JQuery 在表单提交之前修改 提交的值
Apr 14 Javascript
必备的JS调试技巧汇总
Jul 20 Javascript
微信小程序联网请求的轮播图
Jul 07 Javascript
详解VueJS 数据驱动和依赖追踪分析
Jul 26 Javascript
详解webpack + vue + node 打造单页面(入门篇)
Sep 23 Javascript
在小程序/mpvue中使用flyio发起网络请求的方法
Sep 13 Javascript
axios使用拦截器统一处理所有的http请求的方法
Nov 02 Javascript
新手如何快速理解js异步编程
Jun 24 Javascript
cordova+vue+webapp使用html5获取地理位置的方法
Jul 06 Javascript
js、jquery实现列表模糊搜索功能过程解析
Mar 27 jQuery
实例讲解使用原生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输出数组中重名的元素的几种处理方法
2012/09/05 PHP
php英文单词统计器
2016/06/23 PHP
分享几个超级震憾的图片特效
2012/01/08 Javascript
快速解决jQuery与其他库冲突的方法介绍
2014/01/02 Javascript
JS实现的4种数字千位符格式化方法分享
2015/03/02 Javascript
AngularJS基础学习笔记之指令
2015/05/10 Javascript
使用js复制链接中的部分文字的方法
2015/07/30 Javascript
javascript简单判断输入内容是否合法的方法
2016/05/11 Javascript
深入浅析JavaScript中的arguments对象(强力推荐)
2016/06/03 Javascript
浅析Node.js实现HTTP文件下载
2016/08/05 Javascript
微信小程序 wx.uploadFile在安卓手机上面the same task is working问题解决
2016/12/14 Javascript
jQuery插件zTree实现删除树节点的方法示例
2017/03/08 Javascript
JS实现获取图片大小和预览的方法完整实例【兼容IE和其它浏览器】
2017/04/24 Javascript
BootstrapTable加载按钮功能实例代码详解
2017/09/22 Javascript
判断滚动条滑到底部触发事件(实例讲解)
2017/11/15 Javascript
Vue源码解析之数组变异的实现
2018/12/04 Javascript
jQuery实现的导航条点击后高亮显示功能示例
2019/03/04 jQuery
vue组件传值的实现方式小结【三种方式】
2020/02/05 Javascript
使用cx_freeze把python打包exe示例
2014/01/24 Python
python基础教程之对象和类的实际运用
2014/08/29 Python
python入门之语句(if语句、while语句、for语句)
2015/01/19 Python
Python线程中对join方法的运用的教程
2015/04/09 Python
python获取当前用户的主目录路径方法(推荐)
2017/01/12 Python
详解使用python的logging模块在stdout输出的两种方法
2017/05/17 Python
Python2.7读取PDF文件的方法示例
2017/07/13 Python
MySQL适配器PyMySQL详解
2017/09/20 Python
详解Python中的动态属性和特性
2018/04/07 Python
python实现人民币大写转换
2018/06/20 Python
Python中print和return的作用及区别解析
2019/05/05 Python
HTML5利用约束验证API来检查表单的输入数据的代码实例
2016/12/20 HTML / CSS
使用Html5中的cavas画一面国旗
2019/09/25 HTML / CSS
党组织关系的介绍信模板
2019/06/21 职场文书
python保存大型 .mat 数据文件报错超出 IO 限制的操作
2021/05/10 Python
pytorch 如何使用batch训练lstm网络
2021/05/28 Python
HTML基础详解(上)
2021/10/16 HTML / CSS
python基础之错误和异常处理
2021/10/24 Python