• 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

  • 相关阅读:
    八张图读懂未来“互联网+”的六大趋势
    跑一段代码遍历所有汉字
    PHP业务逻辑层和数据访问层设计
    漫谈社区PHP 业务开发
    以Apache服务器、php语言为例 详解动态网站的访问过程
    sublime text
    《产品经理的20堂必修课》
    检测文件是否有bom头
    利用开源框架Volley来下载文本和图片。
    往SD卡中写文件的方法。
  • 原文地址:https://www.cnblogs.com/qixidi/p/10185815.html
Copyright © 2020-2023  润新知