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 相关文章推荐
一个关于javascript匿名函数的问题分析
Mar 30 Javascript
JS动态修改表格cellPadding和cellSpacing的方法
Mar 31 Javascript
JS实现仿QQ聊天窗口抖动特效
May 10 Javascript
jQuery判断指定id的对象是否存在的方法
May 22 Javascript
jquery仿百度百科底部浮动导航特效
Aug 08 Javascript
JavaScript禁止复制与粘贴的实现代码
May 16 Javascript
js匿名函数作为函数参数详解
Jun 01 Javascript
使用Webpack提高Vue.js应用的方式汇总(四种)
Jul 10 Javascript
angular2+node.js express打包部署的实战
Jul 27 Javascript
js 将线性数据转为树形的示例代码
May 28 Javascript
vue项目中监听手机物理返回键的实现
Jan 18 Javascript
vue3.0自定义指令(drectives)知识点总结
Dec 27 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的加密方式及原理
2012/06/14 PHP
如何用C语言编写PHP扩展的详解
2013/06/13 PHP
Codeigniter实现多文件上传并创建多个缩略图
2014/06/12 PHP
WordPress中用于获取及自定义头像图片的PHP脚本详解
2015/12/17 PHP
DEFER怎么用?
2006/07/01 Javascript
基于jquery的动态创建表格的插件
2011/04/05 Javascript
javascript 实现子父窗体互相传值的简单实例
2014/02/17 Javascript
node.js中的buffer.write方法使用说明
2014/12/10 Javascript
详解JavaScript对W3C DOM模版的支持情况
2015/06/16 Javascript
javascript常用的方法分享
2015/07/01 Javascript
jQuery实现仿美橙互联两级导航菜单效果完整实例
2015/09/17 Javascript
Jquery日历插件制作简单日历
2015/10/28 Javascript
JavaScript——DOM操作——Window.document对象详解
2016/07/14 Javascript
vue获取input输入值的问题解决办法
2017/10/17 Javascript
JavaScript实现二叉树的先序、中序及后序遍历方法详解
2017/10/26 Javascript
React Native使用fetch实现图片上传的示例代码
2018/03/07 Javascript
JS简单实现动态添加HTML标记的方法示例
2018/04/08 Javascript
详解angular路由高亮之RouterLinkActive
2018/04/28 Javascript
vue 内置过滤器的使用总结(附加自定义过滤器)
2018/12/11 Javascript
Vue动态创建注册component的实例代码
2019/06/14 Javascript
layui动态加载多表头的实例
2019/09/05 Javascript
vue 实现用户登录方式的切换功能
2020/04/14 Javascript
Python中死锁的形成示例及死锁情况的防止
2016/06/14 Python
Python基于回溯法子集树模板实现8皇后问题
2017/09/01 Python
Python实现识别手写数字大纲
2018/01/29 Python
Pyspider中给爬虫伪造随机请求头的实例
2018/05/07 Python
django的csrf实现过程详解
2019/07/26 Python
css3实现平移效果(transfrom:translate)的示例
2020/11/13 HTML / CSS
PHP使用Redis队列执行定时任务实例讲解
2021/03/24 PHP
学前教育学生自荐信范文
2013/12/31 职场文书
党的群众路线整改落实情况汇报
2014/10/28 职场文书
社区服务活动报告
2015/02/05 职场文书
员工担保书范本
2015/09/22 职场文书
详解Java实践之适配器模式
2021/06/18 Java/Android
JUnit5常用注解的使用
2021/07/02 Java/Android
Golang 字符串的常见操作
2022/04/19 Golang