Skip to main content

构造器/构造函数

为了与普通函数区别,构造函数名字的第一个字母通常大写。

构造函数的特点有两个:

  • 函数体内部使用了 this 关键字,代表了所要生成的对象实例。
  • 生成对象的时候,必须使用 new 命令。

构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(即 this 对象),将其“构造”为需要的样子。

如果构造函数内部有 return 语句,而且 return 后面跟着一个对象,new 命令会返回 return 语句指定的对象;否则,就会不管 return 语句,返回 this 对象。

  • 创建一个空对象,作为将要返回的对象实例。
  • 将这个空对象的原型,指向构造函数的 prototype 属性。
  • 将这个空对象赋值给函数内部的 this 关键字。
  • 开始执行构造函数内部的代码。

在 JavaScript 中实现,new 运算符大致如下所示(更精确的实现稍微复杂一点):

function newOperator(constructor, arrayWithArgs) {
var context = Object.create(constructor.prototype);
constructor.apply(context, arrayWithArgs);
return context;
}

new 命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号。下面两行代码是等价的,但是为了表示这里是函数调用,推荐使用括号。

// 推荐的写法
var v = new Vehicle();
// 不推荐的写法
var v = new Vehicle();

另外注意:如果忘记 new 则会导致 this 指向全局对象,这种情况下,构造函数就变成了普通函数,并不会生成实例对象。而且由于后面会说到的原因,this 这时代表全局对象,将造成一些意想不到的结果。

var Vehicle = function () {
this.price = 1000;
};

var v = Vehicle();
v; // undefined
price; // 1000

可以使用严格模式来避免该意外情况。另一个解决办法,构造函数内部判断是否使用 new 命令,如果发现没有使用,则直接返回一个实例对象。

function Fubar(foo, bar) {
if (!(this instanceof Fubar)) {
return new Fubar(foo, bar);
}

this._foo = foo;
this._bar = bar;
}

Fubar(1, 2)._foo(
// 1
new Fubar(1, 2)
)._foo; // 1

new.target

函数内部可以使用 new.target 属性。如果当前函数是 new 命令调用,new.target 指向当前函数,否则为 undefined。

function f() {
console.log(new.target === f);
}

f(); // false
new f(); // true

使用这个属性,可以判断函数调用的时候,是否使用 new 命令。

function f() {
if (!new.target) {
throw new Error('请使用 new 命令调用!');
}
// ...
}

f(); // Uncaught Error: 请使用 new 命令调用!