JavaScript原型鏈的問題在面試經常遇到 , 有時候我們很難搞懂其中的關系 ! 今天我們來談談 JavaScript 中 原型鏈的問題!
-
什么是原型?
函數(shù)原型:
在JavaScript中,函數(shù)不僅僅是一個可以重用的代碼塊疲酌,而且還可以作為一種數(shù)據使用拒课。在堆空間中為函數(shù)分配了它的存儲空間,函數(shù)名或函數(shù)的其他形式的引用保存了這個存儲空間的引用地址僻肖。所以JavaScript中的函數(shù)是一種引用數(shù)據類型卢鹦,這就是為什么我們說JavaScript中的函數(shù)也是對象。
那么函數(shù)這樣的對象有很多特殊的性質揉稚,原型就是其中之一熬粗。每一個函數(shù)對象都包含一個屬性:prototype。當我們聲明一個函數(shù)時驻呐,函數(shù)對象就創(chuàng)建好了。而函數(shù)對象創(chuàng)建的同時猜拾,系統(tǒng)還會同時創(chuàng)建一個對象佣盒,并讓函數(shù)對象的prototype屬性指向它。
比如說盯仪,當我們執(zhí)行下面代碼時:
function Person ( perName,perAge ) { //… }
就聲明了一個函數(shù)蜜葱,系統(tǒng)會將這個函數(shù)對象創(chuàng)建出來。內存中應該是這樣的情況:
但是更深層次挖掘一下蚪燕,我還需要知道創(chuàng)建函數(shù)對象的同時奔浅,還會創(chuàng)建一個“原型對象”,由函數(shù)對象的prototype屬性指向這個原型對象
對象原型:
函數(shù)可以和以它為構造器所創(chuàng)建的所有對象共享原型對象鲁驶。就比如本文最初展示的代碼中舞骆,per01和per02都是Person函數(shù)創(chuàng)建的径荔,那么per01或per02就可以通過自身的proto屬性關聯(lián)到Person對象的原型脆霎。
下圖表示了它們之間的關系
所以函數(shù)的原型對象是可以為所有被創(chuàng)建對象共享的睛蛛,我們就可以將我們要為所有對象都添加的屬性添加到原型對象上, 從而實現(xiàn)寄生式繼承
function Person(perName,perAge,gender){
this.perName = perName;
this.perAge = perAge;
this.toString = function(){
return “PersonName=”+this.perName+” PersonAge=”+this.perAge;
};
}
var per01 = new Person(“Bob”, 20);
var per02 = new Person(“Kate”, 25);
Person.prototype.message = “Atguig is very good”;
console.log(per01.toString()+” message=”+per01.message);
console.log(per02.toString()+” message=”+per02.message);
//執(zhí)行結果:
//PersonName=Bob PersonAge=20 message=Atguig is very good
//PersonName=Kate PersonAge=25 message=Atguig is very good
JavaScript引擎在讀取per01對象的message屬性時先在當前對象本身的空間內查找,如果能找到則直接返回荸频,如果找不到則沿著proto屬性找到原型對象客冈,再在原型對象中查找message屬性。
- 什么是原型鏈
在研究了對象原型和函數(shù)原型的關系后遇绞,我們還可以進一步深入思考:既然原型對象是一個“對象”燎窘,那么這個對象有沒有proto這個屬性呢?當然有褐健!
function Person(perName,perAge,gender){
this.perName = perName;
this.perAge = perAge;
this.toString = function(){
return “PersonName=”+this.perName+” PersonAge=”+this.perAge;
};
}
var person = new Person(“Tom”, 20, “male”);
console.log(person.__proto__);
console.log(person.__proto__.__proto__);
//執(zhí)行結果:
//Person {}
//Object {}
說明person.proto所指向的對象是由Person函數(shù)創(chuàng)建的蚜迅,而person.proto.proto所指向的對象是由Object函數(shù)創(chuàng)建的。
就對象的本質而言谁不,任何一個對象都是以new 構造器函數(shù)的方式創(chuàng)建的,所以所有對象都和構造器函數(shù)共享原型對象吵血。這樣說可能你會有疑問偷溺,我可以通過{屬性名:屬性值}的方式創(chuàng)建對象呀,這里并沒有用到構造器函數(shù)呀侦另?那么情看下面的代碼:
var obj = {“myName”:”Jerry”,”myAge”:15};
console.log(obj.constructor);
console.log(obj.__proto__ === Object.prototype);
//執(zhí)行結果:
//function Object()
//true
說明從本質上來說,任何對象的創(chuàng)建都依賴對應的構造器函數(shù)褒傅,當然也包含原型機制。既然如此霹菊,那么原型對象本身也是一個對象碌尔,這個對象也指向一個原型對象券敌,那么原型對象的原型對象也是對象,可以繼續(xù)指向一個原型對象……這就是原型鏈叹坦。
但原型鏈并不是無止境的卑雁,到Object()函數(shù)為止。
var obj = {“myName”:”Jerry”,”myAge”:15};
console.log(obj.__proto__.__proto__);
//執(zhí)行結果:
//null
- 如何快速理解原型鏈
其實原型鏈理解起來不難, 我這里整理的一張圖 , 大家可以看看 僅供參考 !