zoukankan      html  css  js  c++  java
  • 帮你理清js的继承

    关于继承网上有很多文章,但是能讲解的大多都不让我满意,所以自己写一篇。本文适合已经知道js继承的各种或部分方式,但是尚未形成系统脉络的读者。

    接下来为方便描述,约定:A是父类,a是A类的实例,B是子类,b是B类的实例。

    原型链继承

    B.prototype = a;

    这种继承的潜在问题是,将A的实例属性变成了原型上的属性。如果某一个属性是引用类型,那么所有的b 都将共用一个引用,改变一个b,所有的b都跟着变。

    问题示例:

    function A(){
        this.objFromA = { name:'a' }
    }
    b1.objFromA.name='b1'
    console.log( b2.objFromA.name )  // 变成了b1

    至于网上说的原型式继承、寄生式继承,其实都是这种方式的另一种写法。

    // Object.create 原型链继承
    let b1 = Object.create(a);
    let b2 = Object.create(a);
    
    // 寄生式继承  
    // 我实在看不出来寄生式继承和原型继承有啥差别,玩概念而已,解决不了任何问题。这种继承就不说了。

    当然你还可以用 Object.setPrototypeOf 实现原型继承:

    let b={}
    Object.setPrototypeOf(b,a)
    // 比Object.create多写一行,较麻烦。

    这些所有的实现方式,本质都是原型链继承,其潜在问题都是一样的:实例属性变成了原型上的属性,如果是引用类型属性的话,多个b会共享。

    构造继承

    function B(){
        A.call(this);     // 每次创建b时,都重新调用A,从而产生的属性是互相独立的,巧妙的避免了属性被共享的问题。
    }

    这种继承问题很明显:无法继承A的原型上的方法。

    两者组合

    function B() {    
        A.call(this);   
    }
    B.prototype = a;
    B.prototype.constructor = B;

    这种方式接近完美:属性不会被共享,同时原型上的方法也继承过来了。

    美中不足的是  B.prototype = a;  这一行,a的实例属性会污染B的原型,成为永远不能被b访问到的 却额外占用了内存的垃圾。

    组合的升级 :

    function B() {
        A.call(this);   
    }
    B.prototype = Object.create(A.prototype);   // 不产生实例a,从而避免了产生垃圾
    B.prototype.constructor = B;

    仍然存在的问题

    大多数文章都是到此为止了,但这个方案还是由缺陷。

    潜在问题一:A的静态方法无法被继承

    静态方法是直接写在A上的,所以也要将A上的成员(注意是A不是a)继承到B上(注意是B不是b)。

    // 方法一:
    Object.assign(B,A)  // 这种方法存在问题:如果A的静态成员是变化的,B将无法跟随变化
    
    // 方法二:
    Object.setPrototypeOf(B, A);  // 这是完美的方法

    潜在问题二:有时构造函数会返回一个对象,而不是通过操作this。所以 A.call(this); 这一句也许并不能继承A的实例成员。稍加改造:

    let _this = A.call(this) || this;    
    return _this;

    最终完美的继承方法

    function B() {
        let _this = A.call(this) || this;    
        return _this; 
    }
    Object.setPrototypeOf(B, A);
    B.prototype = Object.create(A.prototype); 
    B.prototype.constructor = B;

    当你尝试用es6的class继承时 class B extends A 你会发现它最终会被babel翻译成上面这个完美的方案。

    enne5w4 原创博文,转载请注明出处! 地址:https://www.cnblogs.com/zhwc-5w4/ (欢迎技术交流探讨)
  • 相关阅读:
    innerHTML和outerHTML的区别
    递归,汉诺塔
    js 中的 Math.ceil() Math.floor Math.round()
    JS中的异常exception
    CSS样式中visited,hover,active , focus这四个分别表示什么意思?
    用户在设置密码时,提醒请输入半角字符(vue+element+valid)
    设置用户密码时,将全角转换为半角
    后台返回对象数组,对象属性相同时,只取一个对象
    远程链接mongoDB robomongo
    mongodb 入坑
  • 原文地址:https://www.cnblogs.com/zhwc-5w4/p/14484881.html
Copyright © 2011-2022 走看看