摘要:深入系列的第一篇,從原型與原型鏈開(kāi)始講起,如果你想知道構(gòu)造函數(shù)的實(shí)例的原型,原型的原型,原型的原型的原型是什么,就來(lái)看看這篇文章吧。讓我們用一張圖表示構(gòu)造函數(shù)和實(shí)例原型之間的關(guān)系在這張圖中我們用表示實(shí)例原型。
構(gòu)造函數(shù)創(chuàng)建對(duì)象JavaScript深入系列的第一篇,從原型與原型鏈開(kāi)始講起,如果你想知道構(gòu)造函數(shù)的實(shí)例的原型,原型的原型,原型的原型的原型是什么,就來(lái)看看這篇文章吧。
我們先使用構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象:
function Person() { } var person = new Person(); person.name = "Kevin"; console.log(person.name) // Kevin
在這個(gè)例子中,Person 就是一個(gè)構(gòu)造函數(shù),我們使用 new 創(chuàng)建了一個(gè)實(shí)例對(duì)象 person。
很簡(jiǎn)單吧,接下來(lái)進(jìn)入正題:
prototype每個(gè)函數(shù)都有一個(gè) prototype 屬性,就是我們經(jīng)常在各種例子中看到的那個(gè) prototype ,比如:
function Person() { } // 雖然寫(xiě)在注釋里,但是你要注意: // prototype是函數(shù)才會(huì)有的屬性 Person.prototype.name = "Kevin"; var person1 = new Person(); var person2 = new Person(); console.log(person1.name) // Kevin console.log(person2.name) // Kevin
那這個(gè)函數(shù)的 prototype 屬性到底指向的是什么呢?是這個(gè)函數(shù)的原型嗎?
其實(shí),函數(shù)的 prototype 屬性指向了一個(gè)對(duì)象,這個(gè)對(duì)象正是調(diào)用該構(gòu)造函數(shù)而創(chuàng)建的實(shí)例的原型,也就是這個(gè)例子中的 person1 和 person2 的原型。
那什么是原型呢?你可以這樣理解:每一個(gè)JavaScript對(duì)象(null除外)在創(chuàng)建的時(shí)候就會(huì)與之關(guān)聯(lián)另一個(gè)對(duì)象,這個(gè)對(duì)象就是我們所說(shuō)的原型,每一個(gè)對(duì)象都會(huì)從原型"繼承"屬性。
讓我們用一張圖表示構(gòu)造函數(shù)和實(shí)例原型之間的關(guān)系:
在這張圖中我們用 Object.prototype 表示實(shí)例原型。
那么我們?cè)撛趺幢硎緦?shí)例與實(shí)例原型,也就是 person 和 Person.prototype 之間的關(guān)系呢,這時(shí)候我們就要講到第二個(gè)屬性:
__proto__這是每一個(gè)JavaScript對(duì)象(除了 null )都具有的一個(gè)屬性,叫__proto__,這個(gè)屬性會(huì)指向該對(duì)象的原型。
為了證明這一點(diǎn),我們可以在火狐或者谷歌中輸入:
function Person() { } var person = new Person(); console.log(person.__proto__ === Person.prototype); // true
于是我們更新下關(guān)系圖:
既然實(shí)例對(duì)象和構(gòu)造函數(shù)都可以指向原型,那么原型是否有屬性指向構(gòu)造函數(shù)或者實(shí)例呢?
constructor指向?qū)嵗故菦](méi)有,因?yàn)橐粋€(gè)構(gòu)造函數(shù)可以生成多個(gè)實(shí)例,但是原型指向構(gòu)造函數(shù)倒是有的,這就要講到第三個(gè)屬性:constructor?,每個(gè)原型都有一個(gè) constructor 屬性指向關(guān)聯(lián)的構(gòu)造函數(shù)。
為了驗(yàn)證這一點(diǎn),我們可以嘗試:
function Person() { } console.log(Person === Person.prototype.constructor); // true
所以再更新下關(guān)系圖:
綜上我們已經(jīng)得出:
function Person() { } var person = new Person(); console.log(person.__proto__ == Person.prototype) // true console.log(Person.prototype.constructor == Person) // true // 順便學(xué)習(xí)一個(gè)ES5的方法,可以獲得對(duì)象的原型 console.log(Object.getPrototypeOf(person) === Person.prototype) // true
了解了構(gòu)造函數(shù)、實(shí)例原型、和實(shí)例之間的關(guān)系,接下來(lái)我們講講實(shí)例和原型的關(guān)系:
實(shí)例與原型當(dāng)讀取實(shí)例的屬性時(shí),如果找不到,就會(huì)查找與對(duì)象關(guān)聯(lián)的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層為止。
舉個(gè)例子:
function Person() { } Person.prototype.name = "Kevin"; var person = new Person(); person.name = "Daisy"; console.log(person.name) // Daisy delete person.name; console.log(person.name) // Kevin
在這個(gè)例子中,我們給實(shí)例對(duì)象 person 添加了 name 屬性,當(dāng)我們打印 person.name 的時(shí)候,結(jié)果自然為 Daisy。
但是當(dāng)我們刪除了 person 的 name 屬性時(shí),讀取 person.name,從 person 對(duì)象中找不到 name 屬性就會(huì)從 person 的原型也就是 person.__proto__ ,也就是 Person.prototype中查找,幸運(yùn)的是我們找到了 name 屬性,結(jié)果為 Kevin。
但是萬(wàn)一還沒(méi)有找到呢?原型的原型又是什么呢?
原型的原型在前面,我們已經(jīng)講了原型也是一個(gè)對(duì)象,既然是對(duì)象,我們就可以用最原始的方式創(chuàng)建它,那就是:
var obj = new Object(); obj.name = "Kevin" console.log(obj.name) // Kevin
所以原型對(duì)象是通過(guò) Object 構(gòu)造函數(shù)生成的,結(jié)合之前所講,實(shí)例的 __proto__ 指向構(gòu)造函數(shù)的 prototype ,所以我們?cè)俑孪玛P(guān)系圖:
原型鏈那 Object.prototype 的原型呢?
null,不信我們可以打?。?/p>
console.log(Object.prototype.__proto__ === null) // true
所以查到屬性的時(shí)候查到 Object.prototype 就可以停止查找了。
所以最后一張關(guān)系圖就是
順便還要說(shuō)一下,圖中由相互關(guān)聯(lián)的原型組成的鏈狀結(jié)構(gòu)就是原型鏈,也就是藍(lán)色的這條線。
補(bǔ)充最后,補(bǔ)充三點(diǎn)大家可能不會(huì)注意的地方:
constructor首先是 constructor 屬性,我們看個(gè)例子:
function Person() { } var person = new Person(); console.log(person.constructor === Person); // true
當(dāng)獲取 person.constructor 時(shí),其實(shí) person 中并沒(méi)有 constructor 屬性,當(dāng)不能讀取到constructor 屬性時(shí),會(huì)從 person 的原型也就是 Person.prototype 中讀取,正好原型中有該屬性,所以:
person.constructor === Person.prototype.constructor__proto__
其次是 __proto__ ,絕大部分瀏覽器都支持這個(gè)非標(biāo)準(zhǔn)的方法訪問(wèn)原型,然而它并不存在于 Person.prototype 中,實(shí)際上,它是來(lái)自于 Object.prototype ,與其說(shuō)是一個(gè)屬性,不如說(shuō)是一個(gè) getter/setter,當(dāng)使用 obj.__proto__ 時(shí),可以理解成返回了 Object.getPrototypeOf(obj)。
真的是繼承嗎?最后是關(guān)于繼承,前面我們講到“每一個(gè)對(duì)象都會(huì)從原型‘繼承’屬性”,實(shí)際上,繼承是一個(gè)十分具有迷惑性的說(shuō)法,引用《你不知道的JavaScript》中的話,就是:
繼承意味著復(fù)制操作,然而 JavaScript 默認(rèn)并不會(huì)復(fù)制對(duì)象的屬性,相反,JavaScript 只是在兩個(gè)對(duì)象之間創(chuàng)建一個(gè)關(guān)聯(lián),這樣,一個(gè)對(duì)象就可以通過(guò)委托訪問(wèn)另一個(gè)對(duì)象的屬性和函數(shù),所以與其叫繼承,委托的說(shuō)法反而更準(zhǔn)確些。
下一篇文章JavaScript深入之詞法作用域和動(dòng)態(tài)作用域
深入系列JavaScript深入系列目錄地址:https://github.com/mqyqingfeng/Blog。
JavaScript深入系列預(yù)計(jì)寫(xiě)十五篇左右,旨在幫大家捋順JavaScript底層知識(shí),重點(diǎn)講解如原型、作用域、執(zhí)行上下文、變量對(duì)象、this、閉包、按值傳遞、call、apply、bind、new、繼承等難點(diǎn)概念。
如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,?qǐng)務(wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎star,對(duì)作者也是一種鼓勵(lì)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88213.html
摘要:本文詳情構(gòu)造函數(shù)創(chuàng)建對(duì)象先使用構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象在這個(gè)例子中,就是一個(gè)構(gòu)造函數(shù),我們使用創(chuàng)建了一個(gè)實(shí)例對(duì)象。 本文詳情:https://github.com/mqyqingfen... 構(gòu)造函數(shù)創(chuàng)建對(duì)象先使用構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象 function Person(){ } var person = new Person(); person.name = Mit; console.lo...
摘要:深入系列第十二篇,通過(guò)的模擬實(shí)現(xiàn),帶大家揭開(kāi)使用獲得構(gòu)造函數(shù)實(shí)例的真相一句話介紹運(yùn)算符創(chuàng)建一個(gè)用戶定義的對(duì)象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對(duì)象類型之一也許有點(diǎn)難懂,我們?cè)谀M之前,先看看實(shí)現(xiàn)了哪些功能。 JavaScript深入系列第十二篇,通過(guò)new的模擬實(shí)現(xiàn),帶大家揭開(kāi)使用new獲得構(gòu)造函數(shù)實(shí)例的真相 new 一句話介紹 new: new 運(yùn)算符創(chuàng)建一個(gè)用戶定義的對(duì)象類型的實(shí)例或具...
摘要:也就是說(shuō)當(dāng)返回的函數(shù)作為構(gòu)造函數(shù)的時(shí)候,時(shí)指定的值會(huì)失效,但傳入的參數(shù)依然生效。構(gòu)造函數(shù)效果的優(yōu)化實(shí)現(xiàn)但是在這個(gè)寫(xiě)法中,我們直接將,我們直接修改的時(shí)候,也會(huì)直接修改函數(shù)的。 JavaScript深入系列第十一篇,通過(guò)bind函數(shù)的模擬實(shí)現(xiàn),帶大家真正了解bind的特性 bind 一句話介紹 bind: bind() 方法會(huì)創(chuàng)建一個(gè)新函數(shù)。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),bind() 的第一個(gè)參數(shù)...
摘要:寫(xiě)在前面深入系列共計(jì)篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順底層知識(shí)的系列。深入系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫(xiě)在前面 JavaScript 深入系列共計(jì) 15 篇已經(jīng)正式完結(jié),這是一個(gè)旨在幫助大家,其實(shí)也是幫助自己捋順 JavaScript 底層知識(shí)的系列。重點(diǎn)講解了如原型、作用域、執(zhí)行上下文、變量對(duì)象、this、...
摘要:由于屬性是可以變更的,所以未必真的指向?qū)ο蟮臉?gòu)造函數(shù),只是一個(gè)提示。不過(guò),從編程習(xí)慣上,我們應(yīng)該盡量讓對(duì)象的指向其構(gòu)造函數(shù),以維持這個(gè)慣例。 問(wèn)題引入 最近看了許多關(guān)于js繼承實(shí)現(xiàn)的相關(guān)文章,許多實(shí)現(xiàn)方式中都會(huì)存在這么一行代碼: A.prototype.constructor = A 于是感到好奇,這行代碼的實(shí)際意義是什么?如果沒(méi)有的話,還能達(dá)到繼承的目的嗎? 前置知識(shí) 為了熟悉jav...
閱讀 1784·2021-11-25 09:43
閱讀 1820·2021-11-24 10:41
閱讀 3130·2021-09-27 13:36
閱讀 836·2019-08-30 15:53
閱讀 3601·2019-08-30 15:44
閱讀 891·2019-08-30 14:03
閱讀 2604·2019-08-29 16:38
閱讀 1020·2019-08-29 13:23