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