只有對(duì)象,沒(méi)有類(lèi);對(duì)象繼承對(duì)象,而不是類(lèi)繼承類(lèi)。
成都創(chuàng)新互聯(lián)是一家專(zhuān)注網(wǎng)站建設(shè)、網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃、微信小程序定制開(kāi)發(fā)、電子商務(wù)建設(shè)、網(wǎng)絡(luò)推廣、移動(dòng)互聯(lián)開(kāi)發(fā)、研究、服務(wù)為一體的技術(shù)型公司。公司成立十年以來(lái),已經(jīng)為上1000+成都軟裝設(shè)計(jì)各業(yè)的企業(yè)公司提供互聯(lián)網(wǎng)服務(wù)?,F(xiàn)在,服務(wù)的上1000+客戶與我們一路同行,見(jiàn)證我們的成長(zhǎng);未來(lái),我們一起分享成功的喜悅。
“原型對(duì)象”是核心概念。原型對(duì)象是新對(duì)象的模板,它將自身的屬性共享給新對(duì)象。一個(gè)對(duì)象不但可以享有自己創(chuàng)建時(shí)和運(yùn)行時(shí)定義的屬性,而且可以享有原型對(duì)象的屬性。
每一個(gè)對(duì)象都有自己的原型對(duì)象,所有對(duì)象構(gòu)成一個(gè)樹(shù)狀的層級(jí)系統(tǒng)。root節(jié)點(diǎn)的頂層對(duì)象是一個(gè)語(yǔ)言原生的對(duì)象,只有它沒(méi)有原型對(duì)象,其他所有對(duì)象都直接或間接繼承它的屬性。
使用”原型對(duì)象”作為”模板”生成新對(duì)象 :這個(gè)步驟是必要的,這是每個(gè)對(duì)象出生的唯一方式。以原型為模板創(chuàng)建對(duì)象,這也是”原型”(prototype)的原意。
初始化內(nèi)部屬性 :這一步驟不是必要的。通俗點(diǎn)說(shuō),就是,對(duì)”復(fù)制品”不滿意,我們可以”再加工”,使之獲得不同于”模板”的”個(gè)性”。
所以在JavaScript的世界里,萬(wàn)物皆對(duì)象這個(gè)概念從一而終。
全局對(duì)象:一般全局對(duì)象會(huì)有兩個(gè),一個(gè)是ecma提供的Global對(duì)象,一個(gè)是宿主提供。如在瀏覽器中是window、在nodejs中是global。【所以啊,在瀏覽器中全局對(duì)象Global+window】
通常情況下ecma提供的Global對(duì)象對(duì)是不存在的,沒(méi)有具體的對(duì)象
宿主對(duì)象-h(huán)ost object:即由 ECMAScript 實(shí)現(xiàn)的宿主環(huán)境提供的對(duì)象,包含兩大類(lèi),一個(gè)是宿主提供,一個(gè)是自定義類(lèi)對(duì)象,ECMAScript官方未定義的對(duì)象都屬于宿主對(duì)象,所有非本地對(duì)象都是宿主對(duì)象。宿主提供對(duì)象原理--->由宿主框架通過(guò)某種機(jī)制注冊(cè)到ECscript引擎中的對(duì)象,如宿主瀏覽器(以遠(yuǎn)景為參考)會(huì)向ECscript注入window對(duì)象,構(gòu)建其實(shí)現(xiàn)javascript。
內(nèi)置對(duì)象-Build-in object:由 ECMAScript 實(shí)現(xiàn)提供的、獨(dú)立于宿主環(huán)境的所有對(duì)象,在 ECMAScript 程序開(kāi)始執(zhí)行時(shí)出現(xiàn),即在引擎初始化階段就被創(chuàng)建好的對(duì)象。這意味著開(kāi)發(fā)者不必明確實(shí)例化內(nèi)置對(duì)象,它已被實(shí)例化了Global(全局對(duì)象)、Math、JSON
基本包裝類(lèi)型對(duì)象:ECMAScript還提供了3個(gè)特殊的引用類(lèi)型: Boolean、Number、String。這些類(lèi)型與其他內(nèi)置對(duì)象類(lèi)型相似,但同時(shí)具有各自的基本類(lèi)型相應(yīng)的特殊行為。實(shí)際上,每當(dāng)讀取一個(gè)基本類(lèi)型值得時(shí)候,后臺(tái)就會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的基本包裝類(lèi)型的對(duì)象,從而讓我們能夠調(diào)用一些方法來(lái)操作這些數(shù)據(jù)。 包裝類(lèi)型,是一個(gè)專(zhuān)門(mén)封裝原始類(lèi)型的值,并提供對(duì)原始類(lèi)型的值執(zhí)行操作的API對(duì)象
其他內(nèi)置對(duì)象與基本包裝類(lèi)型對(duì)象的區(qū)別?
普通的內(nèi)置對(duì)象與基本包裝類(lèi)型的主要區(qū)別就是對(duì)象的生命期,使用new操作符創(chuàng)建的引用類(lèi)型的實(shí)例,在執(zhí)行流離開(kāi)當(dāng)前作用域之前都一直保存在內(nèi)存中,而自動(dòng)創(chuàng)建的基本包裝類(lèi)型的對(duì)象,則只是存在于一行代碼的執(zhí)行瞬間,然后立即被立即銷(xiāo)毀。這意味著我們不能再運(yùn)行時(shí)為基本包裝類(lèi)型值添加屬性和方法。
var s1="some text"; s1.color="red"; var s2=new String("some text"); s2.color="red"; console.log(s1.color);//undefined console.log(s2.color);//red console.log(s1==s2);//true console.log(s1===s2);//false
在第二行為s1添加一個(gè)color屬性,第三行代碼執(zhí)行時(shí),再次訪問(wèn)s1,結(jié)果s1的color屬性被銷(xiāo)毀了。詳情推薦閱讀《JavaScript內(nèi)置對(duì)象--基本包裝類(lèi)型(Boolean、Number、String)詳解》
原生對(duì)象-native object:也叫內(nèi)部對(duì)象、本地對(duì)象。獨(dú)立于宿主環(huán)境的ECMAScript實(shí)現(xiàn)提供的對(duì)象。與宿主無(wú)關(guān),在javascript(遠(yuǎn)景瀏覽器)、nodejs(node平臺(tái))、jscript(ie瀏覽器)、typescript(微軟平臺(tái))等等中均有這些對(duì)象。簡(jiǎn)單來(lái)說(shuō),本地對(duì)象就是 ECMA-262 定義的類(lèi)(引用類(lèi)型)。
Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError、Global
在運(yùn)行過(guò)程中動(dòng)態(tài)創(chuàng)建的對(duì)象,需要new,以Number為例:
var n1=Number('1'); var n2=1; n2.xxx=2;console.log(n2); //undefined console.log(n1===n2)//false console.log(n1.toString()===n2.toString())//true console.log(n1.__proto__===Number)//false console.log(n2.__proto__===Number)//false console.log(n1.__proto__===n2.__proto__)//true
n1和n2雖然數(shù)值都是1,但n2的類(lèi)型屬于'object',n1則為'number',身為基本類(lèi)型number的n1直接指向了數(shù)字1,而n2指向了一個(gè)地址,這個(gè)地址中存放了數(shù)值1,這就是對(duì)象和基本類(lèi)型的區(qū)別。
但是原型對(duì)象只存在于函數(shù)對(duì)象。也就是本質(zhì)上只要是通過(guò)new Function創(chuàng)建的函數(shù)對(duì)象會(huì)有一個(gè)原型對(duì)象。
而對(duì)于其他非Function的引用類(lèi)型歸根結(jié)底也是通過(guò)new Function創(chuàng)建的。
如上面提到的Array類(lèi)型、Object類(lèi)型。
實(shí)際上,在每個(gè)函數(shù)對(duì)象創(chuàng)建的時(shí)候,都會(huì)自帶一個(gè)prototype的屬性,這個(gè)屬性相當(dāng)于一個(gè)指針,指向他本身的原型對(duì)象,這個(gè)原型對(duì)象里包含著自定義的方法屬性,
function a(){ this.name='xiaoming'; this.sayName=function () { console.log(this.name); } }
在默認(rèn)情況下,a.prototype下會(huì)帶有一個(gè)constructor屬性,這個(gè)屬性指向創(chuàng)建當(dāng)前函數(shù)對(duì)象的構(gòu)造函數(shù),比如這里
constructor指向構(gòu)造函數(shù)a本身也就是說(shuō):a.prototypr.constructor==a //true
另外默認(rèn)還有一個(gè)_proto_屬性,這個(gè)屬性指向由創(chuàng)建這個(gè)函數(shù)對(duì)象的引用類(lèi)型中繼承而來(lái)的屬性和方法。
當(dāng)通過(guò)構(gòu)造函數(shù)實(shí)例化一個(gè)對(duì)象b時(shí),即new a();
首先這個(gè)new出來(lái)的對(duì)象屬于普通對(duì)象,所以沒(méi)有prototype屬性。但他有_proto_這個(gè)屬性,這個(gè)屬性指向創(chuàng)建它的引用類(lèi)型的原型對(duì)象,在這個(gè)例子中指向a.prototype,從而繼承來(lái)自引用類(lèi)型a的屬性和方法。推薦閱讀《JS 的 new 到底是干什么的?》
var 對(duì)象 = new 函數(shù)對(duì)象 這個(gè)聲明形式可以引申出:
函數(shù).__proto__ ===Function.prototype Function.__proto__ === Function.prototype Object.__proto__ === Function.prototype //Objec也是個(gè)函數(shù),函數(shù)都是由Function構(gòu)造出來(lái)的。 Number.__proto__ === Function.prototype 構(gòu)造函數(shù).prototype.__proto__ ===Object.prototype Function.prototype.__proto__ ===Object.prototype Number.prototype.__proto__ ===Object.prototype Object.__proto__ .__proto__ ===null
理解了以上的關(guān)系后,'__proto__'是對(duì)象的屬性、'prototype'是函數(shù)的屬性這句話也就懂了
null是對(duì)象原型鏈的終點(diǎn),其值既有(是一個(gè)對(duì)象)又無(wú)(不引用任何對(duì)象),代表著對(duì)象本源的一種混沌、虛無(wú)的狀態(tài),正與老子《道德經(jīng)》中的“道”,有著同等的意義(心中一萬(wàn)只艸尼瑪奔騰而過(guò),還是寫(xiě)java爽啊)。比如:《undefined與null的區(qū)別》
在JS中,undefined是全局對(duì)象的一個(gè)屬性,它的初始值就是原始數(shù)據(jù)類(lèi)型undefined,并且無(wú)法被配置,也無(wú)法被改變。undefined從字面意思上理解為“未定義”,即表示一個(gè)變量沒(méi)有定義其值。
而null是一個(gè)JS字面量,表示空值,即沒(méi)有對(duì)象。與undefined相比,null被認(rèn)為是“期望一個(gè)對(duì)象,但是不引用任何對(duì)象的值”,而undefined是純粹的“沒(méi)有值”。
// null為對(duì)象原型鏈的終點(diǎn) console.log(Object.getPrototypeOf(Object.prototype)); // null // null是一個(gè)對(duì)象 console.log(typeof null); // object // null 為空 console.log(!null); // true
JS中的所有事物都是對(duì)象,對(duì)象是擁有屬性和方法的數(shù)據(jù)。
為了描述這些事物,JS便有了“原型(prototype)”的概念。
原型模式是js對(duì)繼承的一種實(shí)現(xiàn):使用原型,能復(fù)用代碼,節(jié)省內(nèi)存空間 (java類(lèi)的代碼在內(nèi)存只有一份,然后每個(gè)對(duì)象執(zhí)行方法都是引用類(lèi)的代碼,所有子類(lèi)對(duì)象調(diào)用父類(lèi)方法的時(shí)候,執(zhí)行的代碼都是同一份父類(lèi)的方法代碼。但是JS沒(méi)有類(lèi),屬性和方法都是存在對(duì)象之中,根本沒(méi)有辦法做到j(luò)ava那樣通過(guò)類(lèi)把代碼共享給所有對(duì)象)。
推薦閱讀《深刻理解JavaScript基于原型的面向?qū)ο蟆?/p>
從一張圖看懂原型對(duì)象、構(gòu)造函數(shù)、實(shí)例對(duì)象之間的關(guān)系
prototype:構(gòu)造函數(shù)中的屬性,指向該構(gòu)造函數(shù)的原型對(duì)象。
constructor:原型對(duì)象中的屬性,指向該原型對(duì)象的構(gòu)造函數(shù)
_proto_:實(shí)例中的屬性,指向new這個(gè)實(shí)例的構(gòu)造函數(shù)的原型對(duì)象
在JavaScript 中,每個(gè)對(duì)象都有一個(gè)指向它的原型(prototype)對(duì)象的內(nèi)部鏈接。這個(gè)原型對(duì)象又有自己的原型,直到某個(gè)對(duì)象的原型為 null 為止(也就是不再有原型指向),組成這條鏈的最后一環(huán)。這種一級(jí)一級(jí)的鏈結(jié)構(gòu)就稱(chēng)為原型鏈(prototype chain)。
要清楚原型鏈,首先要弄清楚對(duì)象
普通對(duì)象
最普通的對(duì)象:有__proto__屬性(指向其原型鏈),沒(méi)有prototype屬性。
原型對(duì)象(Person.prototype 原型對(duì)象還有constructor屬性(指向構(gòu)造函數(shù)對(duì)象))
函數(shù)對(duì)象:
凡是通過(guò)new Function()創(chuàng)建的都是函數(shù)對(duì)象。
擁有__proto__、prototype屬性(指向原型對(duì)象)。
JavaScript 對(duì)象是動(dòng)態(tài)的屬性“包”(指其自己的屬性)。JavaScript 對(duì)象有一個(gè)指向一個(gè)原型對(duì)象的鏈。當(dāng)試圖訪問(wèn)一個(gè)對(duì)象的屬性時(shí),它不僅僅在該對(duì)象上搜尋,還會(huì)搜尋該對(duì)象的原型,以及該對(duì)象的原型的原型,依此層層向上搜索,直到找到一個(gè)名字匹配的屬性或到達(dá)原型鏈的末尾。
顯式原型(explicit prototype property )每一個(gè)函數(shù)在創(chuàng)建之后都會(huì)擁有一個(gè)名為prototype的屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象。用來(lái)實(shí)現(xiàn)基于原型的繼承與屬性的共享。
隱式原型 (implicit prototype link) JS中任意對(duì)象都有一個(gè)內(nèi)置屬性__proto__(部分瀏覽器為[[prototype]]),指向創(chuàng)建這個(gè)對(duì)象的函數(shù)(即構(gòu)造函數(shù))constructor的prototype。用來(lái)構(gòu)成原型鏈,同樣用于實(shí)現(xiàn)基于原型的繼承。
當(dāng)我們「讀取」 obj.toString 時(shí),JS 引擎會(huì)做下面的事情:
1. 看看 obj 對(duì)象本身有沒(méi)有 toString 屬性。沒(méi)有就走到下一步。
2. 看看 obj.__proto__ 對(duì)象有沒(méi)有 toString 屬性,發(fā)現(xiàn) obj.__proto__ 有 toString 屬性,于是找到了
3. 如果 obj.__proto__ 沒(méi)有,那么瀏覽器會(huì)繼續(xù)查看 obj.__proto__.__proto__,如果 obj.__proto__.__proto__ 也沒(méi)有,那么瀏覽器會(huì)繼續(xù)查,obj.__proto__.__proto__.proto__
直到找到 toString 或者 __proto__ 為 null(不管你從那個(gè)屬性開(kāi)始,連續(xù)引用__proto__的指針,最后輸出的那個(gè)值就是null)。
上面的過(guò)程,就是「讀」屬性的「搜索過(guò)程」。
而這個(gè)「搜索過(guò)程」,是連著由 __proto__ 組成的鏈子一直走的。
這個(gè)鏈子,就叫做「原型鏈」。
要搞清楚 valueOf / toString / constructor 是怎么來(lái)的,就要用到 console.dir 了。
共享原型鏈(Shared prototype chain)此模式所有子對(duì)象及后代對(duì)象都共享一個(gè)原型(都是通過(guò)b.prototype=a.prototype;這種模式連接的對(duì)象),在這些后代對(duì)象上修改原型,會(huì)影響所以處在同一共享原型鏈上的所有對(duì)象。而且此模式只繼承原型鏈上的屬性和方法,通過(guò)this定義的屬性和方法無(wú)法訪問(wèn)和繼承
那么 obj.toString 和 obj2.toString 其實(shí)是同一個(gè)東西,也就是 obj2.__proto__.toString。
這有什么意義呢?
如果我們改寫(xiě) obj2.__proto__.toString,那么 obj.toString 其實(shí)也會(huì)變!
這樣 obj 和 obj2 就是具有某些相同行為的對(duì)象,這就是意義所在。
如果我們想讓 obj.toString 和 obj2.toString 的行為不同怎么做呢?
直接賦值就好了:
obj.toString = function(){ return '新的 toString 方法' }
每創(chuàng)建一個(gè)函數(shù)都會(huì)有一個(gè)prototype屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象(通過(guò)該構(gòu)造函數(shù)創(chuàng)建實(shí)例對(duì)象的原型對(duì)象)。原型對(duì)象是包含特定類(lèi)型的所有實(shí)例共享的屬性和方法。原型對(duì)象的好處是,可以讓所有實(shí)例對(duì)象共享它所包含的屬性和方法。
原型對(duì)象屬于普通對(duì)象。Function.prototype是個(gè)例外,它是原型對(duì)象,卻又是函數(shù)對(duì)象,作為一個(gè)函數(shù)對(duì)象,它又沒(méi)有prototype屬性。
擁有了描述事物的能力,卻沒(méi)有創(chuàng)造事物的能力,顯然是不完整的,因此需要一個(gè)Object的生成器來(lái)進(jìn)行對(duì)象的生成。
JS將生成器以構(gòu)造函數(shù)constructor來(lái)表示,構(gòu)造函數(shù)是一個(gè)指針,指向了一個(gè)函數(shù)。
函數(shù)(function) 函數(shù)是指一段在一起的、可以做某一件事的程序。構(gòu)造函數(shù)是一種創(chuàng)建對(duì)象時(shí)使用的特殊函數(shù)。
對(duì)象的構(gòu)造函數(shù)function Object同時(shí)也是一個(gè)對(duì)象,因此需要一個(gè)能夠描述該對(duì)象的原型,該原型便是Function.prototype,函數(shù)的原型用來(lái)描述所有的函數(shù)。對(duì)象的構(gòu)造函數(shù)的__proto__指向該原型。
函數(shù)的原型本身也是對(duì)象,因此其__proto__指向了對(duì)象的原型。同樣,該對(duì)象也需要一個(gè)對(duì)應(yīng)的生成器,即其構(gòu)造函數(shù)function Function。
函數(shù)的構(gòu)造函數(shù)是由函數(shù)生成的一個(gè)對(duì)象,所以其原型即為函數(shù)的原型,其隱式原型也同樣為函數(shù)的原型Function.prototype。
instanceof操作符的內(nèi)部實(shí)現(xiàn)機(jī)制和隱式原型、顯式原型有直接的關(guān)系。instanceof的左值一般是一個(gè)對(duì)象,右值一般是一個(gè)構(gòu)造函數(shù),用來(lái)判斷左值是否是右值的實(shí)例。它的實(shí)現(xiàn)原理是沿著左值的__proto__一直尋找到原型鏈的末端,直到其等于右值的prototype為止。
instanceof 的作用是判斷一個(gè)對(duì)象是不是一個(gè)函數(shù)的實(shí)例。比如 obj instanceof fn, 實(shí)際上是判斷fn的prototype是不是在obj的原型鏈上。所以
instanceof運(yùn)算符的實(shí)質(zhì):用來(lái)檢測(cè) constructor.prototype是否存在于參數(shù) object的原型鏈上。
根據(jù)上圖展示的Object和Function的繼承依賴(lài)關(guān)系,我們可以通過(guò)instanceof操作符來(lái)看一下Object和Function的關(guān)系:
console.log(Object instanceof Object); // true console.log(Object instanceof Function); // true console.log(Function instanceof Object); // true console.log(Function instanceof Function); // true
函數(shù)與對(duì)象相互依存,分別定義了事物的描述方法和事物的生成方法,在生成JS萬(wàn)物的過(guò)程中缺一不可。
Function instanceof Function // true, why? Function.prototype是原型對(duì)象,卻是函數(shù)對(duì)象
Object特殊在Object.prototype是憑空出來(lái)的。語(yǔ)法上,所有的{}都會(huì)被解釋為new Object();
Function特殊在__proto__ == prototype。語(yǔ)法上,所有的函數(shù)聲明都會(huì)被解釋為new Function()。
我們來(lái)看Function和Object的特殊之處:
Object是由Function創(chuàng)建的:因?yàn)镺bject.__proto__ === Funciton.prototype;
同理,F(xiàn)unction.prototype是由Object.prototype創(chuàng)建的;
Funciton是由Function自己創(chuàng)建的!
Object.prototype是憑空出來(lái)的!
推薦閱讀 《JavaScript 內(nèi)置對(duì)象與原型鏈結(jié)構(gòu)》與《JavaScript中的難點(diǎn)之原型和原型鏈》
這幾句話能解釋一切關(guān)于原型方面的問(wèn)題:
當(dāng) new 一個(gè)函數(shù)的時(shí)候會(huì)創(chuàng)建一個(gè)對(duì)象,『函數(shù).prototype』 等于 『被創(chuàng)建對(duì)象.__proto__』
一切函數(shù)都是由 Function 這個(gè)函數(shù)創(chuàng)建的,所以『Function.prototype === 被創(chuàng)建的函數(shù).__proto__』
一切函數(shù)的原型對(duì)象都是由 Object 這個(gè)函數(shù)創(chuàng)建的,所以『Object.prototype === 一切函數(shù).prototype.__proto__』
推薦閱讀:《對(duì)原型、原型鏈、 Function、Object 的理解》
先說(shuō)一下繼承,許多OO語(yǔ)言都支持兩張繼承方式:接口繼承、實(shí)現(xiàn)繼承。
|- 接口繼承:只繼承方法簽名
|- 實(shí)現(xiàn)繼承:繼承實(shí)際的方法
由于函數(shù)沒(méi)有簽名,在ECMAScript中無(wú)法實(shí)現(xiàn)接口繼承,只支持實(shí)現(xiàn)繼承,而實(shí)現(xiàn)繼承主要是依靠原型鏈來(lái)實(shí)現(xiàn)。
利用原型讓一個(gè)引用類(lèi)型繼承另一個(gè)引用類(lèi)型的屬性和方法。
每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)想指針(constructor),而實(shí)例對(duì)象都包含一個(gè)指向原型對(duì)象的內(nèi)部指針(__proto__)。如果讓原型對(duì)象等于另一個(gè)類(lèi)型的實(shí)例,此時(shí)的原型對(duì)象將包含一個(gè)指向另一個(gè)原型的指針(__proto__),另一個(gè)原型也包含著一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針(constructor)。假如另一個(gè)原型又是另一個(gè)類(lèi)型的實(shí)例……這就構(gòu)成了實(shí)例與原型的鏈條。
原型鏈基本思路(圖解):
推薦閱讀《JS重點(diǎn)整理之JS原型鏈徹底搞清楚》
類(lèi)(Class)是面向?qū)ο蟪绦蛟O(shè)計(jì)(OOP,Object-Oriented Programming)實(shí)現(xiàn)信息封裝的基礎(chǔ)。類(lèi)是一種用戶定義類(lèi)型,也稱(chēng)類(lèi)類(lèi)型。每個(gè)類(lèi)包含數(shù)據(jù)說(shuō)明和一組操作數(shù)據(jù)或傳遞消息的函數(shù)。類(lèi)的實(shí)例稱(chēng)為對(duì)象。
在ECMAScript 2015 中引入的JS類(lèi)(classes)之前,要在JS中實(shí)現(xiàn)類(lèi)便是采用原型繼承的方式。
當(dāng)把一個(gè)函數(shù)作為構(gòu)造函數(shù),使用new關(guān)鍵字來(lái)創(chuàng)建對(duì)象時(shí),便可以把該函數(shù)看作是一個(gè)類(lèi),創(chuàng)建出來(lái)的對(duì)象則是該類(lèi)的實(shí)例,其隱式原型__proto__指向的是該構(gòu)造函數(shù)的原型。
在訪問(wèn)該對(duì)象的屬性或方法時(shí),JS會(huì)先搜索該對(duì)象中是否定義了該屬性或方法,若沒(méi)有定義,則會(huì)回溯到其__proto__指向的原型對(duì)象去搜索,若仍然未搜索到,則會(huì)繼續(xù)回溯該原型的原型,直到搜索到原型鏈的終點(diǎn)null;
這種特性可以理解為:構(gòu)造函數(shù)生成的實(shí)例,繼承于該構(gòu)造函數(shù)的原型。
得益于這種特性,我們可以使用定義構(gòu)造函數(shù)的方式來(lái)定義類(lèi)。
function Person() {} // 定義Person構(gòu)造函數(shù) // 通常以大寫(xiě)字母開(kāi)頭來(lái)定義類(lèi)名 console.log(new Person() instanceof Person); // true
以上定義了Person類(lèi),該構(gòu)造函數(shù)是由Function構(gòu)造而來(lái),所以其隱式原型指向函數(shù)的原型,而為了描述該事物,同時(shí)生成了該類(lèi)的原型Person.prototype,該原型又是由Object構(gòu)造而來(lái),所以其隱式原型指向了對(duì)象的原型。
后記:文字有點(diǎn)亂,就是多篇文章的精華提煉。發(fā)現(xiàn)把一個(gè)自己懂的事情,深入淺出講明白,并非易事。文有不妥之處,請(qǐng)留言告知,謝謝。
文章首發(fā)于:https://www.zhoulujun.cn/html/webfront/ECMAScript/js/2015_0715_119.html,如果不妥之處,請(qǐng)到官網(wǎng)留言,謝謝!
參考文字:
【道生萬(wàn)物】理解Javascript原型鏈
js高級(jí)---本地對(duì)象、內(nèi)置對(duì)象、宿主對(duì)象
js原型與原型鏈
「每日一題」什么是 JS 原型鏈?
JS理解原型、原型鏈
一張圖弄清Javascript中的原型鏈、prototype、__proto__的關(guān)系
js中的原型、原型鏈、繼承模式
說(shuō)說(shuō)原型(prototype)、原型鏈和原型繼承
淺談JS原型和原型鏈
原型語(yǔ)言解釋
基于類(lèi) vs 基于原型