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

每个对象都有一个原型(一些系统对象除外)。原型通信是通过内部的、隐式的、不可直接访问[[Prototype]]原型属性来进行的,原型可以是一个对象,也可以是null值。

属性构造函数(Property constructor)

上面的例子有有2个重要的知识点,第一个是关于函数的constructor属性的prototype属性,在函数创建的算法里,我们知道constructor属性在函数创建阶段被设置为函数的prototype属性,constructor属性的值是函数自身的重要引用:

复制代码 代码如下:


function A() {}
var a = new A();
alert(a.constructor); // function A() {}, by delegation
alert(a.constructor === A); // true

通常在这种情况下,存在着一个误区:constructor构造属性作为新创建对象自身的属性是错误的,但是,正如我们所看到的的,这个属性属于原型并且通过继承来访问对象。

通过继承constructor属性的实例,可以间接得到的原型对象的引用:

复制代码 代码如下:


function A() {}
A.prototype.x = new Number(10);
 
var a = new A();
alert(a.constructor.prototype); // [object Object]
 
alert(a.x); // 10, 通过原型
// 和a.[[Prototype]].x效果一样
alert(a.constructor.prototype.x); // 10
 
alert(a.constructor.prototype.x === a.x); // true

但请注意,函数的constructor和prototype属性在对象创建以后都可以重新定义的。在这种情况下,对象失去上面所说的机制。如果通过函数的prototype属性去编辑元素的prototype原型的话(添加新对象或修改现有对象),实例上将看到新添加的属性。

然而,如果我们彻底改变函数的prototype属性(通过分配一个新的对象),那原始构造函数的引用就是丢失,这是因为我们创建的对象不包括constructor属性:

复制代码 代码如下:


function A() {}
A.prototype = {
  x: 10
};
 
var a = new A();
alert(a.x); // 10
alert(a.constructor === A); // false!


因此,对函数的原型引用需要手工恢复:

复制代码 代码如下:


function A() {}
A.prototype = {
  constructor: A,
  x: 10
};
 
var a = new A();
alert(a.x); // 10
alert(a.constructor === A); // true

注意虽然手动恢复了constructor属性,和原来丢失的原型相比,{DontEnum}特性没有了,也就是说A.prototype里的for..in循环语句不支持了,不过第5版规范里,通过[[Enumerable]] 特性提供了控制可枚举状态enumerable的能力。

复制代码 代码如下:


var foo = {x: 10};
 
Object.defineProperty(foo, "y", {
  value: 20,
  enumerable: false // aka {DontEnum} = true
});
 
console.log(foo.x, foo.y); // 10, 20
 
for (var k in foo) {
  console.log(k); // only "x"
}
 
var xDesc = Object.getOwnPropertyDescriptor(foo, "x");
var yDesc = Object.getOwnPropertyDescriptor(foo, "y");
 
console.log(
  xDesc.enumerable, // true
  yDesc.enumerable  // false
);

显式prototype和隐式[[Prototype]]属性

通常,一个对象的原型通过函数的prototype属性显式引用是不正确的,他引用的是同一个对象,对象的[[Prototype]]属性:

a.[[Prototype]] ----> Prototype <---- A.prototype

此外, 实例的[[Prototype]]值确实是在构造函数的prototype属性上获取的。

然而,提交prototype属性不会影响已经创建对象的原型(只有在构造函数的prototype属性改变的时候才会影响到),就是说新创建的对象才有有新的原型,而已创建对象还是引用到原来的旧原型(这个原型已经不能被再被修改了)。

复制代码 代码如下:

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

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