前置知识:
- 实例都有
__proto__,它指向父类的prototype;换句话说就是他们俩是指向同一个内存地址的对象,他们的值是完全相等的,就是一个东西;
要手写new首先要知道new的时候都做了些什么:
- 创建一个实例对象,并把这个对象的原型指向类的原型;
- 执行类方法,同时把方法中的
this指向实例对象; - 处理返回结果,当返回结果为原始类型(基本数据类型)时,返回实例,否则返回结果本身。
少废话,看东西!
function _new(Ctor, ...params){
// 1. 创建一个实例对象,并把这个对象的原型指向类的原型;
let obj = {};
obj.__proto__ = Ctor.prototype
// 2. 执行类方法,同时把方法中的`this`指向实例对象;
let result = Ctor.call(obj, ...params)
// 3. 处理返回结果,当返回结果为原始类型(基本数据类型)时,返回实例,否则返回结果本身。
if(result !== null && /^(object|function)$/.test(typeof result)) return result;
return obj
}
// 使用
function Dog(){}
let dog = _new(Dog, 'dudu')
兼容问题
IE中没有__proto__属性! IE中把实例的原型指向类的原型的兼容方法是使用Object.create()。
Object.create()使用:
let Fn = function(){}
// 这样就把实例 f 的原型指向了Fn
let f = Object.create(Fn)
兼容IE的写法
function _new(Ctor...params){
let obj = Object.create(Ctor);
let result = Ctor.call(obj, ...params)
if(result !== null && /^(object|function)$/.test(typeof result)) return result;
return obj
}
// 使用
function Dog(){}
let dog = _new(Dog, 'dudu')
最后
把检验加上:
function _new(Ctor...params){
// !Ctor.prototype 这里判断主要防止传入箭头函数,箭头函数是没有 `constructor`的
if(Ctor === Symbol || Ctor === BigInt || typeof Ctor !== 'function' || !Ctor.prototype){
throw new TypeError(`${Ctor} is not a construcor!`)
}
let obj = Object.create(Ctor);
let result = Ctor.call(obj, ...params)
if(result !== null && /^(object|function)$/.test(typeof result)) return result;
return obj
}
// 使用
function Dog(){}
let dog = _new(Dog, 'dudu')
扩展
手写Object.create
Object.create = function(prototype){
let proxy = function(){}
proxy.prototype = prototype
return new proxy
}