十一、JavaScript之new的模拟实现

call

在 MDN 中对 new 运算符的解释是:

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

为了更清楚理解,举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
function Car(make, model, year) {
console.log(JSON.stringify(this)); // {}
console.log(this.__proto__ === Car.prototype); // true
this.make = make;
this.model = model;
this.year = year;
console.log(JSON.stringify(this)); //{"make":"Eagle","model":"Talon TSi","year":1993}
}

const car1 = new Car("Eagle", "Talon TSi", 1993);

console.log(car1.make); // Eagle

在上面的例子中,new 运算符会进行如下操作:

  1. 创建一个空的简单 JavaScript 对象(即{});
  2. 新对象的proto属性指向构造函数的 prototype 属性 ;
  3. 将步骤 1 新创建的对象作为 this 的上下文 ;
  4. 如果该函数没有返回对象,则返回 this。

模拟实现

实现一个 new 也就意味着不能用 new 关键字,那么要完成这么一系列步骤,我们通过一个函数 newFactory 来实现。

请看下面示例:

1
2
3
4
5
6
7
8
9
10
function newFactory() {
// 取出第一个参数,就是我们要传入的构造函数
var func = [].shift.call(arguments);
// 创建一个空对象,并且指定原型为func.prototype
var obj = Object.create(func.prototype);
// 使用 apply,改变构造函数 this 的指向到新建的对象,这样 obj 就可以访问到构造函数中的属性
var ret = func.apply(obj, arguments);
// 判断构造函数是否返回对象,有则return该对象,没有则返回this;(特殊情况返回null处理)
return typeof ret === "object" && ret !== null ? ret : obj;
}

测试一(构造函数无返回对象):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Otaku(name, age) {
this.name = name;
this.age = age;

this.habit = "Games";
}

Otaku.prototype.strength = 60;

Otaku.prototype.sayYourName = function () {
console.log("I am " + this.name);
};

var person = newFactory(Otaku, "Kevin", "18");

console.log(person.name); // Kevin
console.log(person.habit); // Games
console.log(person.strength); // 60

person.sayYourName(); // I am Kevin

测试二(构造函数有返回对象):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Otaku(name, age) {
this.name = name;
this.age = age;

this.habit = "Games";
return {
name,
age,
};
}

Otaku.prototype.strength = 60;

Otaku.prototype.sayYourName = function () {
console.log("I am " + this.name);
};

var person = newFactory(Otaku, "Kevin", "18");

console.log(person.name); // Kevin
console.log(person.habit); // undefined
console.log(person.strength); // undefined

person.sayYourName(); // person.sayYourName is not a function

参考文献

https://github.com/mqyqingfeng/Blog/issues/12