前言
大家好,我是抹茶。
在JavaScript中,new是一个关键字,它后面跟着函数调用的话,默认情况会返回一个新的对象,这种情况被称为构造函数调用。那么,我们如何自己实现一个new方法呢?
设计与思考
首先我们要思考,new都具备了哪些功能?
- 它会返回一个对象
- 对象的原型链指向的是构造函数的
prototype - 对象会被绑定到函数调用的this
编码实现
第一步,会返回一个对象,所以创造一个新的对象
function myNew (...rest) {
// 1.创建一个新的对象
const obj = {};
return obj;
}
第二步,new 后面跟着的是一个函数,所以要获取构造函数
function myNew (...rest) {
// 1.创建一个新的对象
const obj = {};
// 2.获取构造函数
const Con = rest.shift(); // 构造函数是第一个参数
return obj;
}
第三步,将obj的原型链接到构造函数的原型上
function myNew (...rest) {
// 1.创建一个新的对象
const obj = {};
// 2.获取构造函数
const Con = rest.shift(); // 函数是第一个参数
// 3.将对象的原型链接到构造函数的prototype上
Object.setPrototypeOf(obj, Con.prototype);
return obj;
}
第四步,构造函数执行,执行时要绑定this,this指向的是预期返回的对象
function myNew (...rest) {
// 1.创建一个新的对象
const obj = {};
// 2.获取构造函数
const Con = rest.shift(); // 函数是第一个参数
// 3.将对象的原型链接到构造函数的prototype上
Object.setPrototypeOf(obj, Con.prototype);
// 4.构造函数执行,其this要绑定我们新创建的obj
const result = Con.apply(obj, rest);
return obj;
}
第五步,判断选择返回的对象,因为是返回的对象,所以需要判断构造函数的执行返回的是否是对象
function myNew (...rest) {
// 1.创建一个新的对象
const obj = {};
// 2.获取构造函数
const Con = rest.shift(); // 函数是第一个参数
// 3.将对象的原型链接到构造函数的prototype上
Object.setPrototypeOf(obj, Con.prototype);
// 4.构造函数执行,其this要绑定我们新创建的obj
const result = Con.apply(obj, rest);
// 5.判断返回对象
return typeof result == 'object' && result !== null ? result: obj;
}
现在,我们自己设计实现的myNew已经大功告成,接下来测试一下。
测试功能
思考一下我们预期的结果是怎样的,首先,new的构造调用会返回一个对象,其次,这个对象的原型会链接到构造函数的prototype。
我们采用的测试代码如下:
function myNew (...rest) {
// 1.创建一个新的对象
const obj = {};
// 2.获取构造函数
const Con = rest.shift(); // 函数是第一个参数
// 3.将对象的原型链接到构造函数的prototype上
Object.setPrototypeOf(obj, Con.prototype);
// 4.构造函数执行,其this要绑定我们新创建的obj
const result = Con.apply(obj, rest);
// 5.判断返回对象
return typeof result == 'object' && result !== null ? result: obj;
}
function Con(name) {
this.text = "hello world";
this.num = 10086;
this.name = name;
}
const a = myNew(Con,'抹茶');
console.log(a);// Con { text: 'hello world', num: 10086, name: '抹茶' }
console.log(Object.getPrototypeOf(a) == Con.prototype);//true
以上,我们自己实现的myNew方法通过了测试。
总结
在JavaScript中,new后面跟着一个函数调用的情况如new myFn(),叫作构造函数调用,与普通的形如myFn()不同的是,它会默认返回一个对象,这个对象的原型会链接到构造函数的prototype上。ES6中的class其实是function的语法糖,正是因为JS引擎内部通过new实现了原型链的绑定,new出来的实例才能继承父类的属性和方法(本质是通过原型链访问)。
本文详细的一步步的介绍了如何实现一个new,从而剥开这隐秘的底层设计原理。