大部分面试的时候,面试官先已原型链铺路,随后就会问你 new 关键词都做了些什么?也是为了下一个继承问题再次铺路!
那这篇文章我们就来讲讲这个 new!
我们通常在什么地方能看到它,在创建实例的时候,new 后面加上一个构造函数,就是创建这个构造函数的实例。

首先我们创建一个构造函数,看看 new 都做了哪些事情:

1
2
3
4
5
6
7
8
9
10
11
function Person() {
this.name = "Jack";
this.age = "29";
}
Person.prototype.eat = function () {
console.log("烤鸭");
};
var person = new Person();
console.log(person.name); //Jack
console.log(person.age); // 29
person.eat(); // 烤鸭

person.prototype
person.consturctor
我们从上述可以看出,new 关键词主要做了以下几个事情:

  1. 创建了一个新的对象,
  2. 将新对象的__proto__函数指向构造函数的 prototype,这个新对象就可以访问构造函数原型上的属性
  3. 将 this 指向改变,指向新的对象,这样就可以访问构造函数内部的属性
  4. 返回新的对象

接下来来模拟一下 new 函数:

1
2
3
4
5
6
7
function MyNew() {
let obj = new Object();
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
function Person() {
this.name = "Jack";
this.age = "29";
}
Person.prototype.eat = function () {
console.log("烤鸭");
};
var person = MyNew(Person);
console.log(person.name); //Jack
console.log(person.age); // 29
person.eat(); // 烤鸭

输出结果一样,那忽然有个想法,构造函数毕竟是个函数,如果构造函数有返回值,那 new 后结果是怎样呢:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 返回基本数据类型
function Person() {
this.name = "Jack";
this.age = "29";
return 1;
}
Person.prototype.eat = function () {
console.log("烤鸭");
};
var person = new Person();
console.log(person.name); //Jack
console.log(person.age); // 29
person.eat(); // 烤鸭
// 返回对象
function Person() {
this.name = "Jack";
this.age = "29";
return {
sex: "nan",
like: "nv",
};
}
Person.prototype.eat = function () {
console.log("烤鸭");
};
var person = new Person();
console.log(person.name); //undefined
console.log(person.age); // undefined
console.log(person.sex); //Jack
console.log(person.like); // 29
person.eat(); // Uncaught TypeError: person.eat is not a function

当构造函数返回的是一个基本数据类型时,跟没有返回值是一样的结果,但是当返回值是一个对象时,就会真的返回这个对象,return 之前定义的属性都会失效,并且定义在原型上的属性也会失效。根据这个特性,对之前写的 MyNew 函数进行升级。

1
2
3
4
5
6
7
function MyNew() {
let obj = new Object();
Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
let res = Constructor.apply(obj, arguments);
return typeof res === "object" ? res : obj;
}