本文完整代碼指路個人GitHub,歡迎star。js中的繼承方式有不少種,如下僅列出部分。git
代碼以下:github
function Parent() { this.name = 'jchermy'; } Parent.prototype.getName = function() { console.log(this.name); } function Child() { } Child.prototype = new Parent(); var child1 = new Child(); console.log(child1.getName()); //jchermy
這樣看來貌似能夠完美完成繼承,而後當屬性換成引用類型時,就會出現問題了,以下:函數
function Parent() { this.names = ["aa", "bb"]; //引用類型值 } function Child() { } Child.prototype = new Parent(); var child1 = new Child(); child1.names.push("cc"); console.log(child1.names); //["aa","bb","cc"] var child2 = new Child(); console.log(child2.names); //["aa","bb","cc"] child2.names.push("dd"); console.log(child1.names) //["aa", "bb", "cc", "dd"] console.log(child2.names);//["aa", "bb", "cc", "dd"] var p = new Parent(); console.log(p.names); //["aa", "bb"]
由此咱們能夠得出原型鏈繼承的缺點:this
function Parent() { this.names = ["aa", "bb"]; } function Child() { Parent.call(this); } var child1 = new Child(); child1.names.push("cc"); console.log(child1.names);//["aa", "bb", "cc"] var child2 = new Child(); console.log(child2.names);//["aa", "bb"] child2.names.push("dd"); console.log(child1.names); //["aa", "bb", "cc"] console.log(child2.names); //["aa", "bb", "dd"] var p = new Parent(); p.names; //["aa", "bb"]
能夠看出,借用構造函數繼承避免了一下原型鏈繼承的問題,主要體如今:spa
然而,借用構造函數繼承也有缺點。prototype
function Parent(name) { this.name = "parent "+name; } function Child(name) { this.name = "child"+name; Parent.call(this, name); } var child1 = new Child('hemin'); console.log(chil1.name); //"parent hemin" var child2 = new Child("aa"); console.log(child2.name); //"parent aa"
缺點:方法都在構造函數中定義,每次建立實例都會建立一遍方法code
組合繼承融合原型鏈繼承和構造函數的優勢,是JavaScript中最經常使用的繼承模式對象
function Parent(name) { this.name = name; this.colors = ["red", "blue"]; } Parent.prototype.getName = function() { console.log(this.name); } function Child(name, age) { Parent.call(this, name); this.age = age; } Child.prototype = new Parent(); Child.prototype.constructor = Child; var child1 = new Child("aa", 18); child1.colors.push("black"); child1.name; //"aa" child1.age; //18 child1.colors; //["red", "blue","black"] var child2 = new Child("bb", 20); child2.name; //"bb" child2.age; //20 child2.colors; //["red", "blue"]
然而組合繼承也有一個缺點,就是會調用兩次父構造函數。blog
以下:繼承
Child.prototype = new Parent(); var child1 = new Child('aa', '18');
因此,在這個例子中,若是咱們打印 child1 對象,咱們會發現 Child.prototype 和 child1 都有一個屬性爲colors,屬性值爲['red', 'blue']。
這個問題咱們在下面再討論。
function createObj(o) { function F() {} F.prototype = o; return new F(); } var person = { name: 'jchermy', friends: ["aa", "bb"] } var person1 = createObj(person); var person2 = createObj(person); //注意:修改person1.name的值,person2.name的值並未發生改變, //並非由於person1和person2有獨立的 name 值,而是由於person1.name = 'person1',給person1添加了 name 值,並不是修改了原型上的 name 值。 person1.name = "xiaomi"; console.log(person2.name); //"jchermy" person2.friends.push("cc"); console.log(person1.friends); //["aa", "bb", "cc"]
缺點:包含引用類型的屬性值始終會共享相應的值,與原型鏈繼承同樣
建立一個僅用於封裝繼承過程的函數,該函數在內部以某種形式作加強對象,最後返回對象
function createObj(o) { var clone = Object.create(o); clone.sayName = function () { console.log("hi"); } return clone; } var person = { name: "jchermy", friends: ["aa", "bb"] }; var person1 = createObj(person); var person2 = createObj(person); person1.name = "xiaomi"; console.log(person1.name); //"xiaomi" console.log(person2.name); //"jchermy" person1.friends.push("xxx"); console.log(person1.friends); // ["aa", "bb", "xxx"] console.log(person2.friends); // ["aa", "bb", "xxx"]
缺點:
還記得組合繼承中提到的那些問題嗎,那麼咱們該如何精益求精,避免這一次重複調用呢?
若是咱們不使用 Child.prototype = new Parent() ,而是間接的讓 Child.prototype 訪問到 Parent.prototype 呢?能夠這樣實現:
function Parent(name) { this.name = name; this.colors = ["red", "blue", "green"]; } Parent.prototype.getName = function () { console.log(this.name); } function Child(name, age) { Parent.call(this, name); this.age = age; } //關鍵的三步 var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; var child1 = new Child('xiaomi', 18); var child2 = new Child2('aa', 24); console.log(child1.name); //xiaomi console.log(child2.name); //aa child1.colors.push("black"); child1.colors; //["red", "blue", "green", "black"] child2.colors; //["red", "blue", "green"];
這種方式的高效率體現它只調用了一次 Parent 構造函數,而且所以避免了在 Parent.prototype 上面建立沒必要要的、多餘的屬性。與此同時,原型鏈還能保持不變;所以,還可以正常使用 instanceof 和 isPrototypeOf。開發人員廣泛認爲寄生組合式繼承是引用類型最理想的繼承範式。
若是文章對你有幫助的話,歡迎點贊和收藏!!有錯誤和不合理的地方歡迎你們指正!GitHub給個star就最好啦!=(//▽//)感謝你們~