模拟实现js的new

new是什么

一句话介绍newnew运算符创建一个用户自定义的对象类型的实例,或者具有构造函数的内置对象类型之一。看下下面的代码来了解new操作符都做了什么事情

// Class (constructor)
function Person(name,age){
    this.name = name
    this.age = age
    this.habit = 'watch tv'
}

// 每个函数都有prototype对象属性
// 在类的原型上挂载属性和方法,挂载载原型上,每个实例都可以调用,并且不会每个实例都挂载相同的属性和方法
Person.prototype.strLength = 60
Person.prototype.sayName = function(){
    console.log('I am '+ this.name)
}

// 实例化对象
const person = new Person("yato", 50)
person.sayName();
console.log(person)

进一步理解new

从上面这个例子中,我们可以看到,实例person可以

  • 访问到Person构造函数里的属性
  • 访问到Person.prototype中的属性

接下来模拟实现一个类似new的newFake,使用方式如下

function Person(arguments){
    // ...
}

// 使用new
let person = new Person(arguments)

// 使用newFake
let person = newFake(Person,arguments)

初步实现

function newFake(){
    let obj = Object.create({})

    let Constructor = [].shift.call(arguments)

    if(typeof Constructor !== 'function'){
        throw 'newOperator function the first param must be a function';
    }
        
    // 将新建对象的[[prototype]]属性指向到构造函数的prototype属性
    obj.__proto__ = Constructor.prototype

    // 修改this指向到obj
    Constructor.apply(obj, arguments)

    return obj
}

function Person(name,age){
    this.name = name
    this.age = age
    this.habit = 'watch tv'
}

// 每个函数都有prototype对象属性
// 在类的原型上挂载属性和方法,挂载载原型上,每个实例都可以调用,并且不会每个实例都挂载相同的属性和方法
Person.prototype.strLength = 60
Person.prototype.sayName = function(){
    console.log('I am '+ this.name)
}

// 实例化对象
const person = newFake(Person,"yato", 50)
person.sayName();  // I am yato
console.log(person)  // Person { name: 'yato', age: 50, habit: 'watch tv' }

返回值处理

如果构造函数有返回值的情况

function Person(name,age){
    this.strLength = 60
    this.age = age
    
    return {
 	name: name,
        habit: 'game'
    }
}

let person = new Person('yato', 18)

console.log(person.name) // yato
console.log(person.habit) // game
console.log(person.strength) // undefined
console.log(person.age) // undefined

在这个例子中,构造函数返回了一个对象,在实例person中只能访问返回对象中的属性,而且还要注意一点,这里我们是返回一个对象,假设我们只返回一个基本类型值呢,看下面的例子

function Person(name,age){
    this.strLength = 60
    this.age = age
    
    return 'good job'
}

let person = new Person('yato', 18)

console.log(person.name) // undefined
console.log(person.habit) // undefined
console.log(person.strength) // 60
console.log(person.age) // 18

结果和正常new实例,无返回值的时候表现是一样的!可以得出结论:new操作最后一步,需要判断一返回值是否是一个对象,如果是一个对象就返回这个对象,如果不是对象就返回我们new内部的实例对象

第二版的new实现

function newFake(){
    let obj = Object.create({})

    let Constructor = [].shift.call(arguments)

    if(typeof Constructor !== 'function'){
        throw 'newOperator function the first param must be a function';
    }
    
    // ES6 new.target 是指向构造函数
    newFake.target = Constructor;
        
    // 将新建对象的[[prototype]]属性指向到构造函数的prototype属性
    obj.__proto__ = Constructor.prototype

    // 修改this指向到obj
    let ret = Constructor.apply(obj, arguments)
    
    // 判断返回值是否为对象 Object(包含Functoin, Array, Date, RegExg, Error)都会直接返回这些值。
    // 这些类型中合并起来只有Object和Function两种类型 typeof null 也是'object'所以要不等于null,排除	 // null
    let isObject = typeof ret === 'object' && ret !== null;
    let isFunction = typeof ret === 'function';

    if(isObject || isFunction){
        return ret;
    }
    
    return obj
}

总结一下new做了什么

  • 创建了一个全新的对象
  • 这个对象会被执行实例[[prototype]]属性Classprototype对象属性的链接(原型链)
  • 生成的新对象会成为构造函数的调用的this(修改this指向)
  • 通过new创建的每个实例对象最终将被[[prototype]]链接到构造函数的prototype对象上
  • 如果函数没有返回对象类型(包含Function, Array,Date,RegExg,Error),那么new表达式中的函数会自动返回这个新的对象
posted @ 2020-08-11 14:42  yatolk  阅读(299)  评论(0编辑  收藏  举报