深入理解JavaScript系列(18):面向对象编程之E(8)


// 在修改A.prototype原型之前的情况
a.[[Prototype]] ----> Prototype <---- A.prototype
 
// 修改之后
A.prototype ----> New prototype // 新对象会拥有这个原型
a.[[Prototype]] ----> Prototype // 引导的原来的原型上

例如:

复制代码 代码如下:


function A() {}
A.prototype.x = 10;
 
var a = new A();
alert(a.x); // 10
 
A.prototype = {
  constructor: A,
  x: 20
  y: 30
};
 
// 对象a是通过隐式的[[Prototype]]引用从原油的prototype上获取的值
alert(a.x); // 10
alert(a.y) // undefined
 
var b = new A();
 
// 但新对象是从新原型上获取的值
alert(b.x); // 20
alert(b.y) // 30


因此,有的文章说“动态修改原型将影响所有的对象都会拥有新的原型”是错误的,新原型仅仅在原型修改以后的新创建对象上生效。

这里的主要规则是:对象的原型是对象的创建的时候创建的,并且在此之后不能修改为新的对象,如果依然引用到同一个对象,可以通过构造函数的显式prototype引用,对象创建以后,只能对原型的属性进行添加或修改。

非标准的__proto__属性

然而,有些实现(例如SpiderMonkey),提供了不标准的__proto__显式属性来引用对象的原型:

复制代码 代码如下:


function A() {}
A.prototype.x = 10;
 
var a = new A();
alert(a.x); // 10
 
var __newPrototype = {
  constructor: A,
  x: 20,
  y: 30
};
 
// 引用到新对象
A.prototype = __newPrototype;
 
var b = new A();
alert(b.x); // 20
alert(b.y); // 30
 
// "a"对象使用的依然是旧的原型
alert(a.x); // 10
alert(a.y); // undefined
 
// 显式修改原型
a.__proto__ = __newPrototype;
 
// 现在"а"对象引用的是新对象
alert(a.x); // 20
alert(a.y); // 30


注意,ES5提供了Object.getPrototypeOf(O)方法,该方法直接返回对象的[[Prototype]]属性——实例的初始原型。 然而,和__proto__相比,它只是getter,它不允许set值。

复制代码 代码如下:


var foo = {};
Object.getPrototypeOf(foo) == Object.prototype; // true

对象独立于构造函数
因为实例的原型独立于构造函数和构造函数的prototype属性,构造函数完成了自己的主要工作(创建对象)以后可以删除。原型对象通过引用[[Prototype]]属性继续存在:

复制代码 代码如下:


function A() {}
A.prototype.x = 10;
 
var a = new A();
alert(a.x); // 10
 
// 设置A为null - 显示引用构造函数
A = null;
 
// 但如果.constructor属性没有改变的话,
// 依然可以通过它创建对象
var b = new a.constructor();
alert(b.x); // 10
 
// 隐式的引用也删除掉
delete a.constructor.prototype.constructor;
delete b.constructor.prototype.constructor;
 
// 通过A的构造函数再也不能创建对象了
// 但这2个对象依然有自己的原型
alert(a.x); // 10
alert(b.x); // 10

instanceof操作符的特性
我们是通过构造函数的prototype属性来显示引用原型的,这和instanceof操作符有关。该操作符是和原型链一起工作的,而不是构造函数,考虑到这一点,当检测对象的时候往往会有误解:

复制代码 代码如下:


if (foo instanceof Foo) {
  ...
}


这不是用来检测对象foo是否是用Foo构造函数创建的,所有instanceof运算符只需要一个对象属性——foo.[[Prototype]],在原型链中从Foo.prototype开始检查其是否存在。instanceof运算符是通过构造函数里的内部方法[[HasInstance]]来激活的。

让我们来看看这个例子:

复制代码 代码如下:


function A() {}
A.prototype.x = 10;
 
var a = new A();
alert(a.x); // 10
 
alert(a instanceof A); // true
 
// 如果设置原型为null
A.prototype = null;
 
// ..."a"依然可以通过a.[[Prototype]]访问原型
alert(a.x); // 10
 
// 不过,instanceof操作符不能再正常使用了
// 因为它是从构造函数的prototype属性来实现的
alert(a instanceof A); // 错误,A.prototype不是对象

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgfgzd.html