JS中的原型链,曾经困惑过我。理论上说,当访问一个对象的属性时,首先在本身的属性里去找,如果没有找到,就去这个对象的原型找。如果原型有,就是它了,如果没有继续在往原型的原型找下去。。。
那么问题来了,假设这里的访问是指“写属性”,在搜索这个实例对象原型的时候,咦,找到了这个属性,那么现在重写这个属性,究竟是在本实例对象内新写了这个属性,还是在原型上重写了此属性呢?
区别在于,若是在本实例对象写属性,那么虽然以后访问(不论是读还是读写)的都是本实例的该属性,但其实原型上的这个属性也并没有消失也没有改变,只是访问的时候屏蔽它了。
而若是直接改写了原型上这个属性,那么以后任何这个原型的其它实例,它们的这个属性都会改变(前提是这些实例并没有自身的同名属性)。
到底是哪种呢,我们把代码跑起来看看!
function SuperType () {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
};
function SubType () {
this.subproperty = false;
}
SubType.prototype = new SuperType();
//给新实例写方法 这方法到顶级原型都没有 所以写在新实例里面了
SubType.prototype.getSubValue = function () {
return this.subproperty;
}
//给新实例写方法 这方法顶级原型有
//所以究竟是给实例添加了同名属性,还是直接改写了顶级原型??
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!!看到了没,顶级原型上的这个getSuperValue并没有被改写!如果改写了,那就是false,而不是undefined,是undefined的意思就是,还是原型的这个属性还是那个return this.property的函数,因为没有实例化,所以不存在this, 所以结果是undefined。也就是说,给对象写一个自身并不具有的属性,而原型却具有的属性,那么结果不是改变它原型对象上的改属性,而是给它自身添加了这个同名属性!原型上的该属性,并不会改变!只是在访问实例的时候被屏蔽!
最后,通过prototype其实是通过指针来继承属性,所以如果链断了,或者原型指针重新指向另一个对象,那么之前通过prototype继成的任何属性全部消失。但如果是通过contructor构造函数去实例化的对象,是没有这个问题的,因为通过constructor产生的属性,全部是副本,而不是指针。prototype可以通过指针一直引,a的prototype指向b对象,b的prototype指向c对象,而constructor也有类似这样的方法可以满足链式继成,例如a的constructor是bCon函数,而bCon函数里,调用cCon函数去call(this),即cCon.call(this)就是在这个constructor函数里面。
接下来又发生了一件怪事,以上的重写都通过"="来直接赋值,当然简洁明了,毫无疑问是真正的重写。那么有时候我们对原型上某属性值是数组,我们对它的操作又是另外一番光景了。看以下代码。
//object(o) 函数作用: 以o为原型实例化一个对象
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为原型的一个实例对象
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']
当你给实例写新属性值,这个新属性名在实例自身上并没有,但是该实例的原型上有这个属性的时候,有以下两种情况:
- 这里的“写”属性,是指用''=" 赋值,其实是重新建立一个副本,那么原型上的该属性值不会改变或消失,只是实例自身拥有了这个属性,那么以后对该实例上这个属性的读写,都是发生在自身这个属性上,不涉及原型。比如上例对name属性的重写。原型中的name还是‘Nicholas’,而实例的name则是自己重写过后的值。
- 这里的“写”属性,是引用的方式,比如上例,使用的是
anotherPerson.friends.push('Mary');
, 这里其实是使用指针,指向原型上这个属性,通过指针去重写的属性,都是指向同一个内存上的属性,并没有副本。一旦改变,所有指向它的引用,全部都会发生改变。
网友评论