在工作中有時候會看到prototype和__proto__這兩個屬性,對這兩個屬性我一直比較蒙圈,但是我通過查閱相關資料,決定做一下總結加深自己的理解,寫得不對的地方還請各位大神指出。
1、prototype
每個函數都有一個prototype屬性,該屬性是一個指針,指向一個對象。 而這個對象的用途是包含由特定類型的所有實例共享的屬性和方法。使用這個對象的好處就是可以讓所有實例對象共享它所擁有的屬性和方法
2、 __proto__
每個實例對象都有一個__proto__屬性,用于指向構造函數的原型對象。__proto__屬性是在調用構造函數創建實例對象時產生的。
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ console.log(this.name); }; // 與聲明函數在邏輯上是等價的}var person1=new Person("Nicholas",29,"Software Engineer");console.log(person1);console.log(Person);console.log(person1.prototype);//undefinedconsole.log(person1.__proto__);console.log(Person.prototype);console.log(person1.__proto__===Person.prototype);//true輸出結果如下:

總結:
1、調用構造函數創建的實例對象的prototype屬性為"undefined",構造函數的prototype是一個對象。
2、__proto__屬性是在調用構造函數創建實例對象時產生的。
3、調用構造函數創建的實例對象的__proto__屬性指向構造函數的prototype。
4、在默認情況下,所有原型對象都會自動獲得一個constructor(構造函數)屬性,這個屬性包含一個指向prototype屬性所在函數的指針。
下圖展示了使用Person構造函數創建實例后各個對象之間的關系

上圖展示了 Person 構造函數、 Person 的原型屬性以及 Person現有的兩個實例之間的關系。
3、 跟__proto__屬性相關的兩個方法
isPrototypeOf():雖然在所有實現中都無法訪問到__proto__,但可以通過 isPrototypeOf()方法來確定對象之間是否存在這種關系。
alert(Person.prototype.isPrototypeOf(person1)); //true alert(Person.prototype.isPrototypeOf(person2)); //true
Object.getPrototypeOf():在所有支持的實現中,這個方法返回__proto__的值。例如:
alert(Object.getPrototypeOf(person1) == Person.prototype); //true alert(Object.getPrototypeOf(person1).name); //"Nicholas"
注意:雖然可以通過對象實例訪問保存在原型中的值,但卻不能通過對象實例重寫原型中的值。如果我們在實例中添加了一個屬性,而該屬性與實例原型中的一個屬性同名,那我們就在實例中創建該屬性,該屬性將會屏蔽原型中的那個屬性。請看下面的例子:
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"―― 來自實例 alert(person2.name); //"Nicholas"―― 來自原型4、 判斷屬性是存在實例對象中,還是存在原型對象中,有以下方法
hasOwnProperty():可以檢測一個屬性是存在于實例中,還是存在于原型中。返回值為true表示該屬性存在實例對象中,其他情況都為false。
in 操作符:無論該屬性存在于實例中還是原型中。只要存在對象中,都會返回true。但是可以同時使用 hasOwnProperty()方法和 in 操作符,就可以確定該屬性到底是存在于對象中,還是存在于原型中。
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" ―― 來自實例 alert(person1.hasOwnProperty("name")); //true alert("name" in person1); //true alert(person2.name); //"Nicholas" ―― 來自原型 alert(person2.hasOwnProperty("name")); //false alert("name" in person2); //true delete person1.name; alert(person1.name); //"Nicholas" ―― 來自原型 alert(person1.hasOwnProperty("name")); //false alert("name" in person1); //true5、 獲取或遍歷對象中屬性的幾種方法
for-in:通過for-in循環的返回的是能夠被訪問的、可枚舉的屬性,不管該屬性是在實例中,還是存在原型中。
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,"詩人"); 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():取得實例對象上所有可枚舉的屬性。 Object.getOwnPropertyNames(): 獲取實例對象所有屬性,無論它是否可枚舉。
注意:使用對象字面量來重寫整個原型對象時,本質上完全重寫了默認的 prototype 對象,因此 constructor 屬性也就變成了新對象的 constructor 屬性(指向 Object 構造函數),不再指向 Person。但是可以通過在重寫原型對象時指定constructor屬性,使之還是指向原來的constructor。此時,盡管 instanceof 操作符還能返回正確的結果,但通過 constructor 已經無法確定對象的類型了。
object instanceof constructor:檢測 constructor.prototype 是否存在于參數 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由于原型的動態性,調用構造函數時會為實例添加一個指向最初原型的Prototype指針,而把原型修改為另外一個對象就等于切斷了構造函數與最初原型之間的聯系。看下面的例子
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結果分析:這是因為friend1的prototype指向的是沒重寫Person.prototype之前的Person.prototype,也就是構造函數最初的原型對象。而friend2的prototype指向的是重寫Person.prototype后的Person.prototype。如下圖所示
6、 原型鏈
基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。最直觀的表現就是讓原型對象等于另一個類型的實例。
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()); //trueSubType.prototype = new SuperType();這句代碼使得原來存在于 SuperType 的實例中的所有屬性和方法,現在也存在于 SubType.prototype 中。使得instance的constructor指向了SuperType。
console.log(instance.constructor===SuperType);//true
總結: 訪問一個實例屬性時,首先會在實例中搜索該屬性。如果沒有找到該屬性,則會繼續搜索實例的原型。在通過原型鏈實現繼承的情況下,搜索過程就得以沿著原型鏈繼續向上。在找不到屬性或方法的情況下,搜索過程總是要一環一環地前行到原型鏈末端才會停下來。
就拿上面的例子來說,調用 instance.getSuperValue()會經歷4個搜索步驟:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
新聞熱點
疑難解答