javascript高級程序設計---6繼承

繼承對象

主要實現原理:經過原型鏈繼承~~~~

因此先來了解下原型鏈app

Child.prototype.__proto__ === Parent.prototype;
Child.__proto__ === Parent;
  • Parent是Function的實例。
Parent.__proto__ === Function.prototype;
Function.prototype.__proto__ === Object.prototype;
  • 全部函數的默認原型都是Object的實例。
Parent.prototype.__proto__ === Object.prototype;
Object.prototype.__proto__ === null;
  • 繼承的原型鏈圖

image.png

繼承模式總結比較

繼承模式 優勢 缺點 能夠用instance或者isPrototypeOf判斷類型嗎
原型鏈基礎 Child.prototype = new Parent();重寫了原型鏈繼承了Parent的方法 一、Parent類型的實例變成了Child類型的原型對象,若是Parent有引用類型好比Array的屬性會被篡改 二、建立Child類型的實例時,不能向Parent的構造函數傳參 能夠
借用構造函數(僞造對象或經典繼承) 解決了引用屬性共享和不可傳參 沒法複用函數 能夠
組合繼承(僞經典繼承) 結合了原型鏈和構造模式,最經常使用 老是會調用兩次超類型構造函數 能夠
原型式繼承 簡單輕量,不復雜,適合簡單需求 會共享引用類型的值,篡改 不能夠
寄生式繼承 替代不是自定義和構造函數的狀況 不能函數複用 不能夠
寄生組合式繼承 最理想的繼承範式 能夠

0、類式繼承的方案

class Parent {
  constructor(name){
    this.name = name;
  }

  static sayHello() {
    console.log('hello');
  }
  sayName() {
    console.log(`my name is ${this.name}`);
    return this.name;
  }
}

class Child extends Parent {
  constructor (name, age) {
    super(name);
    this.age = age;
  }
  sayAge() {
    console.log(`my age is ${this.age}`);
    return this.age;
  }
}

let parent = new Parent('Parent');
let child = new Child('Child', 18);

console.log(parent);
console.log(child);

console.log(child.__proto__ === Child.prototype);
console.log(Child.prototype.constructor === Child);
console.log(Child.prototype.__proto__ === Parent.prototype); // 繼承的原理1

console.log(Parent.prototype.constructor === Parent);
console.log(Parent.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ === null);


console.log(Child.__proto__ === Parent); // 繼承的原理2
console.log(Parent.__proto__ === Function.prototype);
console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ === null);

一、經過原型鏈實現繼承---實現繼承的主要方法。基本思想:讓一個引用類型繼承另外一個引用類型的屬性和方法。

  • 實現的本質是重寫原型對象,代之以一個新類型的實例。由於重寫了原型對象,因此給原型添加方法必定要放在替換原型的後面,若是先添加再替換,新添加的都被替換走了,因此永遠也不會生效了。函數

    function Parent () {
       this.parentProperty = true;
     }
     Parent.prototype.getParentProperty = function () {
       console.log(`parent's ${this.parentProperty}`);
     }
    
     function Child() {
       this.childProperty = false;
     }
    
     Child.prototype = new Parent(); // 本質---重寫原型對象
    
     Child.prototype.getChildProperty = function () {
       console.log(`child's${this.childProperty}`);
     }
    
     const parentInstance = new Parent();
     console.log(parentInstance.constructor); // Parent{}  
    
     const childInstance = new Child();
     console.log(childInstance.constructor); // Parent{} 致使childInstance的constructor指向Parent
     childInstance.getParentProperty(); // parent's true
    • 肯定原型和實例的關係
// 法一:instanceof
 console.log(childInstance instanceof Child);
 console.log(childInstance instanceof Parent);
 console.log(childInstance instanceof Object);
// 法二:isPrototypeOf
 console.log(Child.prototype.isPrototypeOf(childInstance));
 console.log(Parent.prototype.isPrototypeOf(childInstance));
 console.log(Object.prototype.isPrototypeOf(childInstance));
  • 不能使用對象字面量建立原型方法。this

    // yes
    Child.prototype = new Parent();
    // no。這樣作新建了一個Object實例,而非Parent的實例,切斷了與Parent的聯繫。
    Child.prototype = {
    };

二、借用構造函數:在Child類型構造函數的內部調用Parent類型構造函數。

  • 函數是特定環境中執行代碼的對象,能夠經過call和apply在新建立的對象上執行構造函數。spa

    function Parent (name) {
       this.colors = ['red', 'green'];
       this.name = name;
       this.age = 99;
     }
    
     function Child(name){
       // 繼承了Parent
       Parent.call(this, name);
    
       // 添加Child的屬性,必定要在調用Parent的後面,否則會被Parent構造函數重寫屬性(若是正好存在)
       this.age = 29;
     }
    
     const childInstance1 = new Child('tom');
     const childInstance2 = new Child('jack');
    
     childInstance1.colors.push('white');
    
     console.log(childInstance1.colors); // ['red', 'green', 'white']
     console.log(childInstance2.colors); // ['red', 'green']
    
     console.log(childInstance1.name); // tom
     console.log(childInstance2.name); // jack
    
     console.log(childInstance1.age); // 29
     console.log(childInstance2.age); // 29
    
     console.log(childInstance2 instanceof Child); // true
     console.log(childInstance2 instanceof Parent); // false
     console.log(childInstance2 instanceof Object); // true

三、組合繼承:組合使用原型鏈和構造函數模式

  • 使用原型鏈實現對原型屬性和方法的繼承(複用);使用構造函數實現對實例屬性的繼承(副本)prototype

    function Parent (name) {
     this.colors = ['red', 'green'];
     this.name = name;
    }
    
    Parent.prototype.getParentName = function () {
     console.log(this.name);
    }
    
    function Child(name, age){
     // 構造函數繼承屬性
     Parent.call(this, name);
    
     this.age = age;
    }
    
    // 使用原型鏈繼承方法
    Child.prototype = new Parent();
    Child.prototype.constructor = Child;
    
    Child.prototype.getChildAge = function () {
     console.log(this.age);
    }
    
    const childInstance1 = new Child('tom', 29);
    const childInstance2 = new Child('jack', 18);
    
    childInstance1.colors.push('white');
    
    console.log(childInstance1.colors); // ['red', 'green', 'white']
    console.log(childInstance2.colors); // ['red', 'green']
    childInstance1.getParentName(); // 'tom'
    childInstance2.getParentName(); // 'jack'
    
    childInstance1.getChildAge(); // 29
    childInstance2.getChildAge(); // 18
    
    console.log(childInstance2 instanceof Child); // true
    console.log(childInstance2 instanceof Parent); // true
    console.log(childInstance2 instanceof Object); // true

四、原型式繼承

  • 藉助原型基於已有的對象建立新對象,同時還沒必要所以建立自定義類型code

    // object對傳入其中的對象進行了一次淺複製
     function object (o) {
       function F() {}
       F.prototype = o;
       return new F();
     }
    
     const person = {
       name: 'tom',
       friends: ['a', 'b', 'c']
     };
    
     const anotherPerson = object(person);
    
     anotherPerson.name = 'huahua';
     anotherPerson.friends.push('d');
     const onePerson = object(person);
    
     console.log(person.friends); // ['a', 'b', 'c', 'd']
     console.log(anotherPerson.friends); // ['a', 'b', 'c', 'd']
     console.log(onePerson.friends); // ['a', 'b', 'c', 'd']
  • ECMAScript5新增了Object.create方法,規範了原型式繼承。

五、寄生式繼承:建立一個僅用於封裝繼承過程的函數,在內部加強對象。

function createAnother(original) {
   const clone = object(original);

   clone.sayHi = function () {
     console.log('hi');
   }

   return clone;
 }

 const person = {
   name: 'tom',
   friends: ['a', 'b', 'c']
 };

 const anotherPerson = createAnother(person);
 anotherPerson.sayHi();

 anotherPerson.friends.push('e');

 console.log(anotherPerson.friends); // ['a', 'b', 'c', 'e']
 console.log(person.friends); // ['a', 'b', 'c', 'e']

六、寄生組合式繼承

  • 沒必要爲了指定Child類型的原型而調用Parent類型的構造函數,須要的只是Parent原型的一個副本。
// object對傳入其中的對象進行了一次淺複製
  function object (o) {
    function F() {}
    F.prototype = o;
    return new F();
  }

  function inheritPrototype (Sub, Super) {
    const prototype = object(Super.prototype); // 建立對象
    prototype.constructor = Sub; // 加強對象
    Sub.prototype = prototype; // 指定對象
  }

  function Parent (name) {
    this.name = name;
    this.colors= ['a', 'b', 'c'];
  }

  Parent.prototype.getParentName = function () {
    console.log(this.name);
  }

  function Child (name, age) {
    // 繼承屬性
    Parent.call(this, name);

    this.age = age;
  }

  // 繼承方法
  inheritPrototype(Child, Parent);

  Child.prototype.getChildAge = function () {
    console.log(this.age);
  }

  const childInstance1 = new Child('tom', 29);
  const childInstance2 = new Child('jack', 18);

  childInstance1.colors.push('white');

  console.log(childInstance1.colors); // ["a", "b", "c", "white"]
  console.log(childInstance2.colors); // ["a", "b", "c"]
  childInstance1.getParentName(); // 'tom'
  childInstance2.getParentName(); // 'jack'

  childInstance1.getChildAge(); // 29
  childInstance2.getChildAge(); // 18

  console.log(childInstance2 instanceof Child); // true
  console.log(childInstance2 instanceof Parent); // true
  console.log(childInstance2 instanceof Object); // true
相關文章
相關標籤/搜索