zoukankan      html  css  js  c++  java
  • JS中继承方式总结

    说在前面:
    为了使代码更为简洁方便理解, 本文中的代码均将“非核心实现”部分的代码移出。


    一、原型链方式
    关于原型链,可点击《深入浅出,JS原型链的工作原理》,本文不再重复叙述。

    思路:让子构造函数的原型等于父构造函数的实例

    
    function A() {
    }
    A.prototype.fn = function (){
        console.log("in A");
    }
    
    function B() {
    }
    B.prototype = new A();  // 让子构造函数的原型等于父构造函数的实例
    
    var b = new B();
    b.fn(); // in A
    console.log(b instanceof B); // true
    console.log(b instanceof A); // true
    console.log(b instanceof Object); // true
    
    

    缺陷:如果父构造函数中的属性为引用类型,则子构造函数的实例会出现相互影响的情况;

    
    function A() {
        this.prop = ['1',"2"];
    }
    A.prototype.fn = function (){
        console.log(this.prop);
    }
    
    function B() {
    }
    B.prototype = new A(); 
    
    var b1 = new B();
    var b2 = new B();
    b1.fn(); //  ["1", "2"]
    b2.fn(); //  ["1", "2"]
    
    b1.prop.push('3'); // 子构造函数实例b1修改继承过来的属性
    b2.prop.push('4'); // 子构造函数实例b2修改继承过来的属性
    
    b1.fn(); // ["1", "2", "3", "4"] // b2上的修改影响了b1
    b2.fn(); // ["1", "2", "3", "4"] // b1上的修改影响了b2
    
    

    *导致缺陷原因:引用类型,属性变量保存的是地址指针而非实际的值,这个指针指向了一块用来保存实际内容的地址。实例化后,所有实例中变量保存了同一个指针,均指向同一个地址,当任何一个实例通过指针修改地址的内容(并非重新赋予新的指针地址或者修改指针指向)时,其他实例的也会受到影响。


    二、借用构造函数方式
    为了解决“原型链方式”继承的缺陷,引入的一种“继承”方案。

    思路:通过call/apply,在子构造函数中调用父类的构造函数

    
    function A() {
        this.prop = ['1',"2"];
    
        this.fn2 = function () {
            console.log(this.prop);
        }
    }
    A.prototype.fn = function (){
        console.log(this.prop);
    }
    
    function B() {
        A.call(this); // 通过call/apply,在子构造函数中调用父类的构造函数
    }
    
    var b1 = new B();
    var b2 = new B();
    b1.fn2(); // ["1", "2"]
    b2.fn2(); // ["1", "2"]
    
    b1.prop.push('3');
    b2.prop.push('4');
    
    b1.fn2(); // ["1", "2", "3"]
    b2.fn2(); // ["1", "2", "4"]
    
    b1.fn(); // 提示异常:b1.fn is not a function
    console.log(b1 instanceof B); // true
    console.log(b1 instanceof A); // false
    console.log(b1 instanceof Object); // true
    
    

    缺陷:由于“继承”过程中,A仅充当普通函数被调用,使得父构造函数A原型无法与形成子构造函数B构成原形链关系。因此无法形成继承关系:"b1 instanceof A"结果为false,B的实例b1亦无法调用A原型中的方法。实际意义上,这种不属于继承。


    三、组合继承
    结合“原型链方式”和“借用构造函数方式”的有点,进行改进的一种继承方式。

    思路:原型上的属性和方法通过“原型链方式”继承;父构造函数内的属性和方法通过“借用构造函数方式”继承

    
    function A() {
        this.prop = ['1',"2"];
    }
    A.prototype.fn = function (){
        console.log(this.prop);
    }
    
    function B() {
        A.call(this); // 借用构造函数方式
    }
    B.prototype = new A(); // 原型链方式
    
    var b1 = new B();
    var b2 = new B();
    b1.fn(); // ["1", "2"]
    b2.fn(); // ["1", "2"]
    
    b1.prop.push('3');
    b2.prop.push('4');
    
    b1.fn(); // ["1", "2", "3"]
    b2.fn(); // ["1", "2", "4"]
    console.log(b1 instanceof B); // true
    console.log(b1 instanceof A); // true
    console.log(b1 instanceof Object); // true
    
    

    缺陷:子构造函数的原型出现一套冗余“父构造函数非原型上的属性和方法”。上述代码在执行“A.call(this);”时候,会给this(即将从B返回给b1赋值的对象)添加一个“prop”属性;在执行“B.prototype = new A();”时,又会通过实例化的形式给B的原型赋值一次“prop”属性。显然,由于实例属性方法的优先级高于原型上的属性方法,绝大多数情况下,原型上的“prop”是不会被访问到的。


    四、寄生组合式继承
    为了解决“组合继承”中子构造函数的原型链出现冗余的属性和方法,引入的一种继承方式。

    思路:在组合继承的基础上,通过Object.create的方式实现原型链方式

    
    function A() {
        this.prop = ['1',"2"];
    }
    A.prototype.fn = function (){
        console.log(this.prop);
    }
    
    function B() {
        A.call(this);
    }
    B.prototype = Object.create(A.prototype); // Object.create的方式实现原型链方式
    
    var b1 = new B();
    var b2 = new B();
    b1.fn(); // ["1", "2"]
    b2.fn(); // ["1", "2"]
    
    b1.prop.push('3');
    b2.prop.push('4');
    
    b1.fn(); // ["1", "2", "3"]
    b2.fn(); // ["1", "2", "4"]
    
    console.log(b1 instanceof B); // true
    console.log(b1 instanceof A); // true
    console.log(b1 instanceof Object); // true
    
    

    最后补充
    1、因为子构造函数的实例自身没有constructor属性,当我们访问实例的constructor属性时,实际是访问原型的constructor属性,该属性应该指向(子)构造函数。到那时上述例子中的代码均会指向父构造函数。为了与ECMAScript规范保持一致,在所有的“原型链继承”后,应当将原型的constructor属性指向子构造函数本身:

    
        B.prototype = ....
    --> B.prototype.constructor = B; <--
        ...
    
    

    2、Object.create是ECMAScript 5中加入的一个函数,这个函数的功能是:将入参(需为一个对象)作为原型,创建并返回一个新的(只有原型的)的对象。此功能等价于:

    
    function object(o){ 
        function F(){}
        F. prototype = o; 
        return new F(); 
    } // 来源于《JavaScript高级程序设计(第3版)》
    
    
    

    来源:https://segmentfault.com/a/1190000017522714

  • 相关阅读:
    HDU 1114 Piggy-Bank
    HDU 2955 Robberies
    NTOJ 290 动物统计(加强版)
    POJ 3624 Charm Bracelet
    HDU 2602 Bone Collector
    POJ 1523 SPF(无向图割顶)
    HDU 5311 Hidden String
    HDU 1421 搬寝室
    HDU 1058 Humble Numbers
    POJ 3259 Wormholes(spfa判负环)
  • 原文地址:https://www.cnblogs.com/qixidi/p/10185815.html
Copyright © 2011-2022 走看看