摘要:組合式繼承是最常用的繼承模式,但組合繼承使用過程中會被調用兩次一次是創建子類型的時候,另一次是在子類型構造函數的內部。
首先需要了解原型鏈機制: 原型鏈作為實現繼承的主要方法,其基本思想就是利用原型讓一個引用類型繼承另 一個引用類型的屬性和方法.
構造函數、原型、實例之間的關系: 每個構造函數都有一個原型對象(prototype),原型對象都包含一個指向構造函數的指針(constructor),而實例都包含一個指向原型對象的內部指針(__propto__).
自我理解: 其實每個Function都是Object基類的一個實例,所以每個Function上都有一個__proto__指向了Object.prototype.當查找一個實例的屬性時,會先從這個實例的自定義屬性上找,如果沒有的話通過__proto__去實例所屬類的原型上去找,如果還沒有的話再通過原型(原型也是對象,只要是對象就有__proto__屬性)的__proto__到Object的原型上去找,一級一級的找,如果沒有就undefined(Object的__proto__返回undefined).
(一) 原型鏈繼承 :
function Parent(name) { this.name = name; } Parent.prototype.printName = function() { console.log("parent name:", this.name); } function Child(name) { this.name = name; } Child.prototype = new Parent("father"); Child.prototype.constructor = Child;//由于Child.prototype繼承Parent,導致constructor丟失 Child.prototype.printName = function() { console.log("child name:", this.name); } var child = new Child("son"); child.sayName(); // child name: son
這種方法存在兩個缺點:
1.子類型無法給超類型傳遞參數; 2.Child.prototype.sayName 必須寫在 Child.prototype = new Parent("father"); 之后,不然就會被覆蓋掉。
(二) 類式繼承:
function Parent(name) { this.name = name; } Parent.prototype.printName = function() { console.log("parent name:", this.name); } Parent.prototype.doSomthing = function() { console.log("parent do something!"); } function Child(name, parentName) { Parent.call(this, parentName); this.name = name; } Child.prototype.printName = function() { console.log("child name:", this.name); } var child = new Child("son"); child.printName(); // child name: son child.doSomthing(); // TypeError: child.doSomthing is not a function
相當于 Parent 這個函數在 Child 函數中執行了一遍,并且將所有與 this 綁定的變量都切換到了 Child 上,這樣就克服了第一種方式帶來的問題。
缺點:沒有原型,每次創建一個 Child 實例對象時候都需要執行一遍 Parent 函數,無法復用一些公用函數。
(三) 組合式繼承:前兩種方式的結合
function Parent(name) { this.name = name; } Parent.prototype.printName = function() { console.log("parent name:", this.name); } Parent.prototype.doSomething = function() { console.log("parent do something!"); } function Child(name, parentName) { Parent.call(this, parentName);// 第二次調用 this.name = name; } Child.prototype = new Parent();// 第一次調用 Child.prototype.constructor = Child; Child.prototype.printName = function() { console.log("child name:", this.name); } var child = new Child("son"); child.printName(); // child name: son child.doSomething(); // parent do something!
組合式繼承是比較常用的一種繼承方法,其背后的思路是使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承。
這樣,既通過在原型上定義方法實現了函數復用,又保證每個實例都有它自己的屬性。
組合式繼承是 JS 最常用的繼承模式,但組合繼承使用過程中會被調用兩次:一次是創建子類型的時候,另一次是在子類型構造函數的內部。
第一次調用構造函數顯然是沒有必要的,因為第一次調用構造函數時候不需要函數內部的那些實例屬性,這么寫只是想獲得其原型上的方法罷了,所以這時候你可能會這樣寫:
Child.prototype = Parent.prototype;
這樣寫顯然是不對的:
1.首先,你這樣寫的話相當于是子類和父類都指向同一個對象,這時候如果你添加了新的方法給 Child 但實際上 Parent 并不需要,相當于強行給 Parent 添加了一個未知的方法。
2.其次,仔細想想,這樣體現不出繼承的多態性,比如此時子類想要重寫父類的 getName 的方法,那么父類的方法也就會隨之修改,這顯然違背了多態性。
也就是說我們第一次調用構造函數的時候,其實是不管構造函數里面的內容,這是我們可以new一個空函數,將其prototype指向Parent.prototype,代碼如下:
(四) 寄生組合式繼承:
function Parent(name) { this.name = name; } Parent.prototype.printName = function() { console.log("parent name:", this.name); } function Child(name, parentName) { Parent.call(this, parentName); this.name = name; } function inheritPrototype(Parent, Child) { Child.prototype = Object.create(Parent.prototype); //修改 Child.prototype.constructor = Child; } inheritPrototype(Parent, Child); Child.prototype.printName = function() { console.log("child name:", this.name); } Child.prototype.constructor = Child; var parent = new Parent("father"); parent.printName(); // parent name: father var child = new Child("son", "father"); child.printName(); // child name: son
(五) ES 6 繼承:
class Parent { constructor(name) { this.name = name; } doSomething() { console.log("parent do something!"); } printName() { console.log("parent name:", this.name); } } class Child extends Parent { constructor(name, parentName) { super(parentName); this.name = name; } printName() { console.log("child name:", this.name); } } const child = new Child("son", "father"); child.printName(); // child name: son child.doSomething(); // parent do something! const parent = new Parent("father"); parent.printName(); // parent name: father
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97881.html
摘要:今天閑來無事,看見幾行小字。又說所有對象,繼承終是。強行押韻一波這首詩的意思就是說的我今天沒有什么事情,然后無意中又在網上看到了任何對象都是從對象繼承而來的這句話。一時興起,便去驗證這句話。 今天閑來無事,看見幾行小字。又說所有對象,繼承終是Obj。—— 強行押韻一波 這首詩的意思就是說的我今天沒有什么事情,然后無意中又在網上看到了任何對象都是從Object對象繼承而來的這句話。一時興...
摘要:繼承可以使得子類具有父類別的各種屬性和方法。繼承是類與類之間的關系。繼承的實質就是兩次的原型搜索,像是實例屬性而不是繼承,才是繼承。更多用法見繼承。 前言 面試中最常會問到的問題:什么是繼承?如何分別用 ES5 和 ES6 實現?想要學習繼承,必須先學好原型與原型鏈,如果此部分還不清楚,請先學習此部分再來閱讀本文,可參考我的文章JS之原型與原型鏈或瀏覽其他相關的學習網站。 定義 繼承...
摘要:前奏的構造函數就是函數的本身正文的繼承是通過函數結合原型而實現的,繼承是先實例化父類直接繼承使用這個詞來定義類的構造函數是函數的繼承用與繼承是實例化子類對象的時候繼承父類然后繼承 最開始接觸的時候es5,js的類概念是 通過函數 實現的。 前奏:showImg(https://segmentfault.com/img/bVbuYHF?w=468&h=345);es5的構造函數就...
摘要:前言見解有限,如有描述不當之處,請幫忙及時指出,如有錯誤,會及時修正。倘若用的是中文搜索。所以最終的實例對象仍然能進行正常的原型鏈回溯,回溯到原本的所有原型方法這樣通過一個巧妙的欺騙技巧,就實現了完美的繼承。 前言 見解有限,如有描述不當之處,請幫忙及時指出,如有錯誤,會及時修正。 20180201更新: 修改用詞描述,如組合寄生式改成寄生組合式,修改多處筆誤(感謝@Yao Ding的...
摘要:但是,的本質仍然是函數,是構造函數的另外一種寫法。報錯原生構造函數的繼承對于一些原生的構造函數,比如,,,等,在是無法通過方法實現原生函數的內部屬性,原生函數內部的無法綁定,內部屬性獲得不了。 在沒有學習 ES6 之前,學習 React,真的是一件非常痛苦的事情。即使之前你對 ES5 有著很好的基礎,包括閉包、函數、原型鏈和繼承,但是 React 中已經普遍使用 ES6 的語法,包括 ...
摘要:一般我們對這種構造函數命名都會采用,并把它稱呼為類,這不僅是為了跟的理念保持一致,也是因為的內建類也是這種命名。由生成的對象,其是。這是標準的規定。本文的主題是原型系統的變遷,所以并沒有涉及和對原型鏈的影響。 概述 JavaScript 的原型系統是最初就有的語言設計。但隨著 ES 標準的進化和新特性的添加。它也一直在不停進化。這篇文章的目的就是梳理一下早期到 ES5 和現在 ES6,...
閱讀 3266·2021-11-18 10:02
閱讀 3443·2021-10-11 10:58
閱讀 3376·2021-09-24 09:47
閱讀 1120·2021-09-22 15:21
閱讀 3915·2021-09-10 11:10
閱讀 3277·2021-09-03 10:28
閱讀 1748·2019-08-30 15:45
閱讀 2136·2019-08-30 14:22