手写new

175 阅读1分钟

前置知识:

  • 实例都有__proto__,它指向父类的prototype;换句话说就是他们俩是指向同一个内存地址的对象,他们的值是完全相等的,就是一个东西;

要手写new首先要知道new的时候都做了些什么:

  1. 创建一个实例对象,并把这个对象的原型指向类的原型;
  2. 执行类方法,同时把方法中的this指向实例对象;
  3. 处理返回结果,当返回结果为原始类型(基本数据类型)时,返回实例,否则返回结果本身。

少废话,看东西!

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
}