JavaScript原型鏈及常見的繼承方法
原型鏈
原型鏈的概念
在JavaScript中,每一個構(gòu)造函數(shù)都有一個原型,這個原型中有一個屬性constructor
會再次指回這個構(gòu)造函數(shù),這個構(gòu)造函數(shù)所創(chuàng)造的實例對象,會有一個指針(也就是我們說的隱式原型__proto__
或者是瀏覽器中顯示的[[Prototype]]
)指向這個構(gòu)造函數(shù)的原型對象。如果說該構(gòu)造函數(shù)的原型對象也是由另外一個構(gòu)造函數(shù)所創(chuàng)造的實例,那么該構(gòu)造函數(shù)的原型對象也會存在一個指針指向另外一個構(gòu)造函數(shù)的原型對象,周而復(fù)始,就形成了一條原型鏈。 最特別的是所有的沒有經(jīng)過再繼承函數(shù)都是由Function
實例化來的,所有的除了函數(shù)外的對象都是由Object
實例化來的,其中Object
也是由Function
實例化來的,但是Object.prototype.__proto__ === null
是成立的。
再強調(diào)一遍:原型鏈?zhǔn)茄刂鴮ο蟮碾[式原型一層層的去尋找的,找到的是構(gòu)造函數(shù)所創(chuàng)造的實例。例如下:
這個就是相當(dāng)于由Student
new 出來的實例s
,查找自身的 name
屬性,然后沿著原型鏈查找,找到Student
中的prototype
當(dāng)中,然后找到了name
這個屬性。
而這個例子,由紅框框起來的代碼(寄生繼承的關(guān)鍵代碼),代替注釋掉的部分,最終s
是找不到name
屬性的,這是因為紅框中的代碼,僅僅是將Student
的隱式原型指向了Person
的顯示原型對象,未能創(chuàng)建任何的實例,當(dāng)然就不會存在屬性這個說法。
原型鏈的問題
原型鏈的問題主要有兩個方面,第一個問題是,當(dāng)原型中出現(xiàn)包含引用值(比如數(shù)組)的時候,所有在這條原型鏈中的實例會共享這個屬性,造成“一發(fā)而動全身”的問題。第二個問題就是子類在實例化時,不能夠給父類型的構(gòu)造函數(shù)傳參,即
無法在不影響所有對象實例的情況下把參數(shù)傳遞進父類型的構(gòu)造函數(shù)傳參
幾種常見的繼承方法
盜用構(gòu)造函數(shù)
function SuperType() { this.friends = ['張三','李四'] } function SubType() { SuperType.call(this); } const p1 = new SubType(); p1.friends.push('王武'); const p2 = new SubType(); console.log(p2.friends); // ['張三','李四', '王武']
盜用構(gòu)造函數(shù)實現(xiàn)繼承在這個例子中有了充分的體現(xiàn): 首先在子類的構(gòu)造函數(shù)中調(diào)用父類的構(gòu)造函數(shù)。因為畢竟函數(shù)就是特定上下文中執(zhí)行代碼的簡單對象,所以可以使用call()
方法以創(chuàng)建的對象為上下文執(zhí)行的構(gòu)造函數(shù)。
盜用構(gòu)造函數(shù)的主要問題,也是創(chuàng)建對象的幾種方式中構(gòu)造函數(shù)模式自定義類型的問題:必須在構(gòu)造函數(shù)中定義方法,造成內(nèi)存浪費。另外,子類也不能訪問父類原型上定義的方法,因此,盜用構(gòu)造函數(shù)也不會單獨使用。
組合繼承
組合繼承也稱為偽經(jīng)典繼承,綜合了原型鏈和構(gòu)造函數(shù),將兩者的有點結(jié)合起來?;镜乃悸肪褪鞘褂迷玩溊^承原型上的屬性和方法,而通過盜用構(gòu)造函數(shù)繼承實現(xiàn)實例的屬性。這樣就可以把方法定義在原型上實現(xiàn)復(fù)用,又可以讓每個實例有自己的屬性。
function SuperType(name) { this.name = name; this.friends = ['張三','李四']; } SuperType.prototype.sayName = function() { console.log(this.name) } // 繼承方法 SubType.prototype = new SuperType(); function SubType(name, age) { SuperType.call(this, name); this.age = age; } const p1 = new SubType('趙六', 12); const p2 = new SubType('趙六2', 22); // 創(chuàng)建的 p1 和 p2 能夠擁有自己的屬性并且引用值屬性也是獨立的,此外,每一個實例能夠公用父類的方法。
組合繼承已經(jīng)接近完美了,但是,我們發(fā)現(xiàn),實現(xiàn)組合繼承就要調(diào)用兩次父類構(gòu)造函數(shù)。在本質(zhì)上,子類型最終是要包含超類對象的所有實例屬性,子類構(gòu)造函數(shù)只要在執(zhí)行時重寫自己的原型就行了,這就為減少一次調(diào)用父類構(gòu)造函數(shù)提供了思路。
原型式繼承
const person = { name: 'zs', friends: ['ls','ww'] } // 創(chuàng)造出一個實例,這個實例的隱式原型指向 person const anotherPerosn = Object.create(person); anotherPerosn.name = 'xm' anotherPerosn.friends.push('zl') console.log(anotherPerosn.name) // xm console.log(anotherPerosn.friends) // ['ls','ww', 'zl']; const anotherPerosn2 = Object.create(person); anotherPerosn.name = 'xh' anotherPerosn.friends.push('dd') console.log(anotherPerosn2.name) // xh console.log(anotherPerosn2.friends) // ['ls','ww', 'zl', 'dd'];
對于原型鏈繼承就不再過多的解釋了。。。。
寄生式繼承
寄生式繼承與原型式繼承比較相似,都會存在屬性引用值共享的問題。
function createAnotherPerson(original) { const clone = Object.create(original); //通過調(diào)用函數(shù)創(chuàng)建一個新的對象 clone.sayHi = function() { // 以某種方式增強這個對象 console.log('Hi'); } return clone; }
寄生式繼承,不僅存在著屬性引用值共享的問題而且函數(shù)還不能進行復(fù)用。
寄生組合式繼承
// 實現(xiàn)了寄生式組合繼承的核心邏輯 function inheritPrototype(subFn, parentFn){ subFn.prototype = Object.create(parentFn.prototype); // 創(chuàng)建賦值對象 Object.defineProperty(subFn.prototype,'constructor', { // 增強對象 enumerable: false, writable: false, configurable: false, value: subFn, }) } function Person(name, age, address) { this.name = name; this.age = age; this.address = address; } Person.prototype.eating = function() { console.log(this.name + "正在吃飯"); } // 共享方法 function Student(name, age, address, sno) { Person.call(this, name, age, address); // 綁定 this 確保創(chuàng)建出來的對象是相互獨立的 this.sno = sno; this.studing = function() { console.log(`${this.name}正在學(xué)習(xí)`) } } function Teacher(name, age, address, tno) { Person.call(this, name, age, address) this.tno = tno; }
寄生+組合式(構(gòu)造函數(shù)+原型鏈)完美的解決了其他繼承出現(xiàn)的問題。
到此這篇關(guān)于JavaScript原型鏈及常見的繼承方法的文章就介紹到這了,更多相關(guān)JS原型鏈內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
原生JS實現(xiàn)的多個彩色小球跟隨鼠標(biāo)移動動畫效果示例
這篇文章主要介紹了原生JS實現(xiàn)的多個彩色小球跟隨鼠標(biāo)移動動畫效果,涉及javascript事件響應(yīng)、頁面元素屬性動態(tài)修改及隨機數(shù)應(yīng)用等相關(guān)操作技巧,需要的朋友可以參考下2018-02-02uni-app小程序沉浸式導(dǎo)航實現(xiàn)的全過程
在跨端項目開發(fā)中,uniapp是個不錯的框架,下面這篇文章主要給大家介紹了關(guān)于uni-app小程序沉浸式導(dǎo)航實現(xiàn)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-10-10Javascript中找到子元素在父元素內(nèi)相對位置的代碼
因為想自動定位到子元素,所以一直在找各種找尋元素位置的代碼。 不過總是找不到可以定位子元素相對位置的代碼2012-07-07JavaScript基于ChatGPT實現(xiàn)打字機消息回復(fù)
ChatGPT 是一個基于深度學(xué)習(xí)的大型語言模型,處理自然語言需要大量的計算資源和時間,響應(yīng)速度肯定比普通的讀數(shù)據(jù)庫要慢的多,本文介紹了ChatGPT打字機消息回復(fù)實現(xiàn)原理,感興趣的同學(xué)可以跟著小編一起學(xué)習(xí)2023-05-05javascript eval()應(yīng)用實例 select
javascript eval應(yīng)用小例子。實例代碼就是控制checkbox的選擇與取消的函數(shù),非常不錯。2009-07-07