1. 回顾构造函数、实例、原型对象的关系
- 构造函数的
prototype
属性指向了构造函数的原型对象 - 实例对象是由构造函数创建的,实例对象的
__proto__
属性指向了构造函数的原型对象 - 构造函数的原型对象的
constructor
属性指向了构造函数,实例对象的原型的constructor
属性(usr1. __proto__.constructor
)也指向了构造函数
现有构造函数Star
,Star
的实例对象usr1
,其三者关系如图所示:
2. new操作符做了什么
简单来说,当我们使用new
操作符为构造函数创建实例时,发生了下面4件事:
- 创建一个新对象
- 为新对象添加属性
__proto__
,将该属性链接至构造函数的原型对象 - 执行构造函数,
this
被绑定在新对象上 - 确保返回一个对象
如下所示,通过new
操作符,为构造函数实例化了一个对象:
function Person(name, age) {
this.name = name;
this.age = age;
}
const usr1 = new Person('Jack', 18);
console.log(usr1);
// Person { name: 'Jack', age: 18 }
若在构造函数中,返回一个对象,那么通过new
操作符得到的对象,就是构造函数中返回的对象:
function Person(name, age) {
this.name = name;
this.age = age;
// 构造函数中返回一个对象
return {
a: 1,
};
}
const usr1 = new Person('Jack', 18);
console.log(usr1);
// { a: 1 }
3. 手动实现new
知道了new
操作符背后做的事情,我们可以自己写一个myNew
,实现相同的功能。
// 定义构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 手动实现new:
// constructor: 构造函数
// ...args: 构造函数参数
function myNew(constructor, ...args) {
// 1. 创建一个新对象
const obj = {};
// 2. 为新对象添加属性__proto__,将该属性链接至构造函数的原型对象
obj.__proto__ = constructor.prototype;
// 3. 执行构造函数,this被绑定在新对象上
const res = constructor.call(obj, ...args);
// 4. 确保返回一个对象
return res instanceof Object ? res : obj;
}
const usr1 = myNew(Person, 'Jack', 18);
const usr2 = new Person('Jack', 18);
console.log(usr1);
console.log(usr2);
在浏览器运行环境下,查看输出结果:
若构造函数本身返回一个对象,自己实现的myNew
也可以返回这个对象:
function Person(name, age) {
this.name = name;
this.age = age;
// 构造函数中返回一个对象
return {
a: 1,
};
}
function myNew(constructor, ...args) {
// 1. 创建一个新对象
const obj = {};
// 2. 为新对象添加属性__proto__,将该属性链接至构造函数的原型对象
obj.__proto__ = constructor.prototype;
// 3. 执行构造函数,this被绑定在新对象上
const res = constructor.call(obj, ...args);
// 4. 确保返回一个对象
return res instanceof Object ? res : obj;
}
const usr2 = myNew(Person, 'Jack', 18);
console.log(usr2);
// { a: 1 }
JavaScript常见手写代码: