最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • js继承图解

    正文概述 掘金(耳东蜗牛)   2020-12-05   483

    语雀地址:地址

    继承是面向对象编程中讨论最多的话题。很多面向对象语言都支持两种继承:接口继承和实现继承。前者只继承方法签名,后者继承实际的方法。接口继承在 ECMAScript 中是不可能的,因为函数没有签名。实现继承是 ECMAScript 唯一支持的继承方式,而这主要是通过原型链实现的。

    原型知识前置

    var Person = function(name){
      this.name = name; // tip: 当函数执行时这个 this 指的是谁?
    };
    Person.prototype.getName = function(){
      return this.name;  // tip: 当函数执行时这个 this 指的是谁?
    }
    var person1 = new Person('Mick');
    

    js继承图解

    继承

    原型链继承

    原型链定义为ECMA的主要继承方式。

    function Parent () {
        this.name = 'kevin';
    }
    
    Parent.prototype.getName = function () {
        console.log(this.name);
    }
    
    function Child () {
    
    }
    
    Child.prototype = new Parent();
    
    var child1 = new Child();
    
    console.log(child1.getName()) 
    

    原先是这样:

    js继承图解

    现在是这样:

    js继承图解

    Child创建的对象的__proto__指向new Parent对象。contructor指向Parent构造函数。这里有一个问题是没有将原型对象的contructor指向Child。加一行代码就可以了。

    Child.prototype.constructor = Child;
    

    缺点

    • 引用类型的属性被所有实例共享,值类型不会被共享,是因为当我们在执行[[get]]操作的时候会去查询原型链。执行[[put]]的时候,会自动给当前的对象创建一个属性

    • 在创建 Child 的实例时,不能向Parent传参

    子类型在实例化时不能给父类型的构造函数传参。事实上,我们无法在不影响所有对象实例的情况下把参数传进父类的构造函数。再加上之前提到的原型中包含引用值的问题,就导致原型链基本不会被单独使用。

    借用构造函数继承【经典继承】

    为了解决原型包含引用值导致的继承问题,一种叫作“盗用构造函数”(constructor stealing)的技术在开发社区流行起来(这种技术有时也称作“对象伪装”或“经典继承”)。基本思路很简单:在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象,所以可以使用apply()和 call()方法以新创建的对象为上下文执行构造函数。

    function Parent (name) {
        this.name = name;
    }
    
    function Child (name) {
        Parent.call(this, name);
    }
    
    var child1 = new Child('kevin');
    
    console.log(child1.name); // kevin
    
    var child2 = new Child('daisy');
    
    console.log(child2.name); // daisy
    

    原先是这样:

    js继承图解

    现在是这样:

    js继承图解

    从上图可以清楚看到:

    当前的继承方式是通过new操作的时候,绑定child的this执行Parent的方法,去执行一次Parent内部的方法。和原型链啥的没有任何关系。

    缺点

    盗用构造函数的主要缺点,也是使用构造函数模式自定义类型的问题:必须在构造函数中定义方法,因此函数不能重用【因为二者之间的函数是两个函数,本质是不相等的函数】。此外,子类也不能访问父类原型上定义的方法,因此所有类型只能使用构造函数模式。由于存在这些问题,盗用构造函数基本上也不能单独使用。

    组合继承

    组合继承(有时候也叫伪经典继承)综合了原型链和盗用构造函数,将两者的优点集中了起来。

    function Parent (name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }
    
    Parent.prototype.getName = function () {
        console.log(this.name)
    }
    
    function Child (name, age) {
        Parent.call(this, name);
        this.age = age;
    }
    
    Child.prototype = new Parent();
    Child.prototype.constructor = Child;
    
    var child1 = new Child('kevin', '18');
    
    child1.colors.push('black');
    
    console.log(child1.name); // kevin
    console.log(child1.age); // 18
    console.log(child1.colors); // ["red", "blue", "green", "black"]
    
    var child2 = new Child('daisy', '20');
    
    console.log(child2.name); // daisy
    console.log(child2.age); // 20
    console.log(child2.colors); // ["red", "blue", "green"]
    

    js继承图解

    组合继承弥补了原型链和盗用构造函数的不足,是 JavaScript 中使用最多的继承模式。而且组合继承也保留了 instanceof 操作符和 isPrototypeOf()方法识别合成对象的能力。

    原型式继承

    2006 年,Douglas Crockford 写了一篇文章:《JavaScript 中的原型式继承》(“Prototypal Inheritance in JavaScript”)。这篇文章介绍了一种不涉及严格意义上构造函数的继承方法。他的出发点是即使不自定义类型也可以通过原型实现对象之间的信息共享。

    function object(o) { 
     function F() {} 
     F.prototype = o; 
     return new F(); 
    }
    
    let person = { 
     name: "Nicholas", 
     friends: ["Shelby", "Court", "Van"] 
    }; 
    
    let anotherPerson = object(person); 
    anotherPerson.name = "Greg"; 
    anotherPerson.friends.push("Rob"); 
    
    let yetAnotherPerson = object(person); 
    yetAnotherPerson.name = "Linda"; 
    yetAnotherPerson.friends.push("Barbie"); 
    console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
    

    js继承图解

    原型式继承虽然说是不自定义类型来实现原型对象之间的信息共享,但是我们可以发现object里面还是自定义了一个类型,去实现原型链继承的方式。

    特定场景:原型式继承非常适合不需要单独创建构造函数,你有一个对象,想在它的基础上再创建一个新对象。

    ECMAScript 5 通过增加 **Object.create()**方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在

    寄生式继承

    与原型式继承比较接近的一种继承方式是寄生式继承(parasitic inheritance),也是 Crockford 首倡的一种模式。寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。

    这种模式依赖于上面的object方法,这里直接使用Obect.create()方法

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

    这里的图和上面的基本相同,就不画了。这里说的是一种思想,在createObj的方法里统一对我们创建的对象做一些处理。

    寄生组合式继承

    组合继承其实也存在效率问题。最主要的效率问题就是父类构造函数始终会被调用两次:一次在是创建子类原型时调用,另一次是在子类构造函数中调用。本质上,子类原型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。

    可以翻到组合继承的地方,看到Parent会被调用两次,call一次,原型对象一次。

    js继承图解

    寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。基本思路是不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。说到底就是使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。

    function inheritPrototype(subType, superType) {
        let prototype = Object.create(superType.prototype); // 创建对象
        prototype.constructor = subType; // 增强对象
        subType.prototype = prototype; // 赋值对象
    }
    
    function SuperType(name) {
        this.name = name;
        this.colors = ["red", "blue", "green"];
    }
    
    SuperType.prototype.sayName = function() {
        console.log(this.name);
    };
    
    function SubType(name, age) {
        SuperType.call(this, name);
        this.age = age;
    }
    
    inheritPrototype(SubType, SuperType);
    SubType.prototype.sayAge = function() {
        console.log(this.age);
    };
    
    var subTupe1 = new SubType('name1', 1)
    var subTupe2 = new SubType('name2', 2)
    

    js继承图解

    总结继承

    js继承图解

    js继承图解


    起源地下载网 » js继承图解

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元