原型和閉包是JS的兩個難點,最近碰到了原型繼承的概念,正好在這裏總結一下。閉包
既然要實現繼承,就必定要有一個父類。函數
// 定義一個父類 function father(name) { //屬性 this.name = name; } // 原型方法 father.prototype.getName = function () { return this.name; }
基本思想就是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。性能
回顧一下原型、實例和構造函數的關係。this
每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象內部的指針。
// 子類 function son(age) { // 屬性 this.age = age; }; son.prototype = new father('jason'); son.prototype.getAge = function () { return this.age; } let firstchild = new son('19'); console.log(firstchild.getAge()) // 19
這裏須要注意幾點的是:spa
原型鏈的最頂端是Object,全部引用類型默認都是繼承於Object的,因此默認也是有toString等方法的。prototype
console.log(firstchild instanceof Object) //true console.log(firstchild instanceof son) //true console.log(firstchild instanceof father) //true
第二個方法是,isPrototypeOf方法。
console.log(Object.prototype.isPrototypeOf(firstchild)) //true console.log(son.prototype.isPrototypeOf(firstchild)) //true console.log(father.prototype.isPrototypeOf(firstchild)) //true
子類型可能要重寫父類型方法,或定義父類沒有的方法。無論是啥,這個方法必定要寫在替換原型語句的後面。
還有原型鏈繼承的時候,不能使用對象字面量建立原型方法。設計
例如:指針
son.prototype = new father('jason'); son.prototype = { getAge: function() { return this.age } }
這樣會致使建立一個新的Object實例,而非原來的father。code
第一,引用類型的原型屬性會被全部實例共享。對象
function father(name) { this.name = name; this.colors = ['blue', 'red', 'white']; } let firstchild = new son('19'); let secondchild = new son('20'); firstchild.colors.push("black"); console.log(firstchild.colors) // ["blue", "red", "white", "black"] console.log(secondchild.colors) // ["blue", "red", "white", "black"]
第二,不能像父類型構造函數傳參數,書裏準確說法是,沒有辦法在不影響全部實例的狀況下,給父類構造函數傳遞參數。
優勢:
缺點:
在子類型的構造函數中調用父類的構造函數,使用父類的構造函數來加強子類實例,等因而複製父類的實例屬性給子類(不用原型)
function son(age) { father.call(this); this.age = age; }; son.prototype = new father('jason'); son.prototype.getAge = function () { return this.age; } let firstchild = new son('19'); let secondchild = new son('20'); firstchild.colors.push("black"); console.log(firstchild.colors); // ["blue", "red", "white", "black"] console.log(secondchild.colors); // ["blue", "red", "white"]
優勢:
缺點:
也就是將原型鏈繼承和構造函數繼承融合,原型鏈實現對原型屬性和方法的繼承,構造函數實現對實例屬性的繼承。
這樣既保證了原型上函數的複用,也保證了每一個實例有本身的屬性。
function son(name, age) { father.call(this, name); this.age = age; }; son.prototype = new father(); son.prototype.getAge = function () { return this.age; } let firstchild = new son('jason', '19'); let secondchild = new son('jason junior', '18'); firstchild.colors.push("black"); console.log(firstchild.colors); // ["blue", "red", "white", "black"] console.log(secondchild.colors); //["blue", "red", "white"] console.log(firstchild.getName()); // jason console.log(secondchild.getName()); // jason junior console.log(firstchild.getAge()); //19 console.log(secondchild.getAge()); //18
特色:
缺點:
爲父類實例添加新特性,做爲子類實例返回
let p = { name: 'jason', colors: ['white', 'black', 'red'] } function object (o) { function F() {}; F.prototype = o; return new F(); } let firstchild = object(p) let secondchild = object(p) firstchild.name = 'jason1' firstchild.colors.push('blue') secondchild.name = 'jason2' secondchild.colors.push('green') console.log(p.colors) // ["white", "black", "red", "blue", "green"]
ECMAScript 5新增Object.create()方法規範原型式繼承。兩個參數,一個參數是新對象原型的對象,一個參數是對象定義額外屬性的對象,第二個可忽略,就等於上述object函數了
創造一個用於封裝繼承過程的函數,該函數內部以某種方式加強對象。
function create(o) { let clone = object(o); o.sayHi = function () { console.log('Hi') } return o; }
組合繼承雖然好用,可是也有缺陷,就是會調用兩次構造函數,一次在建立時候,一次在內部,那個call方法。
所謂寄生組合繼承,即經過借用構造函數方式,繼承屬性,經過原型鍊形式繼承方法。
沿用寄生方式:
function inheritPrototype (sub, sup) { let prototype = object(sup.prototype); prototype.constructor = sub; sub.prototype = prototype; }
function father(name) { this.name = name; this.colors = ['blue', 'red', 'white']; } father.prototype.getName = function () { return this.name; } function son(name, age) { father.call(this, name); this.age = age; }; function object (o) { function F() {}; F.prototype = o; return new F(); } function inheritPrototype (sub, super) { let prototype = object(super.prototype); prototype.constructor = sub; sub.prototype = prototype; } inheritPrototype(son, father); son.prototype.getAge = function () { return this.age; }
優勢:
缺點:
參考 <<JavaScript高級程序設計>>總結