摘要:和構(gòu)造函數(shù)前面提到,是個內(nèi)置隱藏屬性,雖然在可以通過訪問,但是其設(shè)計本意是不可被讀取和修改的,那么我們?nèi)绾卫迷玩渷斫⒗^承關(guān)系提供了關(guān)鍵字。到這兒,思路就清晰了,怎么讓對象和對象的相連實現(xiàn)繼承只需把的構(gòu)造函數(shù)的連接到就行了。
什么是繼承?
大多數(shù)人使用繼承不外乎是為了獲得這兩點好處,代碼的抽象和代碼的復(fù)用。
代碼的抽象就不用說了,交通工具和汽車這類的例子數(shù)不勝數(shù),在傳統(tǒng)的OO語言中(比如Java),代碼的抽象更多的是使用接口(interface)來實現(xiàn),而使用繼承更多地是為了代碼的復(fù)用(雖然現(xiàn)在強調(diào)使用組合而不是使用繼承)。
怎么復(fù)用的?打個比方,class A 繼承了 class B,class A便擁有了class B 的public 和 protected類型的變量和方法,用最簡單的方法去想,便是 class B 將 這些屬性和方法直接copy給class A,這樣便實現(xiàn)了繼承。
因此我們可以這樣說,繼承實際上是一種類與類之間的copy行為。
JavaScript中的繼承在JavaScript中沒有類的概念,只有對象。雖然現(xiàn)在人們經(jīng)常使用class關(guān)鍵字,這讓JavaScript看起來似乎是擁有了”類”,可表面看到的不一定是本質(zhì),class只是一塊糖,嚼碎了才知道里面其實還是原型鏈那一套。因此,JavaScript中的繼承只是對象與對象之間的繼承。反觀繼承的本質(zhì),繼承便是讓子類擁有父類的一些屬性和方法,那么在JavaScript中便是讓一個對象擁有另一個對象的屬性和方法。
所以,這給我了我們一條十分清晰的思路,JavaScript中如何實現(xiàn)繼承?只需讓一個對象擁有另一個對象的屬性和方法,這就實現(xiàn)了。
利用Mixin既然讓一個對象擁有另一個對象的屬性和方法,首先想到的便是利用Mixin的粗暴方式,直接將對象的屬性和方法強制copy到另一個對象。
就像這樣
function mixin(subObj, parentObj) { for (var prop in parentObj) { if (!(prop in subObj)) { subObj[prop] = parentObj[prop] } } }
當然也可以用ES6中的更優(yōu)雅的Object.assign。
這段代碼就實現(xiàn)了最簡單的從一個對象復(fù)制屬性和方法到另一個對象。然而這種方法有一個缺陷,如果父對象的屬性是引用類型,比如一個對象或者數(shù)組,那么修改子對象的時候勢必會對父對象也造成修改,這顯然不可接受。一種想法是采用深度克隆,然而又可能會有循環(huán)引用的問題。
所以,這種繼承方式,比較適合對簡單對象的拓展,不太適合更復(fù)雜的繼承。
利用原型鏈首先來說一下什么是原型,原型在JavaScript中,其實就是某個對象的一個屬性。只不過這個屬性很特殊,對于外界一般是不可見(在chrome中可以通過__proto__獲取),我們一般把它叫作[[Prototype]]。這里和函數(shù)的prototype屬性很相似但卻是兩個東西,后面會提到。
那么什么是原型鏈呢,顧名思義就像這樣:
obj1.[[Prototype]] ===> obj2.[[Prototype]] ===> obj3.[[Prototype]]…. ===> Object.prototype
某一對象的原型屬性中保存著另一個對象,以此類推,好像鏈子一樣串起來。
鏈的終點是Object.prototype對象,因此Object.prototype沒有原型。當我們構(gòu)建一個對象,這個對象的默認的原型就是Object.prototype
在chrome中驗證一下:
var a = {} Object.prototype === a.__proto__ // true
那么我們?nèi)绾斡迷玩湆崿F(xiàn)繼承呢?這要歸功于JavaScript中的委托機制。
當我們獲取一個對象的某個屬性時,比如a.b,會默認調(diào)用一個內(nèi)置的[[Get]]方法,這個[[Get]]方法的算法就是:
在當前對象里查找,找不到則委托給當前對象的[[Prototype]],再找不到則委托給[[Prototype]]的[[Prototype]],直到Object.prototype中也沒找到,則返回undefined。
因此,我們想讓對象a擁有對象b的屬性和方法,即對象a繼承對象b,只需要把b賦值給a的[[Prototype]],利用屬性查找的委托機制,實現(xiàn)了a也”擁有”了b的屬性和方法,而且當a中有和b中的同名屬性時,由于”屏蔽作用”,只有a中的屬性會被優(yōu)先獲取到,實現(xiàn)了override,看起來相當完美。
new 和 “構(gòu)造函數(shù)”前面提到,[[Prototype]]是個內(nèi)置隱藏屬性,雖然在chrome可以通過__proto__訪問,但是其設(shè)計本意是不可被讀取和修改的,那么我們?nèi)绾卫迷玩渷斫⒗^承關(guān)系?
JavaScript提供了new關(guān)鍵字。
通常,在類似Java這樣的OO語言中,new被用來實例化一個類,然而在JavaScript中,new僅僅是一個函數(shù)調(diào)用的方式!
JavaScript中的函數(shù)也很奇怪,每一個函數(shù)都有一個默認的prototype屬性,這個不同于對象的[[Prototype]]屬性,函數(shù)的prototype是故意暴露出來的,而且這個屬性還不為空,還有prototype還有另一個屬性叫constructor,這個constructor竟然又引用回來了這個函數(shù)本身!于是我們看到的效果是這樣的:
用new來調(diào)用函數(shù)有什么不同的呢?new其實做了三件事:
創(chuàng)建一個新對象
將這個新對象的[[Prototype]]連接到調(diào)用函數(shù)的prototype上
綁定調(diào)用函數(shù)的this并調(diào)用
用代碼來表示就是:
function New(fn) { var tmp = {} tmp.__proto__ = fn.prototype fn.call(tmp) return tmp }
可以看到,new幫我們把對象的[[Prototype]]連接到了函數(shù)的prototype上。
到這兒,思路就清晰了,怎么讓對象a和對象b的[[Prototype]]相連實現(xiàn)a繼承b?
只需把a的”構(gòu)造函數(shù)”的[[Prototype]]連接到b就行了。
來實現(xiàn)一下:
function A() { } var b = { show: function() { console.log("這是來自b的方法") } } A.prototype = b // 這里修復(fù)了原先的 constructor A.prototype.constructor = A var a = new A() a.show() // 這是來自b的方法更簡單的Object.create
ES5中提供的Object.create更簡單粗暴,可以直接創(chuàng)建一個對象并將這個對象的[[Prototype]]指向傳入的對象
var b = {c: 1} var a = Object.create(b) console.log(a.c) // 1模擬類繼承
在JavaScript中沒有類的概念,雖然從ES6開始擁有了class關(guān)鍵字,但其背后仍然是原型鏈作支撐,所以這里還是用最本質(zhì)的原型來模擬”類”的繼承。這才是JavaScript的本來面目!
/** * 實現(xiàn) A 繼承 B */ function B(b) { this.b = b } function A(a, b) { // 調(diào)用B并綁定this B.call(this, b) this.a = a } A.prototype = Object.assign({}, B.prototype) A.prototype.constructor = A var c = new A(1, 2) console.log(c.a) // 1 // c 擁有了只有B的實例才擁有的 b 屬性 console.log(c.b) // 2
總結(jié)簡單來說,繼承即是copy和復(fù)用,JavaScript的繼承其實就是利用原型鏈的查找和委托來實現(xiàn)屬性和方法的復(fù)用,new關(guān)鍵字和”構(gòu)造函數(shù)”只是連接原型鏈的工具,這樣的工具還有Object.create。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/100148.html
摘要:題目來源前端實習生面試總結(jié)最近開始了幾次面試,雖然還不知道結(jié)果如何,但是還是要記錄下來進行一個總結(jié),同樣也希望對正在準備面實習生的童鞋們有所幫助最后一個參數(shù)是做什么用的答規(guī)定事件是冒泡還是捕獲。 最近一直在多看基礎(chǔ)的書多碼代碼準備找實習,在網(wǎng)上也搜羅了不少面經(jīng),現(xiàn)在把搜羅到的實習生面試題自己整理一下。 題目來源:前端實習生面試總結(jié)最近開始了幾次面試,雖然還不知道結(jié)果如何,但是還是要記錄...
摘要:拿到秋招的同學,如確定入職需與用人單位簽署三方協(xié)議,以保證雙方的利益不受損失。當然每個崗位所要求的側(cè)重點不同,但卻百變不離其宗。方法論要想達成某個目標都有其特定的方法論,學習技術(shù)也不例外,掌握適當?shù)膶W習方法才能事半功倍。 寫在前面的話 筆者從17年的2月份開始準備春招,其中遇到不少坑,也意識到自己走過的彎路。故寫了這篇文章總結(jié)一番,本文適合主動學習的,對自己要學的課程不明確的,對面試有...
閱讀 3060·2021-10-12 10:12
閱讀 5394·2021-09-26 10:20
閱讀 1528·2021-07-26 23:38
閱讀 2818·2019-08-30 15:54
閱讀 1650·2019-08-30 13:45
閱讀 1968·2019-08-30 11:23
閱讀 3092·2019-08-29 13:49
閱讀 836·2019-08-26 18:23