因此先來了解下原型鏈app
Child.prototype.__proto__ === Parent.prototype; Child.__proto__ === Parent;
Parent.__proto__ === Function.prototype; Function.prototype.__proto__ === Object.prototype;
Parent.prototype.__proto__ === Object.prototype; Object.prototype.__proto__ === null;
繼承模式 | 優勢 | 缺點 | 能夠用instance或者isPrototypeOf判斷類型嗎 |
---|---|---|---|
原型鏈基礎 | Child.prototype = new Parent();重寫了原型鏈繼承了Parent的方法 | 一、Parent類型的實例變成了Child類型的原型對象,若是Parent有引用類型好比Array的屬性會被篡改 二、建立Child類型的實例時,不能向Parent的構造函數傳參 | 能夠 |
借用構造函數(僞造對象或經典繼承) | 解決了引用屬性共享和不可傳參 | 沒法複用函數 | 能夠 |
組合繼承(僞經典繼承) | 結合了原型鏈和構造模式,最經常使用 | 老是會調用兩次超類型構造函數 | 能夠 |
原型式繼承 | 簡單輕量,不復雜,適合簡單需求 | 會共享引用類型的值,篡改 | 不能夠 |
寄生式繼承 | 替代不是自定義和構造函數的狀況 | 不能函數複用 | 不能夠 |
寄生組合式繼承 | 最理想的繼承範式 | 能夠 |
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 = { };
函數是特定環境中執行代碼的對象,能夠經過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']
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']
// 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