没有构造函数,只有对函数的构造调用。
构造函数是在类中的知识,很多人以为JavaScript中也有构造函数是因为这样的代码:
function Foo() {
//...
}
var a = new Foo();
认为Foo是一个类的原因是
- 看到了关键字new,在面向类中的语言中(比如Java)实例化对象的时候也会用到。
- Foo()的调用方式类似初始化类的时候类的构造函数的调用方式。
其实,Foo和其它函数没有任何区别。函数本身不是构造函数。当你在普通的函数调用前面加上new关键字之后,就会把这个函数调用变成一个“构造函数调用”。new会劫持所有普通函数并用构造对象的形式来调用它。
举个例子:
function NothingSpecial() {
console.log("nothing");
}
var a = new NothingSpecial();
//nothing
a; //NothingSpecial {}
NothingSpecial只是一个普通的函数,但是,使用new调用时,它就会构造一个对象并赋值给a。
函数不是构造函数,当且仅当使用new时,函数调用会变成“构造函数调用”。
有人可能会有疑问,他们也许是因为这样的代码才认为Foo是构造函数的:
function Foo() {
//...
}
Foo.prototype.constructor === Foo; //true
var a = new Foo();
a.constructor ===Foo; //true
Foo.prototype默认有一个公有并且不可枚举的属性.constructor,这个属性引用的是对象关联的函数Foo)。
看起来a.constructor === Foo 意味着a有一个指向Foo的.constructor属性。将.constructor属性指向Foo看作是a对象由Foo“构造“很好理解。但是,事实是这样吗?
function Foo() {
//...
}
var a = new Foo();
Object.getOwnPropertyNames(a); //[](并没有constructor属性)
Foo.prototype的.constructor属性只是Foo函数在声明时的默认属性。如果你创建了一个新对象并替换了函数默认的.prototype对象引用,那么新对象并不会自动获得.constructor属性。
function Foo() {
//...
}
//创建一个新原型对象
Foo.prototype = {
//...
}
var a = new Foo();
a.constructor === Foo; //false
a.constructor === Object; //true
很多人认为是Foo()执行了构造工作,但是,如果”constructor”表示”由…构造”,a.constructor应该是Foo,但是并不是这样。
为什么会这样的?我们解释一下:
a没有.constructor属性,它会委托原型链上的Foo.prototype。但是Foo.prototype对象也没有.constructor属性(默认的有),继续委托。委托给顶端的Object.prototype。这个对象有.constructor属性,指向内置的Object()函数。
当然,可以自己手动给Foo.prototype添加一个不可枚举的.constructor属性:
function Foo() {
//...
}
Foo.prototype = {
//...
}
Object.defineProperty(Foo.prototype, "constructor", {
value: Foo, //让constructor指向Foo
configurable: false,
writable: true,
enumerable: false
});
综上所述,.constructor是一个非常不可靠、不安全的引用,尽量避免使用。