constructor and new

构造函数

构造函数在技术上是常规函数。不过有两个约定:

  • 它们的命名以大写字母开头。
  • 它们只能由 "new" 操作符来执行。
js
1function User(name) {
2  this.name = name;
3  this.isAdmin = false;
4}
5
6let user = new User("Jack");
7
8alert(user.name); // Jack
9alert(user.isAdmin); // false

当一个函数被使用 new 操作符执行时,它按照以下步骤:

  1. 一个新的空对象被创建并分配给 this
  2. 函数体执行。通常它会修改 this,为其添加新的属性。
  3. 返回 this 的值。

构造器模式测试:new.target

在一个函数内部,我们可以使用 new.target 属性来检查它是否被使用 new 进行调用了。
对于常规调用,它为 undefined,对于使用 new 的调用,则等于该函数:

js
1function User() {
2  alert(new.target);
3}
4
5// 不带 "new":
6User(); // undefined
7
8// 带 "new":
9new User(); // function User { ... }

它可以被用在函数内部,来判断该函数是被通过 new 调用的“构造器模式”,还是没被通过 new 调用的“常规模式”。
我们也可以让 new 调用和常规调用做相同的工作,像这样:

js
1function User(name) {
2  if (!new.target) { // 如果你没有通过 new 运行我
3    return new User(name); // ……我会给你添加 new
4  }
5
6  this.name = name;
7}
8
9let john = User("John"); // 将调用重定向到新用户
10alert(john.name); // John

new 操作符做了什么

  • 在内存中创建一个新对象。
  • 将新对象内部的 __proto__ 赋值为构造函数的 prototype 属性。
  • 将构造函数内部的 this 被赋值为新对象(即 this 指向新对象)。
  • 执行构造函数内部的代码(给新对象添加属性)。
  • 如果构造函数返回非空对象,则返回该对象。否则返回 this。

new 操作符的模拟实现

js
1function fakeNew() {
2  // 创建新对象
3  var obj = Object.create(null);
4  var Constructor = [].shift.call(arguments);
5  // 将对象的 __proto__ 赋值为构造函数的 prototype 属性
6  obj.__proto__ = Constructor.prototype;
7  // 将构造函数内部的 this 赋值为新对象
8  var ret = Constructor.apply(obj, arguments);
9  // 返回新对象
10  return typeof ret === "object" && ret !== null ? ret : obj;
11}
12
13function Group(name, member) {
14  this.name = name;
15  this.member = member;
16}
17
18var group = fakeNew(Group, "hzfe", 17);