<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
JavaScript
在程式語言界是個特殊種類,它和其他程式語言很不一樣,JavaScript 可以在執行的時候動態地改變某個變數的型別。
比如你永遠也沒法想到像isTimeout這樣一個變數可以存在多少種型別,除了布林值true
和false
,它還可能是undefined、1和0、一個時間戳,甚至一個物件。
如果程式碼跑異常,開啟瀏覽器,開始斷點偵錯,發現InfoList這個變數第一次被賦值的時候是個陣列:
[{name: 'test1', value: '11'}, {name: 'test2', value: '22'}]
過了一會竟然變成了一個物件:
{test1:'11', test2: '22'}
除了變數可以在執行時被賦值為任何型別以外,JavaScript
中也能實現繼承,但它不像 Java、C++、C# 這些程式語言一樣基於類來實現繼承,而是基於原型進行繼承。
這是因為 JavaScript
中有個特殊的存在:物件。每個物件還都擁有一個原型物件,並可以從中繼承方法和屬性。
提到物件和原型,有如下問題:
JavaScript
的函數怎麼也是個物件?proto
和prototype
到底是啥關係?在 JavaScript 中,物件由一組或多組的屬性和值組成:
{ key1: value1, key2: value2, key3: value3, }
在 JavaScript
中,物件的用途很是廣泛,因為它的值既可以是原始型別(number、string、boolean、null、undefined、bigint和symbol),還可以是物件和函數。
不管是物件,還是函數和陣列,它們都是Object
的範例,也就是說在 JavaScript
中,除了原始型別以外,其餘都是物件。
這也就解答了問題1:JavaScript
的函數怎麼也是個物件?
在 JavaScript 中,函數也是一種特殊的物件,它同樣擁有屬性和值。所有的函數會有一個特別的屬性prototype,該屬性的值是一個物件,這個物件便是我們常說的“原型物件”。
我們可以在控制檯列印一下這個屬性:
function Person(name) { this.name = name; } console.log(Person.prototype);
列印結果顯示為:
可以看到,該原型物件有兩個屬性:constructor
和proto
。
到這裡,我們彷彿看到疑惑 “2:proto和prototype到底是啥關係?”的答案要出現了。在 JavaScript 中,proto屬性指向物件的原型物件,對於函數來說,它的原型物件便是prototype
。
函數的原型物件prototype有以下特點:
prototype
)都擁有constructor屬性,該屬性指向與之關聯的建構函式,在這裡建構函式便是Person函數;我們可以用這樣一張圖來描述prototype
、proto和constructor
三個屬性的關係:
從這個圖中,我們可以找到這樣的關係:
物件之所以使用廣泛,是因為物件的屬性值可以為任意型別。因此,屬性的值同樣可以為另外一個物件,這意味著 JavaScript 可以這麼做:通過將物件 A 的proto屬性賦值為物件 B,即:
A.__proto__ = B
此時使用A.proto便可以存取 B 的屬性和方法。
這樣,JavaScript 可以在兩個物件之間建立一個關聯,使得一個物件可以存取另一個物件的屬性和方法,從而實現了繼承;
以Person
為例,當我們使用new Person()建立物件時,JavaScript 就會建立建構函式Person的範例,比如這裡我們建立了一個叫“zhangsan”的Person:
var zhangsan = new Person("zhangsan");
上述這段程式碼在執行時,JavaScript 引擎通過將Person的原型物件prototype賦值給範例物件zhangsan的proto屬性,實現了zhangsan對Person的繼承,即執行了以下程式碼:
//JavaScript 引擎執行了以下程式碼 var zhangsan = {}; zhangsan.__proto__ = Person.prototype; Person.call(zhangsan, "zhangsan");
我們來列印一下zhangsan範例:
console.log(zhangsan)
結果如下圖所示:
可以看到,zhangsan作為Person
的範例物件,它的proto指向了Person的原型物件,即Person.prototype
。
這時,我們再補充下上圖中的關係:
從這幅圖中,我們可以清晰地看到建構函式和constructor
屬性、原型物件(prototype)和proto、範例物件之間的關係,這是很多容易混淆。根據這張圖,我們可以得到以下的關係:
那麼現在,關於proto和prototype的關係,我們可以得到這樣的答案:
proto
屬性來標識自己所繼承的原型物件,但只有函數才有prototype屬性;所以一個物件可通過proto存取原型物件上的屬性和方法,而該原型同樣也可通過proto存取它的原型物件,這樣我們就在範例和原型之間構造了一條原型鏈。紅色線條所示:
當 JavaScript 試圖存取一個物件的屬性時,會基於原型鏈進行查詢。查詢的過程是這樣的:
Object
,Object.prototype.proto === nul
l。null沒有原型,並作為這個原型鏈中的最後一個環節;我們可以通過一個具體的例子,來表示基於原型鏈的物件屬性的存取過程,在該例子中我們構建了一條物件的原型鏈,並進行屬性值的存取:
var o = {a: 1, b: 2}; // 讓我們假設我們有一個物件 o, 其有自己的屬性 a 和 b: o.__proto__ = {b: 3, c: 4}; // o 的原型 o.__proto__有屬性 b 和 c:
當我們在獲取屬性值的時候,就會觸發原型鏈的查詢:
console.log(o.a); // o.a => 1 console.log(o.b); // o.b => 2 console.log(o.c); // o.c => o.__proto__.c => 4 console.log(o.d); // o.c => o.__proto__.d => o.__proto__.__proto__ == null => undefined
綜上,整個原型鏈如下:
{a:1, b:2} ---> {b:3, c:4} ---> null, // 這就是原型鏈的末尾,即 null
可以看到,當我們對物件進行屬性值的獲取時,會觸發該物件的原型鏈查詢過程。
既然 JavaScript 中會通過遍歷原型鏈來存取物件的屬性,那麼我們可以通過原型鏈的方式進行繼承。
也就是說,可以通過原型鏈去存取原型物件上的屬性和方法,我們不需要在建立物件的時候給該物件重新賦值/新增方法。比如,我們呼叫lily.toString()時,JavaScript 引擎會進行以下操作:
由於通過原型鏈進行屬性的查詢,需要層層遍歷各個原型物件,此時可能會帶來效能問題:
因此,我們在設計物件的時候,需要注意程式碼中原型鏈的長度。當原型鏈過長時,可以選擇進行分解,來避免可能帶來的效能問題。
除了通過原型鏈的方式實現 JavaScript
繼承,JavaScript 中實現繼承的方式還包括經典繼承(盜用建構函式)、組合繼承、原型式繼承、寄生式繼承,等等。
function Parent(name) { // 私有屬性,不共用 this.name = name; } // 需要複用、共用的方法定義在父類別原型上 Parent.prototype.speak = function() { console.log("hello"); }; function Child(name) { Parent.call(this, name); } // 繼承方法 Child.prototype = new Parent();
組合繼承模式通過將共用屬性定義在父類別原型上、將私有屬性通過建構函式賦值的方式,實現了按需共用物件和方法,是 JavaScript 中最常用的繼承模式。
雖然在繼承的實現方式上有很多種,但實際上都離不開原型物件和原型鏈的內容,因此掌握proto
和prototype
、物件的繼承等這些知識,是我們實現各種繼承方式的前提條件。
關於 JavaScript 的原型和繼承,常常會在我們面試題中出現。隨著 ES6/ES7 等新語法糖的出現,可能更傾向於使用class/extends等語法來編寫程式碼,原型繼承等概念逐漸變淡。
其次JavaScript 的設計在本質上依然沒有變化,依然是基於原型來實現繼承的。如果不瞭解這些內容,可能在我們遇到一些超出自己認知範圍的內容時,很容易束手無策。
到此這篇關於如何利用JavaScript 實現繼承的文章就介紹到這了,更多相關JavaScript 實現繼承內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45