JS 中繼承比較複雜,坑比較多,最近有點時間整理下,記錄下來。javascript
JS 的繼承實現方式大概分類以下的兩大類,每一種實現都有本身的有點和缺點,根據場景選擇吧java
經過修改原型鏈來來實現繼承git
經過複製父類來來實現繼承es6
爲了理解繼承的原型鏈的變化,我畫了原型鏈圖。下圖是沒有繼承的時候,父類和子類的原型鏈圖github
function Parent(name, age) { this.name = name; this.age = age; } Parent.prototype.getName = function () { return this.name; }; Parent.prototype.getAge = function () { return this.age; }; Parent.prototype.love = { game: ['DoTa'] }; Parent.pay = function(){ return 1000; }; function Son(){}
修改原型鏈也有好幾種,如今分開來講:app
實現原理:修改原型鏈(子類的原型指向父類的原型)實現繼承函數
優勢:簡單測試
缺點:子類修改影響父類this
原型鏈圖注意裏面的紅線spa
不調用構造方法實現
function Son(){} Son.prototype = new Parent(); Son.prototype.constructor = Son;
調用構造方法實現
function Son(){ Parent.apply(this, arguments) } Son.prototype = Parent.prototype; Son.prototype.constructor = Son;
詳細代碼實現:https://github.com/xuanxiao2013/f2e-practice/blob/master/javascript-inherit/inherit1.js
實現原理:修改原型鏈(經過加入臨時函數,阻止子類修改父類)實現繼承
優勢:子類即能繼承父類,又基本不影響父類,達到真正意義上的繼承
缺點:實例的對象和父類原型的對象相同的時候(父類的love),可能會出現子類修改父類對象原型中的全部屬性被實例共享,共享很適合函數,對基本值的屬性也能夠(實例上添加同名屬性),可是對引用類型的值的屬性來講,就會有問題
原型鏈圖注意裏面的紅線
ES3 實現方式詳細代碼實現:
function create(proto){ var F = function(){}; F.prototype = proto; return new F(); } function Son(){ Parent.apply(this, arguments); } Son.prototype = create(Parent.prototype); Son.prototype.constructor = Son;
ES5 實現方式詳細代碼實現:
function Son(){ Parent.apply(this, arguments); } Son.prototype = Object.create(Parent.prototype) Son.prototype.constructor = Son;
ES6 實現方式 詳細代碼實現:
class Parent{ constructor(name, age){ this.name = name; this.age = age; } } Parent.prototype.getName = function () { return this.name; }; Parent.prototype.getAge = function () { return this.age; }; Parent.prototype.love = { game: ['DoTa'] }; class Son extends Parent { constructor(...args){ super(...args); } }
都說ES6 的Class 只是個語法糖,看來緣由在這了
測試:
var parent = new Parent('jack', 40); log('parentName:' + parent.getName()); // parentName:jack var son = new Son('tom', 20); Son.prototype.getName = function(){ return this.name + ' has good father'; }; log('sonName:' + son.getName()); // sonName:tom has good father log('parentName:' + parent.getName()); // parentName:jack log(Son.prototype.constructor === Son); // true log(son instanceof Son); // true log(son instanceof Parent); // true log(son instanceof Object); // true log('parent love:' + parent.love.game); // parent love:DoTa log('son love:' + son.love.game); // son love:DoTa log('------------------------'); // 注意這裏 son.love.game = 'DoTa2'; log('parent love:' + parent.love.game); // parent love:DoTa2 log('son love:' + son.love.game); // son love:DoTa2 log('------------------------'); son.love = { game: 'DoTa3' }; // 注意這裏 log('parent love:' + parent.love.game); // parent love:DoTa2 log('son love:' + son.love.game); // son love:DoTa3
實現原理:經過深度複製把父類的方法複製一份給子類來實現繼承
優勢:子類即能繼承父類,又不影響父類,達到真正意義上的繼承
缺點:複雜
詳細代碼實現:https://github.com/xuanxiao2013/f2e-practice/blob/master/javascript-inherit/inherit3.js