JavaScript是一门动态语言,可以很容易地添加、删除和修改属性。这种特性同样适合于原型,包括函数原型和对象原型。
console.log("---------------通过原型,一切都可以在运行时修改------------");
//定义了一个构造函数,该构造函数中创建了一个swung属性,初始化为布尔值
function NinjaTest3() {
this.swung = true;
}
//通过new操作符调用构造函数,创建实例NinjaTest3
const ninjaTest3 = new NinjaTest3();
//在实例对象创建完成之后,在原型上添加一个方法
NinjaTest3.prototype.swingSword = function () {
return this.swung;
}
//验证该方法存在于对象中
if (ninjaTest3.swingSword()) {
console.log("Method exists, even out of order");
}
//使用字面量对象完全重写NinjaTest3的原型对象,仅有一个pierce方法
NinjaTest3.prototype = {
pierce: function () {
return true;
}
}
//尽管我们已经完全替换了NinjaTest3的构造器原型,但是实例化后的NinjaTest3对象仍然具有swingSword方法,因为对象ninjaTest3仍然保持着对旧的NinjaTest3原型的引用
if (ninjaTest3.swingSword()) {
console.log("Our ninja can still swing!");
}
const ninjaTest3_2 = new NinjaTest3();
if (ninjaTest3_2.pierce()) {
console.log("Newly created ninjas can pierce.");
}
if (!ninjaTest3_2.swingSword) {
console.log("ninjaTest3_2.swingSword:" + ninjaTest3_2.swingSword);
console.log("But they can not swing!");
}
从上面的代码发现:
定义了NinjaTest3构造器,并使用它创建一个对象实例。此程序的状态如下:
由上图可知:构造完成之后,ninjaTest3 具有swung属性,它的原型是NinjaTest3原型,仅有一个构造属性。
实例对象创建完成之后,我们在原型上添加swingSword方法。通过执行测试来验证可以在对象创建完成之后,修改对象的原型。如下如所示:
因为ninjaTest3实例指向NinjaTest3原型,在实例构造完成之后对原型作更改,该实例仍然能够访问。
然后我们使用字面量对象完成重写NinjaTest3的原型对象,该字面量对象仅含有pierce方法。如下图:
函数的原型可以被任意替换,已经构建的实例引用旧的原型。从上图可以发现:即使NinjaTest3 函数不再指向旧的NinjaTest3 原型,但是旧的原型仍然存在于ninjaTest3 实例中,通过原型链依然可以访问到swingSword方法。
但是,此时在NinjaTest3 发生变化之后再创建新的实例对象,此时应用程序的状态如下:
新创建的实例引用新的原型
对象与函数原型之间的引用关系是在对象创建时建立的。新创建的对象将引用新的原型,它只能访问piercr方法,原来旧的对象保持着原有的原型,仍然能够访问swingSword方法。
参考《JavaScript忍者秘籍》