每个对象都有一个原型(一些系统对象除外)。原型通信是通过内部的、隐式的、不可直接访问[[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属性改变的时候才会影响到),就是说新创建的对象才有有新的原型,而已创建对象还是引用到原来的旧原型(这个原型已经不能被再被修改了)。
复制代码 代码如下: