詳解js中的原型,原型對(duì)象,原型鏈
理解原型
我們創(chuàng)建的每一個(gè)函數(shù)都有一個(gè)prototype(原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,而這個(gè)對(duì)象的用途是包含可以由特定類(lèi)型的所有實(shí)例共享的屬性和方法。看如下例子:
function Person(){ } Person.prototype.name = 'ccc' Person.prototype.age = 18 Person.prototype.sayName = function (){ console.log(this.name); } var person1 = new Person() person1.sayName() // --> ccc var person2 = new Person() person2.sayName() // --> ccc console.log(person1.sayName === person2.sayName) // --> true
理解原型對(duì)象
根據(jù)上面代碼,看下圖:
需要理解三點(diǎn):
- 我們只要?jiǎng)?chuàng)建了一個(gè)新的函數(shù),就會(huì)根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個(gè)prototype屬性,指向函數(shù)的原型對(duì)象。即Person(構(gòu)造函數(shù))有一個(gè)prototype指針,指向Person.prototype
- 默認(rèn)情況下,每個(gè)原型對(duì)象上都會(huì)創(chuàng)建一個(gè)constructor(構(gòu)造函數(shù))屬性,這個(gè)屬性是一個(gè)指向prototype屬性所在函數(shù)的指針
- 每個(gè)實(shí)例的內(nèi)部都有一個(gè)指針(內(nèi)部屬性) ,指向構(gòu)造函數(shù)的原型對(duì)象。即 person1 和person2 身上都有一個(gè)內(nèi)部屬性__proto__(在ECMAscript中管這個(gè)指針叫[[prototype]],雖然在腳本中沒(méi)有標(biāo)準(zhǔn)的方式訪(fǎng)問(wèn)[[prototype]],但是firefox,ie,chrome都支持一個(gè)屬性叫__proto__) 指向Person.prototype
注意:person1 和person2 實(shí)例與構(gòu)造函數(shù)之間沒(méi)有直接的關(guān)系。
在之前我們提到,所有實(shí)現(xiàn)中無(wú)法訪(fǎng)問(wèn)到[[prototype]],那我們?nèi)绾沃缹?shí)例和原型對(duì)象之間是否存在關(guān)系呢?這里可以通過(guò)兩個(gè)方法來(lái)判斷:
- 原型對(duì)線(xiàn)上的方法:isPrototypeOf(),如:console.log(Person.prototype.isPrototypeOf(person1)) // --> true
- ECMAscript5中新增的一個(gè)方法:Object.getPrototypeOf(),這個(gè)方法返回[[prototype]]的值。如:console.log(Object.getPrototypeOf(person1) === Person.prototype) // --> true
實(shí)例屬性與原型屬性的關(guān)系
前面我們提到過(guò),原型最初只包含constructor屬性,而該屬性也是共享的,因此可以通過(guò)對(duì)象實(shí)例訪(fǎng)問(wèn)。雖然可以通過(guò)對(duì)象實(shí)例訪(fǎng)問(wèn)保存在原型中的值,但卻不能通過(guò)對(duì)象實(shí)例重寫(xiě)原型中的值。如果我們?cè)趯?shí)例中添加了一個(gè)屬性,而改屬性與實(shí)例原型中的一個(gè)屬性同名,那就會(huì)在實(shí)例上創(chuàng)建該屬性并屏蔽原型中的那個(gè)屬性。如下:
function Person() {} Person.prototype.name = "ccc"; Person.prototype.age = 18; Person.prototype.sayName = function() { console.log(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = 'www' // 在person1中添加一個(gè)name屬性 person1.sayName() // --> 'www'————'來(lái)自實(shí)例' person2.sayName() // --> 'ccc'————'來(lái)自原型' console.log(person1.hasOwnProperty('name')) // --> true console.log(person2.hasOwnProperty('name')) // --> false delete person1.name // --> 刪除person1中新添加的name屬性 person1.sayName() // -->'ccc'————'來(lái)自原型'
我們?nèi)绾闻袛嘁粋€(gè)屬性,到底是實(shí)例上的屬性還是原型上的屬性?這里可以通過(guò)hasOwnProperty()方法來(lái)檢測(cè)一個(gè)屬性是存在于實(shí)例中還是存在于原型中。(此方法繼承于Object)
下圖詳細(xì)分析了上面例子在不同情況下的實(shí)現(xiàn)與原型的關(guān)系:(省略了Person構(gòu)造函數(shù)的的關(guān)系)
更簡(jiǎn)單的原型語(yǔ)法
我們不可能總像之前的例子一樣,沒(méi)添加一個(gè)屬性和方法就要敲一遍,Person.prototype。為了減少不必要的輸入,更常見(jiàn)的方法是像下面這樣:
function Person(){} Person.prototype ={ name: 'ccc', age: 18, sayName: function () { console.log(this.name) } }
在上面代碼中,我們將Person.prototype設(shè)置為等于一個(gè)以對(duì)象字面量形式創(chuàng)建的新對(duì)象。最終結(jié)果相同,但有一個(gè)例外,constructor屬性不再指向Person了。前面我們介紹過(guò),每創(chuàng)建一個(gè)函數(shù),就會(huì)同時(shí)創(chuàng)建它的prototype對(duì)象,這個(gè)對(duì)象也會(huì)自動(dòng)獲得constructor屬性。但是在我們使用的新語(yǔ)法中,本質(zhì)上完全重寫(xiě)了默認(rèn)的prototype對(duì)象,因此constructor屬性也就變成了新對(duì)象的constructor屬性(指向Object構(gòu)造函數(shù)),不再指向Person函數(shù)了。此時(shí),盡管instanceof操作符還能返回正確的結(jié)果,但通過(guò)constructor已經(jīng)無(wú)法確定對(duì)象的類(lèi)型了。如下:
var person1 = new Person() console.log(person1 instanceof Object) // --> true console.log(person1 instanceof Person) // --> true console.log(person1.constructor === Person) // --> false console.log(person1.constructor === Object) // --> true
這里用instanceof操作符測(cè)試Object和Person仍然返回true,constructor屬性則等于Object,不等于Person了,如果constructor真的很重要可以像下面這樣寫(xiě):
function Person(){} Person.prototype ={ constructor: Person, // --> 重設(shè) name: 'ccc', age: 18, sayName: function () { console.log(this.name) } }
但是這會(huì)引起一個(gè)新問(wèn)題,用上述方式重置constructor屬性會(huì)導(dǎo)致它的[[Enumerable]]特性被設(shè)置為true。而默認(rèn)情況下,原生的constructor屬性是不可枚舉的。因此如果你要使用兼容ECMAscript5的JavaScript引擎,可以試一試Object.defineProperty()。
function Person(){} Person.constructor = { name: 'ccc', age: 18, sayName: function(){ console.log(this.name) } } // 重設(shè)構(gòu)造函數(shù),只適用于ECMAscript5兼容的瀏覽器 Object.defineProperty(Person.constructor, "constructor", { enumerable: false, value: Person })
原型的動(dòng)態(tài)性
由于原型中查找值的過(guò)程是一次搜索,因此我們對(duì)原型對(duì)象所做的任何修改都能立即從實(shí)例上反映出來(lái)。比如:
function Person(){} var person1 = new Person() Person.prototype.sayHi= function(){ console.log('hi') } person1.sayHi()
上述代碼我們先創(chuàng)建了一個(gè)Person實(shí)例,并將其保存在person1中,然后在Person.prototype中添加了sayHi()方法。即使person1是添加新方法之前創(chuàng)建的,但它仍然可以訪(fǎng)問(wèn)這個(gè)方法。原因是實(shí)例與原型之間的松散的連接關(guān)系。
盡管可以隨時(shí)為原型添加屬性和方法,并立即能夠在實(shí)例中反映出來(lái)。但是如果重寫(xiě)整個(gè)原型對(duì)象,那么情況就不一樣了。看如下代碼:
function Person(){} var person1 = new Person() Person.prototype = { name: 'ccc', age: 18, sayName: function(){ console.log(this.name) } } person1.sayName() // --> error
看下圖分析:
調(diào)用構(gòu)造函數(shù)時(shí)為實(shí)例添加了一個(gè)指向最初原型的[[prototype]]指針,而把原型修改為另外一個(gè)對(duì)線(xiàn)更久等于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系。請(qǐng)記住:實(shí)例中的指針僅指向原型,而不指向構(gòu)造函數(shù)。
原型鏈
簡(jiǎn)單的回顧一下構(gòu)造函數(shù)、原型和實(shí)例的關(guān)系:每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針,而實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針。那么假如我們讓原型對(duì)象等于另一個(gè)類(lèi)型的實(shí)例,結(jié)果會(huì)怎樣?顯然,此時(shí)的原型對(duì)象將包含一個(gè)指向另一個(gè)原型的指針,相應(yīng)地,另一個(gè)原型中也包含著一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針。假如另一個(gè)原型又是另一個(gè)類(lèi)型的實(shí)例,那么上述關(guān)系依然成立。如此層層遞進(jìn),就構(gòu)成了實(shí)例與原型的鏈條。這就是所謂的原型鏈的基本概念。
圖中由相互關(guān)聯(lián)的原型組成的鏈狀結(jié)構(gòu)就是原型鏈,也就是藍(lán)色的這條線(xiàn)。
以上就是詳解js中的原型,原型對(duì)象,原型鏈的詳細(xì)內(nèi)容,更多關(guān)于js中的原型,原型對(duì)象,原型鏈的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
D3.js中data(), enter() 和 exit()的問(wèn)題詳解
相信大多數(shù)人對(duì)D3.js并不陌生。這是一個(gè)由紐約時(shí)報(bào)可視化編輯 Mike Bostock與他斯坦福的教授和同學(xué)合作開(kāi)發(fā)的數(shù)據(jù)文件處理的JavaScript Library,全稱(chēng)叫做Data-Driven Documents,在d3.js中data(), enter() 和 exit()比較常見(jiàn),下面給大家就這方面的知識(shí)給大家詳解2015-08-08理解Javascript的caller,callee,call,apply區(qū)別
理解Javascript的caller,callee,call,apply區(qū)別...2007-03-03JS 在數(shù)組插入字符的實(shí)現(xiàn)代碼(可參考JavaScript splice() 方法)
在數(shù)組插入字符,添加數(shù)組,刪除數(shù)組可以用slice自帶的方法。操作比較方便,這個(gè)代碼是作者通過(guò)push與shift方法實(shí)現(xiàn),只能是個(gè)思路,但不推薦這樣的方法。2009-12-12小心!AngularJS結(jié)合RequireJS做文件合并壓縮的那些坑
小心!AngularJS結(jié)合RequireJS做文件合并壓縮的那些坑,大家在做文件合并壓縮的時(shí)候一定要注意,感興趣的朋友可以參考一下2016-01-01JavaScript全屏和退出全屏事件總結(jié)(附代碼)
這篇文章主要介紹了JavaScript全屏和退出全屏事件,先通過(guò)window.ieIsfSceen = false或true進(jìn)行判斷是否為全屏,在進(jìn)行進(jìn)入全屏和退出全屏的操作,需要的朋友可以參考下2017-08-08JavaScript簡(jiǎn)單實(shí)現(xiàn)關(guān)鍵字文本搜索高亮顯示功能示例
這篇文章主要介紹了JavaScript簡(jiǎn)單實(shí)現(xiàn)關(guān)鍵字文本搜索高亮顯示功能,涉及javascript基于事件響應(yīng)的頁(yè)面元素遍歷、分割、替換等相關(guān)操作技巧,需要的朋友可以參考下2018-07-07