本文深入解析JavaScript中constructor属性的来源与工作机制。通过剖析Function构造函数的创建过程,阐明constructor为何默认指向Function。重点探讨原型链中constructor的查找逻辑,以及修改prototype指向对实例constructor的影响。同时针对Object.prototype的特殊性进行说明,澄清常见误区,帮助开发者准确理解对象构造与原型继承关系。
constructor的基本定义
constructor是什么
简单来说,constructor属性指的是对象的构造函数。参考以下示例:
代码如下:
function Foo(){};
var foo = new Foo();
alert(foo.constructor);//Foo
alert(Foo.constructor);//Function
alert(Object.constructor);//Function
alert(Function.constructor);//Function 对于foo.constructor为Foo,这很容易理解,因为foo是由Foo构造函数创建的。至于Foo、Object和Function的constructor均为Function,这也没有争议。因为Foo、Object和Function都是函数对象,而所有的函数对象都是由Function这个构造函数构造出来的,所以它们的constructor属性自然指向Function(详细原理可参考《js_函数对象》相关章节)。
Prototype与Constructor的关系
Prototype与Constructor的关系
代码如下:
function Dog(){}
alert(Dog === Dog.prototype.constructor);//true 在JavaScript中,每个函数都拥有一个名为“prototype”的属性,该属性引用一个原型对象。而这个原型对象又包含一个名为“constructor”的属性,它反过来引用了原来的函数本身。这就形成了一种循环引用关系,具体结构如图:

constructor属性的来源
constructor属性来自何方
我们来看一下Function构造String对象的构造过程:

注:Function构造任何函数对象的过程都是相同的。因此,无论是String、Boolean、Number等内置对象,还是用户自定义的对象,其构造过程都与上图一致。这里以String为例,仅作为代表。
从图中可以看出,constructor是在Function创建函数对象时生成的。正如“Prototype与Constructor的关系”一节所述,constructor是函数对象prototype链中的一个属性。即String === String.prototype.constructor。
我还想用一段代码来证明理论是正确的:
代码如下:
function Person(){}
var p = new Person();
alert(p.constructor);//Person
alert(Person.prototype.constructor);//Person
alert(Person.prototype.hasOwnProperty('constructor'));//true
alert(Person.prototype.isPrototypeOf(p));//true
alert(Object.prototype.isPrototypeOf(p));//true
alert(Person.prototype == Object.prototype);//false 到这里,你可能会发现这与之前《原型链的实现原理》中提到的默认prototype指向Object.prototype的说法有冲突,显然之前的理论不够全面。
Object的特殊处理
特别的Object
细心的读者可能会提出疑问:这套理论是否适用于Object?因为以下代码似乎与上述理论冲突:
代码如下:
alert(Object.prototype.hasOwnProperty('constructor'));//true
alert(Object.prototype.hasOwnProperty('isPrototypeOf'));//true,如果按上面的理论,这里应该返回false 真的是这样吗?并非如此!那我们来看一下特殊的Object是如何处理的:

你会发现,这张图的原理与上面一张图的原理是一样的。这就能正确解释为什么Object.prototype.hasOwnProperty('isPrototypeOf')返回true。
constructor原理探究
constructor探究
代码如下:
function Animal(){}
function Person(){}
var person = new Person();
alert(person.constructor); //Person 根据上一节的内容,你能正确理解这段代码的结果吗?思考后,看一下其内存表示:

这张图明确展示了Function构造Animal和Person的过程,同时也显示了实例person与Person之间的关系。
再深入一点,代码如下:
代码如下:
function Animal(){}
function Person(){}
Person.prototype = new Animal();
var person = new Person();
alert(person.constructor); //Animal 这个时候,person的构造函数变成了Animal,这该如何解释?

注:图中的虚线表示Person默认的prototype指向(仅作辅助参考)。但是我们将Person.prototype重新指向了new Animal()。
此时,Person的prototype指向的是Animal的实例,因此person的constructor属性指向了Animal这个构造函数。
结论:constructor的原理非常简单,就是在对象的原型链上查找constructor属性。
注:如果你无法正确理解本文内容,请回顾前面章节的内容。