詳解Javascript中prototype屬性(推薦)
在典型的面向?qū)ο蟮恼Z(yǔ)言中,如java,都存在類(class)的概念,類就是對(duì)象的模板,對(duì)象就是類的實(shí)例。但是在Javascript語(yǔ)言體系中,是不存在類(Class)的概念的,javascript中不是基于‘類的',而是通過(guò)構(gòu)造函數(shù)(constructor)和原型鏈(prototype chains)實(shí)現(xiàn)的。但是在ES6中提供了更接近傳統(tǒng)語(yǔ)言的寫(xiě)法,引入了Class(類)這個(gè)概念,作為對(duì)象的模板。通過(guò)class關(guān)鍵字,可以定義類。基本上,ES6的class可以看作只是一個(gè)語(yǔ)法糖,它的絕大部分功能,ES5都可以做到,新的class寫(xiě)法只是讓原型對(duì)象的寫(xiě)法更加清晰、更像面向?qū)ο缶幊痰恼Z(yǔ)法而已。
按照我的習(xí)慣,在寫(xiě)文章前我會(huì)給出文章目錄。
以下內(nèi)容會(huì)分為如下小節(jié):
1.構(gòu)造函數(shù)的簡(jiǎn)單介紹
2.構(gòu)造函數(shù)的缺點(diǎn)
3.prototype屬性的作用
4.原型鏈(prototype chain)
5.constructor屬性
5.1:constructor屬性的作用
6.instanceof運(yùn)算符
1.構(gòu)造函數(shù)的簡(jiǎn)單介紹
在我的一篇Javascript 中構(gòu)造函數(shù)與new命令的密切關(guān)系文章中,詳細(xì)了介紹了構(gòu)造函數(shù)的概念和特點(diǎn),new命令的原理和用法等,如果對(duì)于構(gòu)造函數(shù)不熟悉的同學(xué),可以前往細(xì)細(xì)品味。以下做一個(gè)簡(jiǎn)單的回顧。
所謂構(gòu)造函數(shù),就是提供了一個(gè)生成對(duì)象的模板并描述對(duì)象的基本結(jié)構(gòu)的函數(shù)。一個(gè)構(gòu)造函數(shù),可以生成多個(gè)對(duì)象,每個(gè)對(duì)象都有相同的結(jié)構(gòu)??偟膩?lái)說(shuō),構(gòu)造函數(shù)就是對(duì)象的模板,對(duì)象就是構(gòu)造函數(shù)的實(shí)例。
構(gòu)造函數(shù)的特點(diǎn)有:
a:構(gòu)造函數(shù)的函數(shù)名首字母必須大寫(xiě)。
b:內(nèi)部使用this對(duì)象,來(lái)指向?qū)⒁傻膶?duì)象實(shí)例。
c:使用new操作符來(lái)調(diào)用構(gòu)造函數(shù),并返回對(duì)象實(shí)例。
看一個(gè)最簡(jiǎn)單的一個(gè)例子。
function Person(){ this.name = 'keith'; } var boy = new Person(); console.log(boy.name); //'keith'
2.構(gòu)造函數(shù)的缺點(diǎn)
所有的實(shí)例對(duì)象都可以繼承構(gòu)造函數(shù)中的屬性和方法。但是,同一個(gè)對(duì)象實(shí)例之間,無(wú)法共享屬性。
function Person(name,height){ this.name=name; this.height=height; this.hobby=function(){ return 'watching movies'; } } var boy=new Person('keith',180); var girl=new Person('rascal',153); console.log(boy.name); //'keith' console.log(girl.name); //'rascal' console.log(boy.hobby===girl.hobby); //false
上面代碼中,一個(gè)構(gòu)造函數(shù)Person生成了兩個(gè)對(duì)象實(shí)例boy和girl,并且有兩個(gè)屬性和一個(gè)方法。但是,它們的hobby方法是不一樣的。也就是說(shuō),每當(dāng)你使用new來(lái)調(diào)用構(gòu)造函數(shù)放回一個(gè)對(duì)象實(shí)例的時(shí)候,都會(huì)創(chuàng)建一個(gè)hobby方法。這既沒(méi)有必要,又浪費(fèi)資源,因?yàn)樗衕obby方法都是童顏的行為,完全可以被兩個(gè)對(duì)象實(shí)例共享。
所以,構(gòu)造函數(shù)的缺點(diǎn)就是:同一個(gè)構(gòu)造函數(shù)的對(duì)象實(shí)例之間無(wú)法共享屬性或方法。
3.prototype屬性的作用
為了解決構(gòu)造函數(shù)的對(duì)象實(shí)例之間無(wú)法共享屬性的缺點(diǎn),js提供了prototype屬性。
js中每個(gè)數(shù)據(jù)類型都是對(duì)象(除了null和undefined),而每個(gè)對(duì)象都繼承自另外一個(gè)對(duì)象,后者稱為“原型”(prototype)對(duì)象,只有null除外,它沒(méi)有自己的原型對(duì)象。
原型對(duì)象上的所有屬性和方法,都會(huì)被對(duì)象實(shí)例所共享。
通過(guò)構(gòu)造函數(shù)生成對(duì)象實(shí)例時(shí),會(huì)將對(duì)象實(shí)例的原型指向構(gòu)造函數(shù)的prototype屬性。每一個(gè)構(gòu)造函數(shù)都有一個(gè)prototype屬性,這個(gè)屬性就是對(duì)象實(shí)例的原型對(duì)象。
function Person(name,height){ this.name=name; this.height=height; } Person.prototype.hobby=function(){ return 'watching movies'; } var boy=new Person('keith',180); var girl=new Person('rascal',153); console.log(boy.name); //'keith' console.log(girl.name); //'rascal' console.log(boy.hobby===girl.hobby); //true
上面代碼中,如果將hobby方法放在原型對(duì)象上,那么兩個(gè)實(shí)例對(duì)象都共享著同一個(gè)方法。我希望大家都能理解的是,對(duì)于構(gòu)造函數(shù)來(lái)說(shuō),prototype是作為構(gòu)造函數(shù)的屬性;對(duì)于對(duì)象實(shí)例來(lái)說(shuō),prototype是對(duì)象實(shí)例的原型對(duì)象。所以prototype即是屬性,又是對(duì)象。
原型對(duì)象的屬性不是對(duì)象實(shí)例的屬性。對(duì)象實(shí)例的屬性是繼承自構(gòu)造函數(shù)定義的屬性,因?yàn)闃?gòu)造函數(shù)內(nèi)部有一個(gè)this關(guān)鍵字來(lái)指向?qū)⒁傻膶?duì)象實(shí)例。對(duì)象實(shí)例的屬性,其實(shí)就是構(gòu)造函數(shù)內(nèi)部定義的屬性。只要修改原型對(duì)象上的屬性和方法,變動(dòng)就會(huì)立刻體現(xiàn)在所有對(duì)象實(shí)例上。
Person.prototype.hobby=function(){ return 'swimming'; } console.log(boy.hobby===girl.hobby); //true console.log(boy.hobby()); //'swimming' console.log(girl.hobby()); //'swimming'
上面代碼中,當(dāng)修改了原型對(duì)象的hobby方法之后,兩個(gè)對(duì)象實(shí)例都發(fā)生了變化。這是因?yàn)閷?duì)象實(shí)例其實(shí)是沒(méi)有hobby方法,都是讀取原型對(duì)象的hobby方法。也就是說(shuō),當(dāng)某個(gè)對(duì)象實(shí)例沒(méi)有該屬性和方法時(shí),就會(huì)到原型對(duì)象上去查找。如果實(shí)例對(duì)象自身有某個(gè)屬性或方法,就不會(huì)去原型對(duì)象上查找。
boy.hobby=function(){ return 'play basketball'; } console.log(boy.hobby()); //'play basketball' console.log(girl.hobby()); //'swimming'
上面代碼中,boy對(duì)象實(shí)例的hobby方法修改時(shí),就不會(huì)在繼承原型對(duì)象上的hobby方法了。不過(guò)girl仍然會(huì)繼承原型對(duì)象的方法。
總結(jié)一下:
a:原型對(duì)象的作用,就是定義所有對(duì)象實(shí)例所共享的屬性和方法。
b:prototype,對(duì)于構(gòu)造函數(shù)來(lái)說(shuō),它是一個(gè)屬性;對(duì)于對(duì)象實(shí)例來(lái)說(shuō),它是一個(gè)原型對(duì)象。
4.原型鏈(prototype chains)
對(duì)象的屬性和方法,有可能是定義在自身,也有可能是定義在它的原型對(duì)象。由于原型對(duì)象本身對(duì)于對(duì)象實(shí)例來(lái)說(shuō)也是對(duì)象,它也有自己的原型,所以形成了一條原型鏈(prototype chain)。比如,a對(duì)象是b對(duì)象的原型,b對(duì)象是c對(duì)象的原型,以此類推。所有一切的對(duì)象的原型頂端,都是Object.prototype,即Object構(gòu)造函數(shù)的prototype屬性指向的那個(gè)對(duì)象。
當(dāng)然,Object.prototype對(duì)象也有自己的原型對(duì)象,那就是沒(méi)有任何屬性和方法的null對(duì)象,而null對(duì)象沒(méi)有自己的原型。
1 console.log(Object.getPrototypeOf(Object.prototype)); //null
2 console.log(Person.prototype.isPrototypeOf(boy)) //true
原型鏈(prototype chain)的特點(diǎn)有:
a:讀取對(duì)象的某個(gè)屬性時(shí),JavaScript引擎先尋找對(duì)象本身的屬性,如果找不到,就到它的原型去找,如果還是找不到,就到原型的原型去找。如果直到最頂層的Object.prototype還是找不到,則返回undefined。
b:如果對(duì)象自身和它的原型,都定義了一個(gè)同名屬性,那么優(yōu)先讀取對(duì)象自身的屬性,這叫做“覆蓋”(overiding)。
c:一級(jí)級(jí)向上在原型鏈尋找某個(gè)屬性,對(duì)性能是有影響的。所尋找的屬性在越上層的原型對(duì)象,對(duì)性能的影響越大。如果尋找某個(gè)不存在的屬性,將會(huì)遍歷整個(gè)原型鏈。
看概念可能比較晦澀,我們來(lái)看一個(gè)例子。但是理解了概念真的很重要。
var arr=[1,2,3]; console.log(arr.length); //3 console.log(arr.valueOf()) //[1,2,3] console.log(arr.join('|')) //1|2|3
上面代碼中,定了一個(gè)數(shù)組arr,數(shù)組里面有三個(gè)元素。我們并沒(méi)有給數(shù)組添加任何屬性和方法,可是卻在調(diào)用length,join(),valueOf()時(shí),卻不會(huì)報(bào)錯(cuò)。
length屬性是繼承自Array.prototype的,屬于原型對(duì)象上的一個(gè)屬性。join方法也是繼承自Array.prototype的,屬于原型對(duì)象上的一個(gè)方法。這兩個(gè)方法是所有數(shù)組所共享的。當(dāng)實(shí)例對(duì)象上沒(méi)有這個(gè)length屬性時(shí),就會(huì)去原型對(duì)象查找。
valueOf方法是繼承自O(shè)bject.prototype的。首先,arr數(shù)組是沒(méi)有valueOf方法的,所以就到原型對(duì)象Array.prototype查找。然后,發(fā)現(xiàn)Array.prototype對(duì)象上沒(méi)有valueOf方法。最后,再到它的原型對(duì)象Object.prototype查找。
來(lái)看看Array.prototype對(duì)象和Object.prototype對(duì)象分別有什么屬性和方法。
console.log(Object.getOwnPropertyNames(Array.prototype)) //["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push", "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf", "forEach", "map", "filter", "reduce", "reduceRight", "some", "every", "find", "findIndex", "copyWithin", "fill", "entries", "keys", "values", "includes", "constructor", "$set", "$remove"] console.log(Object.getOwnPropertyNames(Object.prototype)) // ["toSource", "toString", "toLocaleString", "valueOf", "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__", "constructor"]
我相信,大家看到這里,對(duì)prototype還是似懂非懂的。這很正常,畢竟是js中比較重要又比較抽象的概念,不可能那么快就掌握,再啃多幾篇,說(shuō)不定掌握其精髓。在某乎上,有一個(gè)活生生的實(shí)例,可能也是大家會(huì)遇到的問(wèn)題。可以看看 js構(gòu)造函數(shù)和原型對(duì)象。
5.constructor屬性
prototype對(duì)象有一個(gè)constructor屬性,默認(rèn)指向prototype對(duì)象所在的構(gòu)造函數(shù)。
function A(){}; console.log(A.prototype.constructor===A) //true
要注意的是,prototype是構(gòu)造函數(shù)的屬性,而constructor則是構(gòu)造函數(shù)的prototype屬性所指向的那個(gè)對(duì)象,也就是原型對(duì)象的屬性。注意不要混淆。
console.log(A.hasOwnProperty('prototype')); //true console.log(A.prototype.hasOwnProperty('constructor')); //true
由于constructor屬性是定義在原型(prototype)對(duì)象上面,意味著可以被所有實(shí)例對(duì)象繼承。
function A(){}; var a=new A(); console.log(a.constructor); //A() console.log(a.constructor===A.prototype.constructor);//true
上面代碼中,a是構(gòu)造函數(shù)A的實(shí)例對(duì)象,但是a自身沒(méi)有contructor屬性,該屬性其實(shí)是讀取原型鏈上面的
A.prototype.constructor屬性。
5.1:constructor屬性的作用
a:分辨原型對(duì)象到底屬于哪個(gè)構(gòu)造函數(shù)
function A(){}; var a=new A(); console.log(a.constructor===A) //true console.log(a.constructor===Array) //false
上面代碼表示,使用constructor屬性,確定實(shí)例對(duì)象a的構(gòu)造函數(shù)是A,而不是Array。
b:從實(shí)例新建另一個(gè)實(shí)例
function A() {}; var a = new A(); var b = new a.constructor(); console.log(b instanceof A); //true
上面代碼中,a是構(gòu)造函數(shù)A的實(shí)例對(duì)象,可以從a.constructor間接調(diào)用構(gòu)造函數(shù)。
c:調(diào)用自身的構(gòu)造函數(shù)成為可能
A.prototype.hello = function() { return new this.constructor(); }
d:提供了一種從構(gòu)造函數(shù)繼承另外一種構(gòu)造函數(shù)的模式
function Father() {} function Son() { Son.height.constructor.call(this); } Son.height = new Father();
上面代碼中,F(xiàn)ather和Son都是構(gòu)造函數(shù),在Son內(nèi)部的this上調(diào)用Father,就會(huì)形成Son繼承Father的效果。
e:由于constructor屬性是一種原型對(duì)象和構(gòu)造函數(shù)的關(guān)系,所以在修改原型對(duì)象的時(shí)候,一定要注意constructor的指向問(wèn)題。
解決方法有兩種,要么將constructor屬性指向原來(lái)的構(gòu)造函數(shù),要么只在原型對(duì)象上添加屬性和方法,避免instanceof失真。
6.instanceof運(yùn)算符
instanceof運(yùn)算符返回一個(gè)布爾值,表示指定對(duì)象是否為某個(gè)構(gòu)造函數(shù)的實(shí)例。
function A() {}; var a = new A(); console.log(a instanceof A); //true
因?yàn)閕nstanceof對(duì)整個(gè)原型鏈上的對(duì)象都有效,所以同一個(gè)實(shí)例對(duì)象,可能會(huì)對(duì)多個(gè)構(gòu)造函數(shù)都返回true。
function A() {}; var a = new A(); console.log(a instanceof A); //true console.log(a instanceof Object); //true
注意,instanceof對(duì)象只能用于復(fù)雜數(shù)據(jù)類型(數(shù)組,對(duì)象等),不能用于簡(jiǎn)單數(shù)據(jù)類型(布爾值,數(shù)字,字符串等)。
var x = [1]; var o = {}; var b = true; var c = 'string'; console.log(x instanceof Array); //true console.log(o instanceof Object); //true console.log(b instanceof Boolean); //false console.log(c instanceof String); //false
此外,null和undefined都不是對(duì)象,所以instanceof 總是返回false。
console.log(null instanceof Object); //false console.log(undefined instanceof Object); //false
利用instanceof運(yùn)算符,還可以巧妙地解決,調(diào)用構(gòu)造函數(shù)時(shí),忘了加new命令的問(wèn)題。
function Keith(name,height) { if (! this instanceof Keith) { return new Keith(name,height); } this.name = name; this.height = height; }
上面代碼中,使用了instanceof運(yùn)算符來(lái)判斷函數(shù)體內(nèi)的this關(guān)鍵字是否指向構(gòu)造函數(shù)Keith的實(shí)例,如果不是,就表明忘記加new命令,此時(shí)構(gòu)造函數(shù)會(huì)返回一個(gè)對(duì)象實(shí)例,避免出現(xiàn)意想不到的結(jié)果。
因?yàn)橄抻谄脑?,暫時(shí)介紹到這里。
我會(huì)在下次的分享中談?wù)勗停╬rototype)對(duì)象的一些原生方法,如Object.getPrototypeOf(),Object.setPrototypeOf()等,并且介紹獲取原生對(duì)象方法的比較。
以上所述是小編給大家介紹的詳解Javascript中prototype屬性(推薦)的相關(guān)知識(shí),希望對(duì)大家有所幫助。
- js中繼承的幾種用法總結(jié)(apply,call,prototype)
- js中的hasOwnProperty和isPrototypeOf方法使用實(shí)例
- JavaScript prototype 使用介紹
- js中prototype用法詳細(xì)介紹
- Javascript中Array.prototype.map()詳解
- JQuery,Extjs,YUI,Prototype,Dojo 等JS框架的區(qū)別和應(yīng)用場(chǎng)景簡(jiǎn)述
- 判斷js中各種數(shù)據(jù)的類型方法之typeof與0bject.prototype.toString講解
- JavaScript中的prototype使用說(shuō)明
- JavaScript函數(shù)及其prototype詳解
相關(guān)文章
js對(duì)象合并與數(shù)組合并綜合應(yīng)用舉例
這篇文章主要給大家介紹了關(guān)于js對(duì)象合并與數(shù)組合并綜合應(yīng)用舉例的相關(guān)資料,本文將介紹常見(jiàn)的JS對(duì)象合并和數(shù)組合并方法,幫助讀者更好地理解和運(yùn)用這些方法,需要的朋友可以參考下2023-11-11JS+CSS實(shí)現(xiàn)的藍(lán)色table選項(xiàng)卡效果
這篇文章主要介紹了JS+CSS實(shí)現(xiàn)的藍(lán)色table選項(xiàng)卡效果,通過(guò)鼠標(biāo)事件調(diào)用自定義函數(shù)實(shí)現(xiàn)頁(yè)面元素樣式的遍歷與動(dòng)態(tài)切換效果,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10BootStrap 實(shí)現(xiàn)各種樣式的進(jìn)度條效果
這篇文章主要介紹了BootStrap 實(shí)現(xiàn)各種樣式的進(jìn)度條效果,代碼分為動(dòng)態(tài)和疊加兩種效果,需要的朋友可以參考下2016-12-12微信小程序如何使用Promise對(duì)wx.request()封裝詳解(附完整代碼)
微信小程序的wx.request是微信小程序最早生成的數(shù)據(jù)庫(kù)傳輸模式,數(shù)據(jù)傳輸簡(jiǎn)單明確,下面這篇文章主要給大家介紹了關(guān)于微信小程序如何使用Promise對(duì)wx.request()封裝的相關(guān)資料,需要的朋友可以參考下2023-03-03get(0).tagName獲得作用標(biāo)簽示例代碼
get(0).tagName可獲得作用標(biāo)簽,下面是它的一個(gè)小應(yīng)用,在學(xué)習(xí)js的朋友可以參考下2014-10-10一文掌握J(rèn)avaScript數(shù)組常用工具函數(shù)總結(jié)
這篇文章主要介紹了一文掌握J(rèn)avaScript數(shù)組常用工具函數(shù)總結(jié),文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值2022-06-06基于javascript實(shí)現(xiàn)圖片滑動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了基于javascript實(shí)現(xiàn)圖片滑動(dòng)效果的相關(guān)資料,具有一定的參考價(jià)值,感興趣的朋友可以參考一下2016-05-05BootStrap使用file-input插件上傳圖片的方法
這篇文章主要介紹了BootStrap使用file-input插件上傳圖片的方法,bootstrap的圖片上傳框架 file-input 插件非常不錯(cuò),下面小編通過(guò)本文介紹下這個(gè)插件的使用方法,感興趣的朋友一起看看吧2016-09-09