JS基礎之原型&原型鏈
- 原型&原型鏈
- 構造函數創建對象
- prototype
- proto
- constructor
- 實例與原型
- 原型的原型
- 原型鏈
- 其他
- constructor
- proto
- 繼承
原型&原型鏈
構造函數創建對象
我們先使用構造函數創建一個對象:
function Person(){
}
var person = new Person();
person.name = 'trigger';
console.log(person.name);//trigger
在這個例子中,Person就是一個構造函數,我們使用new創建了一個實例對象person。
prototype
每個函數都有一個prototype
屬性,比如:
function Person(){}
//雖然寫在注釋里,但是你要注意:
//prototype是函數才會有的屬性
Person.prototype.name = 'pig';
var person1 = new Person();
var person2 = new Person();console.log(person1.name);//pig
console.log(person2.name);//pig
那這個函數的prototype
屬性到底指向的是什么呢?是這個函數的原型嗎?
其實,函數的prototype
屬性指向了一個對象,這個對象正是調用該構造函數而穿件的實例的原型,也就是這個例子中的person1
和person2
的原型。
那什么是原型呢?你可以這樣理解:
每一個JavaScript對象(null除外)在創建的時候就會與之關聯另一個對象,這個對象就是我們所說的原型,每一個對象都會從原型“繼承”屬性。
用一張圖表示構造函數和實例原型之間的關系:
這里用Person.prototype表示實例原型。
那么該怎么表示實例與實例原型,也就是person
和Person.prototype
之間的關系呢?
proto
這是每一個JavaScript對象(除了null)都具有的一個屬性,叫__proto__
,這個屬性會指向該對象的原型。
function Person(){}
var person = new Person();
console.log(person.__proto__ === Person.prototype);//true
既然實例對象和構造函數都可以指向原型,那么原型是否有屬性指向構造函數或者實例呢?
constructor
指向實例倒是沒有,因為一個構造函數可以生成多個實例,但是原型指向構造函數是有的:constuctor
,每個原型都有一個constructor
屬性指向關聯的構造函數。
function Person(){}
console.log(Person === Person.prototype.constructor);//true
所以,這里可以得到:
function Person(){}
var person = new Person();
console.log(person.__proto__ == Person.prototype);//true
console.log(Person.prototype.constructor == Person);//true
console.log(Object.getPrototypeOf(person) === Person.prototype);// true
實例與原型
當讀取實例的屬性時,如果找不到,就會查找與對象關聯的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層為止。
舉個例子:
function Person(){
}
Person.prototype.name = 'cat';
var person = new Person();
person.name = 'dog';
console.log(person.name);//dog
delete person.name;
console.log(person.name);//cat
在這個例子中,我們給實例對象person添加了name屬性,當我們打印person.name
的時候,結果自然為dog
。
但是當我們刪除person中的name屬性時,讀取person.name
,從person對象中找不到name屬性就會從person的原型,也就是person.__proto__
,也就是Person.prototype
中查找,結果為cat
。
原型的原型
如果在原型撒花姑娘還沒有找到呢?原型的原型又是什么呢?
var obj = new Object();
obj.name = 'rabbit';
console.log(obj.name);//rabbit
其實原型對象就是通過Object構造函數生成的,結合之前所說,實例的__proto__
指向構造函數的prototype
,所以我們再更新下關系圖:
原型鏈
那Object.prototype
的原型呢?
null,我們可以打印:
console.log(Object.prototype.__proto__ === null);//true
然而null究竟代表了什么呢?
null表示“沒有對象”,即該處不應該有值。
所以Object.prototype.__proto__
的值為null跟Object.prototype
沒有原型,其實表達了一個意思。
所以查找屬性的時候查到Object.prototype
就可以停止查找了。
最后一張關系圖也可以更新為:
其中,藍色為原型鏈。
其他
constructor
首先是constructor
屬性:
function Person(){
}
var person = new Person();
console.log(person.constructor === Person);//true
當獲取person.constructor
時,其實person中并沒有constructor屬性,當不能讀取到constructor屬性時,會從person的原型也就是Person.prototype
中讀取,正好原型中有該屬性,所以:
person.constructor === Person.prototype.constructor
proto
絕大部分瀏覽器都支持這個非標準的方法訪問原型,然而它并不存在與Person.prototype
中,實際上,他是來自于Object.prototype
,與其說是一個屬性,不如說是一個getter/setter
,當使用obj.__proto__
時,可以理解成返回了Object.getPrototypeOf(obj)
。
繼承
關于繼承,前面提到“每一個對象都會從原型‘繼承’屬性”,實際上,繼承是一個十分具有迷惑性的說法,引用《你不知道的JavaScript》中的話,就是:
繼承意味著復制操作,然而JavaScript默認并不會復制對象的屬性,相反,JavaScript只是在兩個對象之間創建一個關聯,這樣,一個對象就可以通過委托訪問另一個對象的屬性和函數,所以與其叫繼承,委托的說法反而更準確些。
好啦~枯燥的知識就到這里啦!
欣賞一下美女壁紙,放松一下心情吧~ 嘻嘻 ~