淺談javascript中的prototype和__proto__的理解
在工作中有時(shí)候會(huì)看到prototype和__proto__這兩個(gè)屬性,對(duì)這兩個(gè)屬性我一直比較蒙圈,但是我通過(guò)查閱相關(guān)資料,決定做一下總結(jié)加深自己的理解,寫(xiě)得不對(duì)的地方還請(qǐng)各位大神指出。
- 跟__proto__屬性相關(guān)的兩個(gè)方法
- 判斷屬性是存在實(shí)例對(duì)象中,還是存在原型對(duì)象中的方法
- 獲取或遍歷對(duì)象中屬性的幾種方法
1、prototype
每個(gè)函數(shù)都有一個(gè)prototype屬性,該屬性是一個(gè)指針,指向一個(gè)對(duì)象。 而這個(gè)對(duì)象的用途是包含由特定類型的所有實(shí)例共享的屬性和方法。使用這個(gè)對(duì)象的好處就是可以讓所有實(shí)例對(duì)象共享它所擁有的屬性和方法
2、 __proto__
每個(gè)實(shí)例對(duì)象都有一個(gè)__proto__屬性,用于指向構(gòu)造函數(shù)的原型對(duì)象。__proto__屬性是在調(diào)用構(gòu)造函數(shù)創(chuàng)建實(shí)例對(duì)象時(shí)產(chǎn)生的。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ console.log(this.name); }; // 與聲明函數(shù)在邏輯上是等價(jià)的 } var person1=new Person("Nicholas",29,"Software Engineer"); console.log(person1); console.log(Person); console.log(person1.prototype);//undefined console.log(person1.__proto__); console.log(Person.prototype); console.log(person1.__proto__===Person.prototype);//true
輸出結(jié)果如下:
總結(jié):
1、調(diào)用構(gòu)造函數(shù)創(chuàng)建的實(shí)例對(duì)象的prototype屬性為"undefined",構(gòu)造函數(shù)的prototype是一個(gè)對(duì)象。
2、__proto__屬性是在調(diào)用構(gòu)造函數(shù)創(chuàng)建實(shí)例對(duì)象時(shí)產(chǎn)生的。
3、調(diào)用構(gòu)造函數(shù)創(chuàng)建的實(shí)例對(duì)象的__proto__屬性指向構(gòu)造函數(shù)的prototype。
4、在默認(rèn)情況下,所有原型對(duì)象都會(huì)自動(dòng)獲得一個(gè)constructor(構(gòu)造函數(shù))屬性,這個(gè)屬性包含一個(gè)指向prototype屬性所在函數(shù)的指針。
下圖展示了使用Person構(gòu)造函數(shù)創(chuàng)建實(shí)例后各個(gè)對(duì)象之間的關(guān)系
上圖展示了 Person 構(gòu)造函數(shù)、 Person 的原型屬性以及 Person現(xiàn)有的兩個(gè)實(shí)例之間的關(guān)系。
3、 跟__proto__屬性相關(guān)的兩個(gè)方法
isPrototypeOf():雖然在所有實(shí)現(xiàn)中都無(wú)法訪問(wèn)到__proto__,但可以通過(guò) isPrototypeOf()方法來(lái)確定對(duì)象之間是否存在這種關(guān)系。
alert(Person.prototype.isPrototypeOf(person1)); //true alert(Person.prototype.isPrototypeOf(person2)); //true
Object.getPrototypeOf():在所有支持的實(shí)現(xiàn)中,這個(gè)方法返回__proto__的值。例如:
alert(Object.getPrototypeOf(person1) == Person.prototype); //true alert(Object.getPrototypeOf(person1).name); //"Nicholas"
注意:雖然可以通過(guò)對(duì)象實(shí)例訪問(wèn)保存在原型中的值,但卻不能通過(guò)對(duì)象實(shí)例重寫(xiě)原型中的值。如果我們?cè)趯?shí)例中添加了一個(gè)屬性,而該屬性與實(shí)例原型中的一個(gè)屬性同名,那我們就在實(shí)例中創(chuàng)建該屬性,該屬性將會(huì)屏蔽原型中的那個(gè)屬性。請(qǐng)看下面的例子:
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); //"Greg"—— 來(lái)自實(shí)例 alert(person2.name); //"Nicholas"—— 來(lái)自原型
4、 判斷屬性是存在實(shí)例對(duì)象中,還是存在原型對(duì)象中,有以下方法
hasOwnProperty():可以檢測(cè)一個(gè)屬性是存在于實(shí)例中,還是存在于原型中。返回值為true表示該屬性存在實(shí)例對(duì)象中,其他情況都為false。
in 操作符:無(wú)論該屬性存在于實(shí)例中還是原型中。只要存在對(duì)象中,都會(huì)返回true。但是可以同時(shí)使用 hasOwnProperty()方法和 in 操作符,就可以確定該屬性到底是存在于對(duì)象中,還是存在于原型中。
var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name")); //false alert("name" in person1); //true person1.name = "Greg"; alert(person1.name); //"Greg" —— 來(lái)自實(shí)例 alert(person1.hasOwnProperty("name")); //true alert("name" in person1); //true alert(person2.name); //"Nicholas" —— 來(lái)自原型 alert(person2.hasOwnProperty("name")); //false alert("name" in person2); //true delete person1.name; alert(person1.name); //"Nicholas" —— 來(lái)自原型 alert(person1.hasOwnProperty("name")); //false alert("name" in person1); //true
5、 獲取或遍歷對(duì)象中屬性的幾種方法
for-in:通過(guò)for-in循環(huán)的返回的是能夠被訪問(wèn)的、可枚舉的屬性,不管該屬性是在實(shí)例中,還是存在原型中。
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; } Person.prototype={ sayName:function(){ return this.name; } } var p=new Person("李明",30,"詩(shī)人"); for(var prop in p){ console.log(prop);//name、age、job、sayName } console.log(Object.keys(p));//["name", "age", "job"] console.log(Object.keys(Person.prototype));//["sayName"] console.log(Object.getOwnPropertyNames(Person.prototype)) // ["constructor", "sayName"]
Object.keys():取得實(shí)例對(duì)象上所有可枚舉的屬性。 Object.getOwnPropertyNames(): 獲取實(shí)例對(duì)象所有屬性,無(wú)論它是否可枚舉。
注意:使用對(duì)象字面量來(lái)重寫(xiě)整個(gè)原型對(duì)象時(shí),本質(zhì)上完全重寫(xiě)了默認(rèn)的 prototype 對(duì)象,因此 constructor 屬性也就變成了新對(duì)象的 constructor 屬性(指向 Object 構(gòu)造函數(shù)),不再指向 Person。但是可以通過(guò)在重寫(xiě)原型對(duì)象時(shí)指定constructor屬性,使之還是指向原來(lái)的constructor。此時(shí),盡管 instanceof 操作符還能返回正確的結(jié)果,但通過(guò) constructor 已經(jīng)無(wú)法確定對(duì)象的類型了。
object instanceof constructor:檢測(cè) constructor.prototype 是否存在于參數(shù) object 的原型鏈上。
function Person() {} var friend2 = new Person(); Person.prototype = { //constructor : Person, name: "Nicholas", age: 29, job: "Software Engineer", sayName: function() { alert(this.name); } }; var friend = new Person(); console.log(friend2 instanceof Object); //true console.log(friend2 instanceof Person); //false, console.log(friend2.constructor == Person); //true console.log(friend2.constructor == Object); //false console.log(friend instanceof Object); //true console.log(friend instanceof Person); //true console.log(friend.constructor == Person); //false console.log(friend.constructor == Object); //true
由于原型的動(dòng)態(tài)性,調(diào)用構(gòu)造函數(shù)時(shí)會(huì)為實(shí)例添加一個(gè)指向最初原型的Prototype指針,而把原型修改為另外一個(gè)對(duì)象就等于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系??聪旅娴睦?/p>
function Person(){ } var friend = new Person(); Person.prototype = { constructor: Person, name : "Nicholas", age : 29, job : "Software Engineer", sayName : function () { alert(this.name); } }; var friend2=new Person(); friend.sayName(); //Uncaught TypeError: friend.sayName is not a function friend2.sayName();//Nicholas console.log(friend instanceof Person);//false console.log(friend instanceof Object);//true console.log(friend2 instanceof Person);//true
結(jié)果分析:這是因?yàn)閒riend1的prototype指向的是沒(méi)重寫(xiě)Person.prototype之前的Person.prototype,也就是構(gòu)造函數(shù)最初的原型對(duì)象。而friend2的prototype指向的是重寫(xiě)Person.prototype后的Person.prototype。如下圖所示
6、 原型鏈
基本思想是利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。最直觀的表現(xiàn)就是讓原型對(duì)象等于另一個(gè)類型的實(shí)例。
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //繼承了 SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue()); //true
SubType.prototype = new SuperType();這句代碼使得原來(lái)存在于 SuperType 的實(shí)例中的所有屬性和方法,現(xiàn)在也存在于 SubType.prototype 中。使得instance的constructor指向了SuperType。
console.log(instance.constructor===SuperType);//true
總結(jié): 訪問(wèn)一個(gè)實(shí)例屬性時(shí),首先會(huì)在實(shí)例中搜索該屬性。如果沒(méi)有找到該屬性,則會(huì)繼續(xù)搜索實(shí)例的原型。在通過(guò)原型鏈實(shí)現(xiàn)繼承的情況下,搜索過(guò)程就得以沿著原型鏈繼續(xù)向上。在找不到屬性或方法的情況下,搜索過(guò)程總是要一環(huán)一環(huán)地前行到原型鏈末端才會(huì)停下來(lái)。
就拿上面的例子來(lái)說(shuō),調(diào)用 instance.getSuperValue()會(huì)經(jīng)歷4個(gè)搜索步驟:
- 搜索instance實(shí)例;
- 搜索 SubType.prototype;
- 搜索SuperType的實(shí)例;
- 搜索 SuperType.prototype,最后一步才會(huì)找到該方法。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iframe的onreadystatechange事件在firefox下的使用
firefox不支持iframe的onreadystatechange事件,不過(guò)有個(gè)方法可以實(shí)現(xiàn)同等的效果,需要的朋友可以參考下2014-04-04JavaScript實(shí)現(xiàn)單鏈表過(guò)程解析
這篇文章主要介紹了JavaScript實(shí)現(xiàn)單鏈表過(guò)程,鏈表中的元素在內(nèi)存中不必是連續(xù)的空間。鏈表的每個(gè)元素有一個(gè)存儲(chǔ)元素本身的節(jié)點(diǎn)和指向下一個(gè)元素的引用。下面請(qǐng)和小編一起進(jìn)入文章了解更多的詳細(xì)內(nèi)容吧2021-12-12javascript小數(shù)計(jì)算出現(xiàn)近似值的解決辦法
在javascript里面,小數(shù)只能進(jìn)行相似計(jì)算,例如:5.06+1.30,你得到的結(jié)果會(huì)是6.359999999999999,但有的小數(shù)計(jì)算又是正確的,如果計(jì)算出現(xiàn)了近似值,你可以用如下的方法計(jì)算。2010-02-02uniapp使用百度地圖的保姆式教學(xué)(適合初學(xué)者!)
公司項(xiàng)目中有地圖展示和定位功能,所以下面這篇文章主要給大家介紹了關(guān)于uniapp使用百度地圖的保姆式教學(xué),文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03js中Math之random,round,ceil,floor的用法總結(jié)
本篇文章是對(duì)js中Math之random,round,ceil,floor的用法進(jìn)行了總結(jié)介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-12-12