您是否曾困惑于 Javascript 中的 new 关键字呢?是否曾想理解关于 function 和 constructor 的区别是什么呢?

大多数 Javascript 的新开发者不太想要使用 new 关键字,因为这会让代码写的像是 Java 并且在使用上会造成一点混乱;在这篇文章我会尽量尝试着去理清这些东西,并解释它是如何运作的。

谈谈 function constructor

constructor 翻为构造器但为了让您之后更好地理解,会直接使用 constructor;在 Javascript 中任何一个函数(function)都可以被当作 constructor;Javascript 并没有明确的区分两者,也就是说 function 可以被当作 constructor 或者当作一般函数调用。

constructor 的用法就是 function 搭配new关键字:

function Person() {
  this.firstName = "Jay";
  this.lastName = "Chou";
}

var person = new Person();
console.log(person);

接着我们把person呼叫出来看,会得到如下的结果,通过new它会帮我们建立一个对象,然后里面有Person这个function里面的内容,并且变成了属性名称和属性值:

进一步来看 new 让这个过程发生了什么

当使用new命令时,它后面的函数依次执行下面的 4 个步骤:

  1. 创建一个空对象,作为将要返回的对象实例。
  2. 将这个空对象的原型,指向构造函数的prototype属性。
  3. 将这个空对象赋值给函数内部的this关键字。
  4. 如果函数没有 return 其他对象,那么new表达式中的函数调用会自动返回这个新对象。

也就是说,当我们使用new这个关键字时,实际上会先有一个空的对象被建立。

接着People这个构造函数会被执行,这个空对象的原型,指向了People.prototype

我们知道当函数执行的时候,在execution context中会有this被建立,而当我们使用new的时候,函数里面的this会被指定成刚刚所建立的那个空对象

所以当执行People这个function,执行到this.firstNamethis.lastName时,因为this现在指称的是那个空对象,所以实际上是在帮这个空对象赋予属性名称和属性值。

在这样的过程中,只要这个构造函数People 没有指定return为其他对象,它就会直接返回给我们这个新建立的对象

接着让我们通过一段代码来更清楚的了解这个执行的过程:

function Person() {
  this.firstName = "Jay";
  this.lastName = "Chou";
  console.log("这个函数有被执行");
}

var person = new Person();
console.log(person);

这时候在 chrome 中呼叫出来的结果如下,说明了当我们使用new 在构造对象的时候People 这个function 确实有被执行:

通过 new 会帮我们建立一个空的对象

现在我把我们上面的代码改成这样:

function Person() {
  console.log(this);
}

var person = new Person();
// console.log(person);

这时候代码返回的结果如下,表示的确在执行这段代码的过程中帮我们建立了一个新的空对象:

函数的最后若 return 其他对象,则原新对象内容会被覆盖

现在,让我们把原本的代码稍微做如下修改:

function Person() {
  this.firstName = "Jay";
  this.lastName = "Chou";
  return { RETURN: "原本this的内容就不会被返回" };
}

var person = new Person();
console.log(person);

构造函数的内部若return其他对象,new命令会返回return语句指定的对象,将原新对象内容覆盖掉;否则,就会不管return语句,返回this对象。返回的结果如下:

手写一个 new 实现

function create() {
  // 创建一个空的对象
  var obj = new Object(),
    // 获得构造函数,arguments中去除第一个参数
    Con = [].shift.call(arguments);
  // 链接到原型,obj 可以访问到构造函数原型中的属性
  obj.__proto__ = Con.prototype;
  // 绑定 this 实现继承,obj 可以访问到构造函数中的属性
  var ret = Con.apply(obj, arguments);
  // 优先返回构造函数返回的对象
  return ret instanceof Object ? ret : obj;
}

使用这个手写的 new

function Person() {...}

// 使用内置函数new
var person = new Person(...)

// 使用手写的new,即create
var person = create(Person, ...)

代码原理解析:

  1. new Object() 的方式新建了一个对象obj
  2. 取出第一个参数,就是我们要传入的构造函数。此外因为 shift 会修改原数组,所以 arguments 会被去除第一个参数
  3. obj 的原型指向构造函数,这样obj就可以访问到构造函数原型中的属性
  4. 使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
  5. 返回 obj

function constructor 的实际应用

由上面的方法,我们可以通过function的方式来建立一个新的对象,如果我们想要建立出同属性名称但不同属性值的对象内容,我们可以把对象的属性值变成参数,如此就能通过此function constructor建立出许多不同的对象:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

var person1 = new Person("Jay", "chou");
console.log(person1);
var person2 = new Person("Jane", "chou");
console.log(person2);

如此,我们就可以通过同一个构造函数建立出很多不同的对象:

此外,我们会把根据构造器(constructor)所建立出来的对象称作是实例(instance,这在之前的文章里也有提过。

注意!如果我们忘了加上 new 关键字

这里有一个地方我们需要非常留意,如果你在撰写代码的过程当中,忘记加上new这个关键字的话,比如:

function Person() {
  this.firstName = "Jay";
  this.lastName = "Chou";
}

var person = Person();
console.log(person);

如此,因为 JavaScript 不知道你是要执行这个函数,还是要根据这个function去建立object,因次最后会返回undefined的结果。

小结

  • 其实构造函数(function constructor)就是普通的 function,只是我们可以通过这个 function 来建立对象。
  • 通过在 function 前面加上 new 这个运算符,它会把函数中 this 这个关键字建立成一个新的对象,然后如果你没有在该函数的内部指定返回出其它对象的话,它就会自动返回这个新的对象给你。

那又是如何通过function constructors 来设定我们的原型(prototype)呢?让我们在下一篇文章来谈吧!

如果觉得文章对你有些许帮助,欢迎在我的 GitHub 博客点赞和关注,感激不尽!


JavaScript      JavaScript constructor new

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!