zoukankan      html  css  js  c++  java
  • JS: 继承

    继承

    记录一下 javascript 的各种继承方式,个人用得比较多的还是原型链继承和 ES6 的 extends。

    原型链继承


    // 原型模式
    function Parents() {
      this.name = 'Parents';
    }
    Parents.prototype.getName = function() {
      return this.name;
    }
    
    // 原型链继承
    function Child() {}
    Child.prototype = new Parents();
    Child.prototype.constructor = Child;
    
    let son = new Child();
    son.getName(); // Parents;
    

    缺点:

    • 在创建 Child 的实例时,无法向 Parents 传参
    • 父类里面的引用类型被共享,个例修改导致所有实例都被修改
    function Parents() {
      this.name = ['dad', 'mom'];
    }
    
    function Child() {}
    Child.prototype = new Parents();
    Child.prototype.constructor = Child;
    
    // 无法向 Parents 传参
    let son = new Child();
    let daughter = new Child();
    // 父类里面的引用类型被共享
    son.name == daughter.name; // true
    // 个例修改导致所有实例都被修改
    son.name[0] = 'father';
    daughter.name[0]; // "father"
    

    借用构造函数


    为了解决上面的问题 ,经典继承方式被设计出来:

    // 构造函数模式
    function Parents(childName) {
      this.childName = childName;
      this.name = ['dad', 'mom'];
      this.getName = function() {
        return this.name;
      }
    }
    
    // 借用构造函数继承
    function Child(name) {
      Parents.call(this, name);
    }
    
    // 可以向 Parents 传参
    let son = new Child('son');
    son.childName; // "son"
    
    // 父类里面的引用类型不会被共享
    let daughter = new Child('daughter');
    son.name[0] = 'father';
    daughter.name[0]; // "dad"
    

    缺点:方法都在构造函数里定义,每次创建 Child 实例,都要重新创建一次方法,函数无法得到复用。

    组合继承


    为了弥补上面的缺点,于是乎组合继承诞生了。

    // 组合模式
    function Parents(childName) {
      this.childName = childName;
      this.name = ['dad', 'mom'];
    }
    Parents.prototype.getName = function() {
      return this.name;
    }
    
    // 组合继承
    function Child(name) {
      Parents.call(this, name);
    }
    Child.prototype = new Parents();
    Child.prototype.constructor = Child;
    
    let son = new Child('son');
    son.name[0] = 'father';
    
    let daughter = new Child('daughter');
    daufhter.name[0]; // "dad"
    

    因为原型链能保持不变,所以instanceOfisProtypeOf()也能识别基于组合继承创建的对象。

    原型式继承


    function obj(o) {
      function F() {}
      F.prototype = 0;
      return new F();
    }
    

    这种方式就是在 obj 函数内部,创建一个临时性的构造函数 F(),然后将传入的对象作为这个构造函数的原型,最后返回这个临时类的实例。

    其实就是对传入的对象进行一次浅复制,类似于 ES5 的 Object.create() 方法。

    Object.create(proto, [propertiesObject]) 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

    proto: 新创建对象的原型对象。

    propertiesObject: 可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不 是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties() 的第二个参数。

    寄生式继承


    function createObj (o) {
        var clone = Object.create(o);
        clone.sayHi = function () {
            console.log('hi');
        }
        return clone;
    }
    

    这种方式就是创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回增强过的对象。本质上还是对传入的对象进行一次浅复制。

    寄生组合式继承


    这种方式得先创建一个用于封装继承过程的函数。

    function inheritPrototype(child, Parents) {
      let prototype = Object.create(Parents.prototype);
      prototype.constructor = child;
      child.prototype = prototype
    }
    

    然后用上面的函数实现继承:

    function Parents(childName) {
      this.childName = childName;
      this.name = ['dad', 'mom'];
    }
    Parents.prototype.getName = function() {
      return this.name;
    }
    
    function Child(name) {
      Parents.call(this, name);
    }
    
    inheritPrototype(Child, Parents);
    

    其实和组合继承差不多,只是封装了一个 inheritPrototype 函数使得在继承的时候只在 Child 里调用一次 Parents 构造函数。

    ES6 的 extends


    ES6 引入了 class,可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。但需要注意的是,ES6 的 class 只是语法糖,本质上还是 ES5 的通过修改原型链实现继承的方法,具体可以去babel 试用来看看 babel 对于 class 的转换。

    class Parents {
      // 构造函数
      constructor(name) {
        this.name = name;
      }
      // 静态方法
      static getName() {
        return this.name;
      }
      // 方法
      printName() {
        console.log(this.name);
      }
    }
    
    class Child extends Parents {
      constructor(name, childName) {
        // 调用父类的constructor(name)
        super(name);
        this.childName = childName;
      }
      printName() {
        super.printName();
        console.log(this.childName);
      }
    }
    
    let c = new Child('mom', 'son');
    c.printName();// mom son
    

    ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

    大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

    (1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

    (2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

    参考


    阮一峰_Class 的继承

    《javascript高级程序设计》(三)

  • 相关阅读:
    【jQuery EasyUI系列】使用属性介绍
    【jQuery EasyUI系列】创建CRUD数据网格
    [jQuery EasyUI系列] 创建增删改查应用
    [JQuery EasyUI系列]简介
    Javascript 使用小案例
    Spring学习进阶(一)初识Spring
    No plugin found for prefix 'jetty' in the current project and in the plugin groups 【转】
    工作框架各种使用整理 --创建的时候使用前面创建成功的输出
    工作框架各种使用整理---自己处理分页
    策略模式
  • 原文地址:https://www.cnblogs.com/guolao/p/10461687.html
Copyright © 2011-2022 走看看