要实现new关键字, 咱们首先要知道new都作了什么事情, 红宝书(JavaScript高级程序设计第三版)第145页中(没错我翻书了, ^_^)是这样描述的:app
对于已经了解的人来讲,上面的四步没有任何问题,描述也准确无误, 可是我一个萌新上来就说这个? 建立一个对象? 是个什么样对象? 做用域赋给新对象?... 函数
下面咱们就结合实际的例子来一块儿理解一下,这四步到底作了什么ui
咱们先看咱们平时定义构造函数以及调用构造函数的姿式, 而后尝试从结果反推new
的执行过程this
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function() {console.log('My name is great ', this.name)};
const p = new Person('Alex', 22); // {name: 'Alex', age: 22}
复制代码
首先定义了一个Person
构造函数, 该构造函数有两个参数(name
, age
)以及定义在原型上的一个方法sayHi
, 使用new
关键字执行了Person
后, 咱们获得了一个对象P
, 而且p
上面有 name
以及 age
两个属性; 同时咱们也发现, p
能够访问到原型上的 sayHi
方法, 基于咱们看到的现象咱们下面一步步模拟new
的执行过程spa
js中咱们没法模拟new
的执行方式, 咱们可使用函数来模拟实现过程, 由于new
的时候执行了构造函数, 因此咱们第一个参数传入一个构造函数constructor
, 后面的参数做为constructor
执行时须要的参数:prototype
let myNew = function (constructor) {
}
复制代码
new
执行后获得的是一个对象, 因此咱们的函数执行后也要返回一个对象:设计
let myNew = function (constructor) {
// 建立一个对象
let instance = {};
// 返回新对象
return instance;
}
复制代码
返回出来的instance
就至关于咱们示例中的p
, p
能够访问构造函数上的原型, 因此咱们须要将建立的对象instance
的__proto__
属性指向构造函数的原型,这样获得的instance
才能访问到构造函数的原型(这一步不大清楚的同窗能够先看看原型链的概念)rest
let myNew = function (constructor) {
// 建立一个对象
let instance = {};
instance.__proto__ = constructor.prototype;
// 返回新对象
return instance;
}
console.log(myNew(Person, 'Alex', 22)) //{}
复制代码
到了这里, 咱们已经能理解第一步(建立一个对象),和第四步了(返回新对象), 但当前实现的myNew
执行之后返回的是仍旧是一个空对象, 和咱们预期的不符,缺乏了必要的属性name
和age
, 咱们继续观察一下构造函数code
function Person(name, age) {
this.name = name;
this.age = age;
}
复制代码
传入的参数最终都赋值给了构造函数this
的属性, 若是能将将this
改为instance
不就能够了吗? 使用apply
改变this
的指向cdn
let myNew = function (constructor) {
let restArgs = Array.prototype.slice.call(arguments, 1);
// 建立一个对象
let instance = {};
instance.__proto__ = constructor.prototype;
// 将构造函数的做用域赋给新对象(所以this就指向了这个新对象) && 执行构造函数中的代码(为这个新对象添加属性)
constructor.apply(instance, restArgs);
// 返回新对象
return instance;
}
console.log(myNew(Person, 'Alex', 22)) // {name: 'Alex', age: 22}
复制代码
而后咱们再稍加整理一下, 完整的代码以下
let myNew = function (constructor) {
if (!(constructor instanceof Function)) { throw new Error('arguments[0] require a function')};
let restArgs = Array.prototype.slice.call(arguments, 1);
// 建立一个对象
let instance = {};
instance.__proto__ = constructor.prototype;
// 将构造函数的做用域赋给新对象(所以this就指向了这个新对象) && 执行构造函数中的代码(为这个新对象添加属性)
let result = constructor.apply(instance, restArgs);
// 返回新对象
return result instanceof Object ? result : instance;
}
console.log(myNew(Person, 'Alex', 22)) // {name: 'Alex', age: 22}
复制代码
至此,咱们就彻底模拟了new
建立的整个过程!过程当中有错误的地方,欢迎评论留言!