构造函数和实例化

JavaScript 可以通过构造函数批量创建对象,这个创建的过程通过 new 关键字实现,也被称作实例化,对象被创建时会有默认的属性和原型方法。

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHi = function () {
  var output = "My name is " + this.name + ", I'm " + this.age + " years old."
  console.log(output);
}

var panda = new Person('panda', 25);

panda.sayHi(); // My name is panda, I'm 25 years old.

new 的过程都做了什么

new 的过程中主要做了 4 件事:

  • 创建一个空对象;
  • 将这个空对象的原型指向构造函数的原型;
  • 执行构造函数,并将默认属性挂载到这个空对象上;
  • 判断构造函数的返回值,如果是对象则返回这个对象,否则返回新创建的对象。

模拟 new 关键字

清楚了构造函数实例化的过程,我们就来手动实现一个 New 函数,来替代关键字 new

// New 函数的第一个参数为构造函数,剩余的参数为实例化传入的参数
function New() {
  var Constructor = Array.prototype.shift.call(arguments);
  var obj = {};
  obj.__proto__ = Constructor.prototype;
  var result = Constructor.apply(obj, arguments);
  return result instanceof Object ? result : obj;
}
// 使用 New 创建实例
var panda = New(Person, 'panda', 25);

console.log(panda); // Person {name: "panda", age: 25}
panda.sayHi(); // My name is panda, I'm 25 years old.

上面的方法已经实现了 new 关键字的功能,但是美中不足的是,不能将构造函数和实例化的参数区分清晰,下面稍微优化一下。

/* 优化后 */
function New(Constructor) {
  return function () {
    var obj = {};
    obj.__proto__ = Constructor.prototype;
    var result = Constructor.apply(obj, arguments);
    return result instanceof Object ? result : obj;
  }
}
/* 使用 New 创建实例 */
var panda = New(Person)('panda', 25);

console.log(panda); // Person {name: "panda", age: 25}
panda.sayHi(); // My name is panda, I'm 25 years old.

与第一种相比只是 New 函数的使用方式有所变化,New 执行后返回一个函数,调用这个返回的函数,才会进行实例化,传入的参数为实例化对象的参数,这样是 New 的功能变的单一,也将构造函数与实例化的参数分开。