深入理解JavaScript系列(10):new

new 操作,我们在类对象,函数中都会使用到,那么它到底是用来干什么的呢?又是怎么实现的呢?

第一版 - 基础完成

我们先看看 MDN 的定义:

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

我们先来举个例子吧

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.weight = 70;
Person.prototype.getInfo = function () {
console.log(this.name, this.age, this.weight);
};
const person = new Person('hzzzzzzzq', 18);
console.log(person.name); // hzzzzzzzq
console.log(person.age); // 18
console.log(person.weight); // 70
person.getInfo(); // hzzzzzzzq 18 70

我们从例子中来看,我们创建的实例 person 可以做到

  • 获取构建函数里的属性
  • 获取构建函数的原型里的属性

所以由此我们来推断 new 操作做了什么。

  • new 的返回时一个对象
  • 需要为对象添加属性
  • 需要将对象原型指向构造函数的原型

但是由于 new 操作是关键字,所以就是用一个创建方法 objectFactory 来替代了。

1
2
3
4
5
let pserson = new Person(...arguments);

// 等价我们创建的方法

let person = objectFactory(Person, ...arguments);

由此,我们来看第一版代码吧。

1
2
3
4
5
6
7
function objectFactory() {
const obj = {}; // 创建一个新对象,也可以使用 new Object()
const Constructor = [].shift.call(arguments); // 获取构造函数,传入参数的第一个值
obj.__proto__ = Constructor.prototype; // 将对象的原型指向构造函数的原型
Constructor.apply(obj, arguments); // 绑定对象,为对象添加属性
return obj; // 返回对象
}

接下来,我们来测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.weight = 70;
Person.prototype.getInfo = function () {
console.log(this.name, this.age, this.weight);
};

function objectFactory() {
const obj = {}; // 创建一个新对象,也可以使用 new Object()
const Constructor = [].shift.call(arguments); // 获取构造函数,传入参数的第一个值
obj.__proto__ = Constructor.prototype; // 将对象的原型指向构造函数的原型
Constructor.apply(obj, arguments); // 绑定对象,为对象添加属性
return obj; // 返回对象
}
// const person = new Person('hzzzzzzzq', 18);
const person = objectFactory(Person, 'hzzzzzzzq', 18);
console.log(person.name); // hzzzzzzzq
console.log(person.age); // 18
console.log(person.weight); // 70
person.getInfo(); // hzzzzzzzq 18 70

发现,结果是一样的,这样就实现了吗?
当然不是,我们的构造函数如果有返回值呢?那会怎么样呢?

第二版 - 返回值

我们来看看例子,由此,我们可以看见,如果构造函数带有返回值,则会导致返回值以外定义的值无效。

  • 如果是函数,则返回类型错误
  • 如果是变量,返回 undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person(name, age) {
this.weight = 70;

return {
name: name,
age: age,
};
}
Person.prototype.getInfo = function () {
console.log(this.name, this.age, this.weight);
};
const person = new Person('hzzzzzzzq', 18);
console.log(person.name); // hzzzzzzzq
console.log(person.age); // 18
console.log(person.weight); // undefined
person.getInfo(); // TypeError: person.getInfo is not a function

但是返回值就一定是对象吗?结果显然是不一定。

所有,我们来看看不是对象时是什么情况:

1
2
3
4
5
6
7
8
9
10
11
function Person(name, age) {
this.name = name;
this.age = age;
this.weight = 70;

return 'hello';
}
const person = new Person('hzzzzzzzq', 18);
console.log(person.name); // hzzzzzzzq
console.log(person.age); // 18
console.log(person.weight); // 70

我们发现,都正确返回了,所以我们需要进行判断

  • 构造函数返回的是一个对象,则返回这个对象
  • 返回的是一个值,则忽略

接下来我们来更改我们的代码.

1
2
3
4
5
6
7
8
function objectFactory() {
const obj = {};
const Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
const result = Constructor.apply(obj, arguments); // 获取 返回值

return typeof result === 'object' ? result : obj; // typeof 进行判读
}

小结

我们来小结一下 new 做的事情。

  • 创建一个空的简单 JavaScript 对象(即{})
  • 为对象添加属性__proto__,将该属性链接至构造函数的原型对象
  • 将该对象作为 this 的上下文 ;
  • 如果该函数没有返回对象,则返回 this

到此,我们就完美复刻出了 new 操作喽。

-------------------- 本文结束 感谢阅读 --------------------