new的过程发生了什么

83 阅读3分钟

简单的new过程

  1. 新建一个对象
  2. 将这个对象的原型指向构造函数的原型
  3. 将this指向这个对象,并且调用构造函数
  4. 将这个新对象返回

话不多说上代码:

function myNew(fn,...arg){
    let obj = {}   //创建新的对象
    obj.__proto__ = fn.prototype  //obj原型指向构造函数原型
    fn.call(obj,...arg) // 将this指向新对象,并调用构造函数
    return obj//返回新对象
}

测试:

function Peson(name,age){
    this.name = name
    this.age = age
    this.sayname = function(){
        console.log(this.name)    
    }
}

let p1 = myNew(Person,'小王'18)
cosole.log(p1)   // {"name":"小王","age":18}

对于返回值的处理

当然,如果构造函数没有返回,我们只需要返回新创建的对象即可,但是如果构造函数有返回值,且返回值为基础类型或者引用类型的情况,我们该怎么处理呢?

首先我们先看看原生new对于这些情况的处理

基础类型

function Peson(name,age){
    this.name = name
    this.age = age
    this.sayname = function(){
        console.log(this.name)    
    }
    return 1 
}

let p2 = new Peson('tom',20)
console.log(p2) //{"name":"tom","age":20}

可以看到基础类型是没有返回的(当然还有其他基础类型,也都是没有返回,包括null了,和undefined,大家可以下去自行尝试)

引用类型

function Peson(name,age){
    this.name = name
    this.age = age
    this.sayname = function(){
        console.log(this.name)    
    }

    return {
      name:'我是返回的对象',
      age: 18
    }
    //return [1,2,3,4]
    //reurn  function fn(){} 
}

let p2 = new Peson('tom',20)
console.log(p2) 

image.png

image.png

image.png

可以看到引用类型都返回来了。

所以我们要进行边界处理,第四步需要对于返回值进行判断

完整的new过程

  1. 新建一个对象
  2. 将这个对象的原型指向构造函数的原型
  3. 将this指向这个对象,并且调用构造函数
  •       如果构造函数有返回值,且此返回值为引用类型,则返回此值

  •       如果构造函数无返回值,则将这个对象返回

function myNew(fn,...arg){
    let obj = {}   //创建新的对象
    obj.__proto__ = fn.prototype  //obj原型指向构造函数原型
    let result = fn.call(obj,...arg) // 将this指向新对象,并调用构造函数
    return  resu instanceof Object ? result : obj //如果构造函数有返回值,且返回值为引用类型,则返回构造函数的返回值,反之返回新创建的对象
}

测试:

function Peson(name,age){
    this.name = name
    this.age = age
    this.sayname = function(){
        console.log(this.name)    
    }

    return {
        name:'我是返回的对象',
        age: 18
    }
    // return [1,2,3,4]
    // return  function fn(){} 
}

let p1 = myNew(Peson,'小王',18)

console.log(p1)

image.png

后续思考

思考到还有一种边界情况,箭头函数不能作为构造函数,因为箭头函数的this不能改变,如果箭头函数作为构造函数会出现报错:

let Peson = (name, age)=>{
        this.name = name
        this.age = age
}

let p2 = new Peson('tom',20)
console.log(p2)

image.png

而此时我们手写的new函数还是会返回一个对象


function myNew(fn,...arg){
    let obj = {}  
    obj.__proto__ = fn.prototype  
    let result = fn.call(obj,...arg) 
    return  result instanceof Object ? result : obj 
}

    let Peson = (name, age)=>{
        this.name = name
        this.age = age
}

let p1 = myNew(Peson,'小王',18)

console.log(p1)

image.png

我看到网上很少对这种情况进行考虑,所以我们还需要加一个新的判断:

因为箭头函数没有prototype属性,所以当fn.prototype为undefined时,抛出错误

let Peson = (name, age)=>{
        this.name = name
        this.age = age
}

function myNew(fn,...arg){
    let obj = {} 
    if(fn.prototype === undefined){
      throw new Error(fn +'is not a constructor')
    } //判断是否是箭头函数
    obj.__proto__ = fn.prototype 
    let result = fn.call(obj,...arg) 
    return  result instanceof Object ? result : obj 
}

let p1 = myNew(Peson,'小王',18)

console.log(p1)

image.png

小结

如有不对,欢迎大家指正,非常感谢