如何简单的封装一个new?

168 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

前言: 在看书的时候我发现关于 new 关键字的知识点在书上讲的有点模糊,当看到 new 这个关键字的时候我脑袋里蹦出的第一个东西就是 new一个对象。但是仔细想想除了这句话我好像也想不出任何一点关于 new 的消息了,于是抱着学习的心态深入的了解了一下 new 这个关键字。

new 做了什么事?

我们都知道new这个关键字是用来构造出一个新的对象,那么在 new 一个新对象的时候 new 这个关键字到底在背后做了一些什么事情?

  1. 声明一个空的对象
  2. 调用构造函数,将这个对象的隐式原型与构造函数的显示原型进行绑定
  3. 将构造函数上面的this绑定到这个对象上面
  4. 返回这个对象

自己封装一个 new

上面我们知道了 new 这个操作符在调用的时候有4步,那么我们按照上面的步骤来手写一个new,一开始没有学习之前心里绝对这个new肯定很难实现,但是了解了之后好像还挺简单的。

// 先自己定义一个构造函数
function Person(name,age){
    this.name=name
    this.age=age
}

function myNew(fn,...args){  // 接收构造函数,还有构造函数的参数
    let obj={}   // 声明一个对象
    obj.__proto__=fn.prototype            // 将这个对象的隐式原型绑定到构造函数的显示原型上
    fn.apply(obj,args)  // 将这个对象的this绑定到这个对象上面,让这个对象具有构造函数身上的属性

    return obj
}
 
console.log( myNew(Person,'bruce',18)); //Person { name: 'bruce', age: 18 }

你看根据new在执行的时候会执行的四步好像自己实现一个new操作符也不是很难理解欸。

当然构造函数也是一个函数,既然是函数就可以有返回值,那么在构造函数里面有返回值结果会是啥?

在构造函数里面有返回值


function Person(name,age){
    this.name=name
    this.age=age
    return 123  //返回数字
}

let p=new Person('bruce',18)
console.log(p); // Person { name: 'bruce', age: 18 }

////
function Person(name,age){
    this.name=name
    this.age=age
    return 'bruec'  //返回字符串
}

let p=new Person('bruce',18)
console.log(p); // Person { name: 'bruce', age: 18 }

在构造函数里返回基本类型,其new 出来还是这个对象。

但是如果返回引用类型呢?


function Person(name,age){
    this.name=name
    this.age=age
    return [1,2,3]  // 返回数组
}

let p=new Person('bruce',18)
console.log(p); // [ 1, 2, 3 ]

////

function Person(name,age){
    this.name=name
    this.age=age
    return {}  // 返回空对象
}

let p=new Person('bruce',18)
console.log(p);  / {}

在构造函数里返回引用类型,其new 出来则是返回的这个引用类型对象。

new 构造函数返回不同类型时有哪些表现? 如果在new出来的对象里再return 一个基本类型,那么这个new出来的对象不会改变,否则就是自己return 出来的对象

所以上面封装的 new 还不够完美,需要判断new 里面会不会有返回值;如果有返回值,那就去判断这个返回值是原始类型还是引用类型,否则直接返回 new 出来的对象。

// 升级后的 new 函数
function myNew(fn,...args){
   let obj={}
   obj.__proto=fn.prototype
   let result= fn.apply(obj,args)  // 如果有返回值

   return typeof result === 'object' && result!=null ? result :obj
   // 上面这句话是一个三元运算符,意思是:如果构造函数里面有返回值并且返回值是对象就返回这个对象,否则返回obj
}