JavaScript | 继承

—————————————————————————————————————————————————————————

继承 – ECMAScript只协助得以完结一而再(依靠原型链),不支持接口继承(函数没有签字)

原型链

  • 动用原型让一个引用类型继承另一个引用类型的品质和艺术,
  • 构造函数、原型、实例的涉嫌:每个构造函数都有一个原型对象,原型对象涵盖一个针对性构造函数的指针。实例包含一个针对原型对象的里边指针,在创造实例之后即针对原型对象
  • 而当A原型对象的指针指向B个原型对象时(此时A原型对象与B实例同级),就形成了一条原型链。
  • 图解:

    图片 1

    原型搜索机制:当读取形式访问一个实例属性时,首先会在实例中寻找该属性,即使没有找到该属性则沿着原型链向上查找

在例子<Demo-1>中,调用instance.getSuperValue(),先搜索实例instance,再搜索SubType.prototype,再搜索SuperType.protorype,最后一步才找到该方法。

默认的原型:所有的引用类型默认都继承了Object,所以默认原型的指针都会指向Object.prototype,完整的原型链如下:


instance → SubType.prototype → SuperType.prototype → Object.prototype

![](https://images2017.cnblogs.com/blog/1146465/201707/1146465-20170731170706005-1605874342.png)
  • p.s.

    不可能不替换掉实例的原型后才能给实例添加方法

不能使用对象字面量创建原型方法,这样做会重写原型链,如<Demo-3>
  • 缺点:

    包罗引用类型值(Function Object Array)的原型属性会被抱有实例共享,在通过原型来已毕一而再时,原型实际上会变成另一个类型的实例,所以本来的实例属性就成为了明天的原型属性了。<Demo-4>

    在创立子类型的实例时,无法向超类型的构造函数中传递参数。

    // “use strict”;

    // Demo – 1
    // SuperType 颇具一个特性和一个方法
    // SubType 拥有一个属性和一个办法,又从SuperType那里继承了一个质量一个主意
    function SuperType(){

    this.property = "111";
    

    }
    SuperType.prototype.getSuperValue = function(){

    return this.property;
    

    }
    function SubType(){

    this.subproperty = "222";
    

    }
    // p.s.new操作此前,SubType.prototype指向的是function,不容许为function()定义.getSubValue方法,所以要将助长方法放在修改原型指向之后
    // 操作之后SubType.prototype指向SuperType
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function(){ // 必须在SubType替换原型之后才能定义

    return this.subproperty;
    

    }
    var instance = new SubType();
    console.log(instance.property); // 111
    console.log(instance.getSuperValue()); // 111
    console.log(instance.subproperty); // 222
    console.log(instance.getSubValue()); // 222
    console.log(instance.constructor); // f SuperType(){} 原本SubType中的constructor属性被重写
    // 重写SuperType.getSuperValue()
    // 即使要重写那么些主意,会遮掩原来的办法
    // 换句话说,当通过SubType的实例调用getSuperValue时调用的就是那个重新定义的主意,但由此SuperType的实例调用时还会继续调用原来的法门
    var beforeReWrite = new SuperType();
    SuperType.prototype.getSuperValue = function(){

    console.log("rewrite");
    

    }
    console.log(instance.getSuperValue()); // rewrite,this.property = undefined
    console.log(SuperType.prototype.getSuperValue()); // rewrite,this.property = undefined
    console.log(beforeReWrite.getSuperValue());

    // Demo – 2
    // 确认原型和实例的涉嫌
    console.log(instance instanceof Object); // true
    console.log(instance instanceof SuperType); // true
    console.log(instance instanceof SubType); // true
    // 另一种办法
    console.log(Object.prototype.isPrototypeOf(instance)); // true
    console.log(SuperType.prototype.isPrototypeOf(instance)); // true
    console.log(SubType.prototype.isPrototypeOf(instance)); // true

    // Demo – 3
    function SuperType2(){

    this.property = "1111";
    

    }
    SuperType2.prototype.getSuperValue = function(){

    return this.property;
    

    }
    function SubType2(){

    this.subproperty = "2222";
    

    }
    SubType2.prototype = new SuperType2();
    SubType2.prototype = {

    getSubValue:function(){
        return this.subproperty;
    },
    someOtherMethod:function(){
        return false;
    }
    

    }
    var instance2 = new SubType2();
    console.log(instance2 instanceof Object); // true
    console.log(instance2 instanceof SuperType2); // false,原型链被割裂
    console.log(instance2 instanceof SubType2); // true
    // console.log(instance2.getSuperValue()); // error

    // Demo – 4
    function SuperType3(){

    this.colors = ["red","blue","green"];
    

    }
    function SubType3(){}
    SubType3.prototype = new SuperType3();
    var instance3 = new SubType3();
    instance3.colors.push(“black”);
    console.log(instance3.colors); // [“red”, “blue”, “green”, “black”]
    var instance4 = new SubType3();
    console.log(instance4.colors); // [“red”, “blue”, “green”, “black”]

 

借用构造函数(伪造对象 /
经典一连)

  • 在子类型构造函数的里边调用超类型构造函数
  • 优点:

    涸泽而渔了单独使用原型链共享引用类型值属性的标题

可以在子类型构造函数中向超类型构造函数传递参数
  • 缺点:

    不可以幸免构造函数形式存在的标题:方法都在构造函数中定义,不可以兑现函数复用

    // “use strict”;

    function SuperType(name) {

    this.name = name;
    this.colors = ["111", "222", "333"];
    

    }

    function SubType() {

    SuperType.call(this, "name1");
    this.age = 20;
    

    }

    var instance = new SubType();
    instance.colors.push(“444”);
    console.log(instance.colors); // [“111”, “222”, “333”, “444”]
    console.log(instance.name); // name1
    console.log(instance.age); // 20
    var instance2 = new SubType();
    console.log(instance2.colors); // [“111”, “222”, “333”]

 

结缘继承(伪经典继承)

  • 将原型链和借用构造函数组合,使用原型链已毕对原型属性和章程的连续,通过借用构造函数来促成对实例属性的接续
  • 对应创制对象 <组合使用构造函数情势和原型方式>
  • 优点:最常用
  • 症结:须要调用五回超类型构造函数,四遍在开创子函数原型时,另三回在子函数构造函数内部。调用子类型构造函数时索要重写属性

    // “use strict”;
    function SuperType(name) {

    this.name = name;
    this.colors = ["111", "222", "333"];
    

    }
    SuperType.prototype.sayName = function() {

    console.log(this.name);
    

    }

    function SubType(name, age) {

    SuperType.call(this, name); // 继承属性
    this.age = age;
    

    }
    SubType.prototype = new SuperType(); // 继承方法
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function() {

    console.log(this.age);
    

    }
    var instance1 = new SubType(“hugh”, 20);
    instance1.colors.push(“444”);
    console.log(instance1.colors); // [“111”, “222”, “333”, “444”]
    instance1.sayName(); // hugh
    instance1.sayAge(); // 20
    var instance2 = new SubType(“dong”, 21);
    console.log(instance2.colors); // [“111”, “222”, “333”]
    instance2.sayName(); // dong
    instance2.sayAge(); // 21

 

原型式继承

  • 对应成立对象 <动态原型形式>
  • 从未选择严厉意义上的构造函数,借助已有些对象创制新对象
  • 优点:

    在不想创立构造函数,只想让一个目的与另一个对象有限支撑类似的景观下,原型式继承完全能够胜任

  • 缺点:

    涵盖引用类型值的习性始终都会共享,就如原型格局一样

    // “use strict”;
    function object(o){

    function F(){} // 创建临时性构造函数
    F.prototype = o; // 将传入的对象作为构造函数的原型
    return new F(); // 返回临时类型的一个新实例
    

    }

    var person = {

    name:"hugh",
    friends:["111",'222','333']
    

    };

    var anotherPerson = object(person);
    anotherPerson.name = “dong”;
    anotherPerson.friends.push(“444”);

    var yetAnotherPerson = object(person);
    yetAnotherPerson.name = “hehe”;
    yetAnotherPerson.friends.push(“555”);

    console.log(person.friends); // [“111”, “222”, “333”, “444”, “555”]
    console.log(person.name); // hugh
    console.log(anotherPerson.friends); // [“111”, “222”, “333”, “444”, “555”]
    console.log(anotherPerson.name); // dong
    console.log(yetAnotherPerson.friends); // [“111”, “222”, “333”, “444”, “555”]
    console.log(yetAnotherPerson.name); // hehe

    // 使用Object.create()规范化原型式继承
    // 以那种办法指定的任何性质都会覆盖原型对象上的同名属性
    var otherPerson1 = Object.create(person);
    otherPerson1.friends.push(“666”);
    console.log(yetAnotherPerson.friends); // [“111”, “222”, “333”, “444”, “555”, “666”]
    var otherPerson2 = Object.create(person,{

    name:{
        value:"test"
    }
    

    });
    console.log(otherPerson2.name);

 

寄生式继承

  • 对应创立对象 <寄生构造函数 /
    工厂形式>
  • 开创一个仅用于封装继承进度的函数,在其间夯实对象,最终回到对象
  • 演示集成方式时使用的object()函数不是必须的,任何可以回到新目的的函数都适用于此情势
  • 选取情况:在第一考虑对象而不是自定义类型和构造函数的图景下,寄生式继承也是一种有效的情势
  • 缺陷:不可以完毕函数复用,类似于构造函数格局

    // “use strict”;
    function object(o) {

    function F() {} // 创建临时性构造函数
    F.prototype = o; // 将传入的对象作为构造函数的原型
    return new F(); // 返回临时类型的一个新实例
    

    }

    function createAnother(original) { // 接收的函数作为新目的基础的目的

    var clone = object(original);
    clone.sayHi = function() { // 添加新方法
        console.log('hi');
    };
    return clone;
    

    }
    var person = {

    name: "hugh",
    friends: ['111', '222', '333']
    

    };
    var person1 = createAnother(person);
    person1.sayHi();
    console.log(person1.name);
    console.log(person1.friends);

 

寄生组合式继承

  • 优点:

    最美丽的接二连三范式

    解决组合继承重写属性的标题,只调用了两次SuperType构造函数

    幸免了在SubType.prototype上创立不需求的特性

    原型链保持不变

    可见健康使用instanceof和isPrototypeOf()

    “use strict”;
    function object(o) {

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

    }

    // 1.开立超类型原型的一个副本
    // 2.为开创的副本添加constructor属性,弥补因重写原型而错过的习性
    // 3.将新创立的靶子(即副本)赋值给子类型的原型
    function inheritProtoType(subType,superType){

    var prototype = object(superType.prototype); // 创建对象
    prototype.constructor = subType; // 增强对象
    subType.prototype = prototype; // 指定对象
    

    }
    function SuperType(name){

    this.name = name;
    this.colors= [1,2,3,4];
    

    }
    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);
    

    }

图片 2