JS中的原型鏈,曾經(jīng)困惑過(guò)我。理論上說(shuō),當(dāng)訪問(wèn)一個(gè)對(duì)象的屬性時(shí),首先在本身的屬性里去找,如果沒(méi)有找到,就去這個(gè)對(duì)象的原型找。如果原型有,就是它了,如果沒(méi)有繼續(xù)在往原型的原型找下去。。。
那么問(wèn)題來(lái)了,假設(shè)這里的訪問(wèn)是指“寫(xiě)屬性”,在搜索這個(gè)實(shí)例對(duì)象原型的時(shí)候,咦,找到了這個(gè)屬性,那么現(xiàn)在重寫(xiě)這個(gè)屬性,究竟是在本實(shí)例對(duì)象內(nèi)新寫(xiě)了這個(gè)屬性,還是在原型上重寫(xiě)了此屬性呢?
區(qū)別在于,若是在本實(shí)例對(duì)象寫(xiě)屬性,那么雖然以后訪問(wèn)(不論是讀還是讀寫(xiě))的都是本實(shí)例的該屬性,但其實(shí)原型上的這個(gè)屬性也并沒(méi)有消失也沒(méi)有改變,只是訪問(wèn)的時(shí)候屏蔽它了。
而若是直接改寫(xiě)了原型上這個(gè)屬性,那么以后任何這個(gè)原型的其它實(shí)例,它們的這個(gè)屬性都會(huì)改變(前提是這些實(shí)例并沒(méi)有自身的同名屬性)。
到底是哪種呢,我們把代碼跑起來(lái)看看!
function SuperType () {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
};
function SubType () {
this.subproperty = false;
}
SubType.prototype = new SuperType();
//給新實(shí)例寫(xiě)方法 這方法到頂級(jí)原型都沒(méi)有 所以寫(xiě)在新實(shí)例里面了
SubType.prototype.getSubValue = function () {
return this.subproperty;
}
//給新實(shí)例寫(xiě)方法 這方法頂級(jí)原型有
//所以究竟是給實(shí)例添加了同名屬性,還是直接改寫(xiě)了頂級(jí)原型??
SubType.prototype.getSuperValue = function () {
return false;
}
var instance = new SubType();
console.log(instance.getSubValue()); //false
console.log(instance.getSuperValue()); //false
console.log(SuperType.prototype.getSuperValue()); //undefined
undefined!!看到了沒(méi),頂級(jí)原型上的這個(gè)getSuperValue并沒(méi)有被改寫(xiě)!如果改寫(xiě)了,那就是false,而不是undefined,是undefined的意思就是,還是原型的這個(gè)屬性還是那個(gè)return this.property的函數(shù),因?yàn)闆](méi)有實(shí)例化,所以不存在this, 所以結(jié)果是undefined。也就是說(shuō),給對(duì)象寫(xiě)一個(gè)自身并不具有的屬性,而原型卻具有的屬性,那么結(jié)果不是改變它原型對(duì)象上的改屬性,而是給它自身添加了這個(gè)同名屬性!原型上的該屬性,并不會(huì)改變!只是在訪問(wèn)實(shí)例的時(shí)候被屏蔽!
最后,通過(guò)prototype其實(shí)是通過(guò)指針來(lái)繼承屬性,所以如果鏈斷了,或者原型指針重新指向另一個(gè)對(duì)象,那么之前通過(guò)prototype繼成的任何屬性全部消失。但如果是通過(guò)contructor構(gòu)造函數(shù)去實(shí)例化的對(duì)象,是沒(méi)有這個(gè)問(wèn)題的,因?yàn)?strong>通過(guò)constructor產(chǎn)生的屬性,全部是副本,而不是指針。prototype可以通過(guò)指針一直引,a的prototype指向b對(duì)象,b的prototype指向c對(duì)象,而constructor也有類(lèi)似這樣的方法可以滿足鏈?zhǔn)嚼^成,例如a的constructor是bCon函數(shù),而bCon函數(shù)里,調(diào)用cCon函數(shù)去call(this),即cCon.call(this)就是在這個(gè)constructor函數(shù)里面。
接下來(lái)又發(fā)生了一件怪事,以上的重寫(xiě)都通過(guò)"="來(lái)直接賦值,當(dāng)然簡(jiǎn)潔明了,毫無(wú)疑問(wèn)是真正的重寫(xiě)。那么有時(shí)候我們對(duì)原型上某屬性值是數(shù)組,我們對(duì)它的操作又是另外一番光景了??匆韵麓a。
//object(o) 函數(shù)作用: 以o為原型實(shí)例化一個(gè)對(duì)象
function object(o) {
function F () {};
F.prototype = o;
return new F();
}
var person = {
name: 'Nicholas',
friends: ['shelby','Court','Van']
};
var anotherPerson = object(person);//anotherPerson是以person為原型的一個(gè)實(shí)例對(duì)象
anotherPerson.name = 'Greg';
console.log(anotherPerson.name); //'Greg'
console.log(person.name); //'Nicholas'
anotherPerson.friends = 'mary';
console.log(anotherPerson.friends); //mary
console.log(person.friends); // ['shelby','Court','Van']
anotherPerson.friends.push('Mary');
console.log(anotherPerson.friends); // ['shelby','Court','Van','Mary']
console.log(person.friends); // ['shelby','Court','Van','Mary']
當(dāng)你給實(shí)例寫(xiě)新屬性值,這個(gè)新屬性名在實(shí)例自身上并沒(méi)有,但是該實(shí)例的原型上有這個(gè)屬性的時(shí)候,有以下兩種情況:
- 這里的“寫(xiě)”屬性,是指用''=" 賦值,其實(shí)是重新建立一個(gè)副本,那么原型上的該屬性值不會(huì)改變或消失,只是實(shí)例自身?yè)碛辛诉@個(gè)屬性,那么以后對(duì)該實(shí)例上這個(gè)屬性的讀寫(xiě),都是發(fā)生在自身這個(gè)屬性上,不涉及原型。比如上例對(duì)name屬性的重寫(xiě)。原型中的name還是‘Nicholas’,而實(shí)例的name則是自己重寫(xiě)過(guò)后的值。
- 這里的“寫(xiě)”屬性,是引用的方式,比如上例,使用的是
anotherPerson.friends.push('Mary');, 這里其實(shí)是使用指針,指向原型上這個(gè)屬性,通過(guò)指針去重寫(xiě)的屬性,都是指向同一個(gè)內(nèi)存上的屬性,并沒(méi)有副本。一旦改變,所有指向它的引用,全部都會(huì)發(fā)生改變。