wengjq/Blog

javaScript原型浅析

Opened this issue · 0 comments

JavaScript 中所有的函数默认都有一个名为 prototype(原型)的公有并且不可枚举的属性,这个属性是一个指针,它会指向一个对象。

function Foo () {
}
Foo.prototype; //{ }

而这个对象通常被称为Foo的原型对象,用 FooPrototype(自己假想的)表示。 Foo.prototype = FooPrototype; 在默认情况下,所有原型对象都会获得一个 constructor 属性,这个属性包含一个指向prototype属性所在函数的指针。当我们使用 new 来调用这个函数Foo创建一个实例时,该实例的内部会包含一个指针(内部属性),指向这个构造函数的原型对象,这个指针叫 [[prototype]] ,可以使用 proto 来访问这个原型对象。

function Foo () {
}
Foo.__proto__ === Function.prototype // true
var a = new Foo();
a.__proto__ === Foo.prototype; // true
Object.getPrototypeOf(a) === Foo.prototype; // true
Foo.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null;// true

Foo.prototype.constructor === Foo; // true
a.constructor === Foo; // true
a.constructor === Foo.prototype.constructor; // true

这里有个比较疑惑的点在于 a.constructor ,难道 a 也有 .constructor 属性?实际上a本身并没有 .constructor 属性,而且,虽然 a.constructor 确实指向 Foo 函数,但是这个属性并不是表示 a 有 Foo “构造”的。

实际上,.constructor 引用同样被委托给了 Foo.prototype ,而 Foo.prototype.constructor 默认指向 Foo 。
举个例子:

function Foo () {
}
Foo.prototype = { /**/ };//创建一个新原型对象,相当于 new Object()。
var a1 = new Foo();
a1.constructor === Foo;//false
a1.constructor === Object.prototype.constructor;//true
a1.constructor === Object;//true

a1并没有 .constructor 属性,所以它会委托 [[prototype]] 链上的 Foo.prototype 。但是这个对象也没有 .constructor 属性(不过默认的 Foo.prototype 对象有这个属性!),所以它会继续委托,这次它会委托给委托链顶端的 Object.prototype 。这个对象有 .constructor 属性,指向内置的 Object(...) 函数。

当然,我们可以给 Foo.prototype 添加一个 .constructor 属性,这需要手动添加一个符合正常行为的不可枚举属性。

function Foo () {
}
Foo.prototype = { /**/ };
Object.defineProperty({
  enumerable: false,
  writable: true,
  configurable: true,
  value: Foo // 让 .constructor 指向 Foo
})

JavaScript 里其他的引用类型,如 Array ,Date ,RegExp ,Function ,String ,Number ,Boolean 等。举例如下:

var a = new Array();
a.__proto__ === Array.prototype; // true
a.__proto__.__proto__ === Object.prototype; // true
a.__proto__.__proto__.__proto__ === null; // true
Array.prototype.constructor === Array;//true
a.constructor === Array;//true
a.constructor === Array.prototype.constructor;//true
Array.constructor === Function;// true