摘要:三組合繼承結合原型鏈方式和借用構造函數方式的有點,進行改進的一種繼承方式。四寄生組合式繼承為了解決組合繼承中子構造函數的原型鏈出現冗余的屬性和方法,引入的一種繼承方式。
說在前面:
為了使代碼更為簡潔方便理解, 本文中的代碼均將“非核心實現”部分的代碼移出。
一、原型鏈方式
關于原型鏈,可點擊《深入淺出,JS原型鏈的工作原理》,本文不再重復敘述。
思路:讓子構造函數的原型等于父構造函數的實例
function A() { } A.prototype.fn = function (){ console.log("in A"); } function B() { } B.prototype = new A(); // 讓子構造函數的原型等于父構造函數的實例 var b = new B(); b.fn(); // in A console.log(b instanceof B); // true console.log(b instanceof A); // true console.log(b instanceof Object); // true
缺陷:如果父構造函數中的屬性為引用類型,則子構造函數的實例會出現相互影響的情況;
function A() { this.prop = ["1","2"]; } A.prototype.fn = function (){ console.log(this.prop); } function B() { } B.prototype = new A(); var b1 = new B(); var b2 = new B(); b1.fn(); // ?["1", "2"] b2.fn(); // ?["1", "2"] b1.prop.push("3"); // 子構造函數實例b1修改繼承過來的屬性 b2.prop.push("4"); // 子構造函數實例b2修改繼承過來的屬性 b1.fn(); // ["1", "2", "3", "4"] // b2上的修改影響了b1 b2.fn(); // ["1", "2", "3", "4"] // b1上的修改影響了b2
*導致缺陷原因:引用類型,屬性變量保存的是地址指針而非實際的值,這個指針指向了一塊用來保存實際內容的地址。實例化后,所有實例中變量保存了同一個指針,均指向同一個地址,當任何一個實例通過指針修改地址的內容(并非重新賦予新的指針地址或者修改指針指向)時,其他實例的也會受到影響。
二、借用構造函數方式
為了解決“原型鏈方式”繼承的缺陷,引入的一種“繼承”方案。
思路:通過call/apply,在子構造函數中調用父類的構造函數
function A() { this.prop = ["1","2"]; this.fn2 = function () { console.log(this.prop); } } A.prototype.fn = function (){ console.log(this.prop); } function B() { A.call(this); // 通過call/apply,在子構造函數中調用父類的構造函數 } var b1 = new B(); var b2 = new B(); b1.fn2(); //?["1", "2"] b2.fn2(); // ["1", "2"] b1.prop.push("3"); b2.prop.push("4"); b1.fn2(); //?["1", "2", "3"] b2.fn2(); // ["1", "2", "4"] b1.fn(); // 提示異常:b1.fn is not a function console.log(b1 instanceof B); // true console.log(b1 instanceof A); // false console.log(b1 instanceof Object); // true
缺陷:由于“繼承”過程中,A僅充當普通函數被調用,使得父構造函數A原型無法與形成子構造函數B構成原形鏈關系。因此無法形成繼承關系:"b1 instanceof A"結果為false,B的實例b1亦無法調用A原型中的方法。實際意義上,這種不屬于繼承。
三、組合繼承
結合“原型鏈方式”和“借用構造函數方式”的有點,進行改進的一種繼承方式。
思路:原型上的屬性和方法通過“原型鏈方式”繼承;父構造函數內的屬性和方法通過“借用構造函數方式”繼承
function A() { this.prop = ["1","2"]; } A.prototype.fn = function (){ console.log(this.prop); } function B() { A.call(this); // 借用構造函數方式 } B.prototype = new A(); // 原型鏈方式 var b1 = new B(); var b2 = new B(); b1.fn(); //?["1", "2"] b2.fn(); // ["1", "2"] b1.prop.push("3"); b2.prop.push("4"); b1.fn(); //?["1", "2", "3"] b2.fn(); // ["1", "2", "4"] console.log(b1 instanceof B); // true console.log(b1 instanceof A); // true console.log(b1 instanceof Object); // true
缺陷:子構造函數的原型出現一套冗余“父構造函數非原型上的屬性和方法”。上述代碼在執行“A.call(this);”時候,會給this(即將從B返回給b1賦值的對象)添加一個“prop”屬性;在執行“B.prototype = new A();”時,又會通過實例化的形式給B的原型賦值一次“prop”屬性。顯然,由于實例屬性方法的優先級高于原型上的屬性方法,絕大多數情況下,原型上的“prop”是不會被訪問到的。
四、寄生組合式繼承
為了解決“組合繼承”中子構造函數的原型鏈出現冗余的屬性和方法,引入的一種繼承方式。
思路:在組合繼承的基礎上,通過Object.create的方式實現原型鏈方式
function A() { this.prop = ["1","2"]; } A.prototype.fn = function (){ console.log(this.prop); } function B() { A.call(this); } B.prototype = Object.create(A.prototype); // Object.create的方式實現原型鏈方式 var b1 = new B(); var b2 = new B(); b1.fn(); //?["1", "2"] b2.fn(); // ["1", "2"] b1.prop.push("3"); b2.prop.push("4"); b1.fn(); //?["1", "2", "3"] b2.fn(); // ["1", "2", "4"] console.log(b1 instanceof B); // true console.log(b1 instanceof A); // true console.log(b1 instanceof Object); // true
最后補充
1、因為子構造函數的實例自身沒有constructor屬性,當我們訪問實例的constructor屬性時,實際是訪問原型的constructor屬性,該屬性應該指向(子)構造函數。但是上述例子中,代碼均會指向父構造函數。為了與ECMAScript規范保持一致,在所有的“原型鏈繼承”后,應當將原型的constructor屬性指向子構造函數本身:
B.prototype = .... --> B.prototype.constructor = B; <-- ...
2、Object.create是ECMAScript 5中加入的一個函數,這個函數的功能是:將入參(需為一個對象)作為原型,創建并返回一個新的(只有原型的)的對象。此功能等價于:
function object(o){ function F(){} F. prototype = o; return new F(); } // 來源于《JavaScript高級程序設計(第3版)》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/108850.html
摘要:原型繼承借助父級對象,通過構造函數創建一個以父級對象為原型的新對象這里,直接將父對象設置為子對象的原型,中的方法就是這種實現方式。構造器借用中的和方法非常好用,其改變方法執行上下文的功能在繼承的實現中也能發揮作用。 不同于基于類的編程語言,如 C++ 和 Java,JavaScript 中的繼承方式是基于原型的。同時由于 JavaScript 是一門非常靈活的語言,其實現繼承的方式也非...
摘要:本文重點不要試圖在內置類型的子類中重寫方法,可以繼承的可拓展類尋求變通掌握多重繼承中的和了解處理多重繼承的一些建議。子類化的代碼如下輸出小結上述問題只發生在語言實現的內置類型子類化情況中,而且只影響直接繼承內置類型的自定義類。 導語:本文章記錄了本人在學習Python基礎之面向對象篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。 本文重點: 1、不要試圖在內置...
摘要:繼承理論源于生活又高于生活在中繼承,和現實生活中繼承是相似的如兒子繼承父親財產子女的生理特性有父母的特性身高膚色性格等等只是一定比例上是這樣的,不是絕對的一樣中繼承方法有以下幾種本質區別方法特別注意是本質區別冒充繼承也稱之為借用構造函數這種 JS繼承 理論源于生活、又高于生活 在JS中繼承,和現實生活中繼承是相似的 如:兒子繼承父親財產、子女的生理特性有父母的特性(身高、膚色、性格...
摘要:一什么是是一款軟件項目管理和理解工具。基于項目對象模型的概念,通過添加一小段描述來管理項目的構建。另外如果子模塊中指定了版本號,那么會使用子模塊中指定的版本。 一、什么是Maven? Maven是一款軟件項目管理和理解工具。基于項目對象模型(POM)的概念,通過添加一小段描述來管理項目的構建。 二、為什么要使用Maven? 以前在用Java開發一個項目時,往往需要引入幾十或者上百個Ja...
摘要:本文是本人閱讀學習深入理解原型和閉包時所作的總結和筆記,當然也引用了很多原文,感興趣的朋友也可以直接去看原文。即這里的稱為隱式原型。注意,構造函數的函數名第一個字母大寫規則約定。但實際上,上述情況是一種理想的情況。 本文是本人閱讀學習深入理解JavaScript原型和閉包時所作的總結和筆記,當然也引用了很多原文,感興趣的朋友也可以直接去看原文。 1、一切都是對象 先說結論,一切引用類型...
閱讀 2112·2023-04-25 17:23
閱讀 2924·2021-11-17 09:33
閱讀 2518·2021-08-21 14:09
閱讀 3602·2019-08-30 15:56
閱讀 2610·2019-08-30 15:54
閱讀 1630·2019-08-30 15:53
閱讀 2136·2019-08-29 13:53
閱讀 1152·2019-08-29 12:31