javascript之new的模拟实现

110 阅读2分钟

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一。

在模拟之前我们得知道new一个构造函数的时候,发生了什么?

  1. 创建一个空对象,将它的引用赋值给this,继承函数的原型
  2. 通过this 将属性和方法添加至这个对象
  3. 最后返回this指向的新对象,也就是实例(如果构造器没有手动返回对象,则返回第一步创建的新对象,如果有,则舍弃掉第一步创建的新对象,返回手动return的对象。)

根据以上三点我们先来看下第三点构造函数没有返回值的情况

function bar(name, age) {
    this.name = name
    this.age = age
}

bar.prototype.sex = '男'
bar.prototype.sayHello = function() {
    console.log('hello, I am ' + this.name)
}

var test = new bar('李四', 18)

console.log(test.name) // 李四
console.log(test.age) // 18
console.log(test.sex) // 男
test.sayHello() // hello, I am 李四

接下来我们来模拟一下吧。 由于new是关键字,无法像call等函数一样直接覆盖,所以我们写一个函数newMethod来模拟new 的效果:

function newMethod(context, ...args) {
    // 1. 创建一个空对象
    var obj = Object.create(null)
    // 或使用 obj = {}
    // 2. 将属性和方法添加至这个对象
    Object.setPrototypeOf(obj, context.prototype)
    context.apply(obj, args)
    return obj
}

接下来我们再来看看构造函数有返回值的情况

function bar(name, age) {
    this.sex = '女'
    this.age = age
    return {
        name: name,
        habit:'读书'
    }
}

bar.prototype.sayHello = function() {
    console.log('hello, I am ' + this.name)
}

var test = new bar('李四', 18)

console.log(test.name) // 李四
console.log(test.age) // undefined
console.log(test.sex) // undefined
console.log(test.habit) // 读书
test.sayHello()  // error
// 返回数组
function bar(name, age) {
    this.sex = '女'
    this.age = age
    return [1,2,3]
}

bar.prototype.sayHello = function() {
    console.log('hello, I am ' + this.name)
}

var test = new bar('李四', 18)

console.log(test.name) // undefined
console.log(test.age) // undefined
console.log(test.sex) // undefined
console.log(test[0]) // 1
console.log(test[1]) // 2
console.log(test[2]) // 3
test.sayHello()  // error

在例子中构造函数返回了一个对象(数组也是对象类型), 在实例test中只能访问对象中的属性。 如果我们返回的是字符串,结果又是怎样的呢? 我们来看看:

// 返回字符串
function bar(name, age) {
    this.sex = '女'
    this.age = age
    return this.name
}

bar.prototype.sayHello = function() {
    console.log('hello, I am ' + this.name)
}

var test = new bar('李四', 18)

console.log(test.name) // undefined
console.log(test.age) // 18
console.log(test.sex) // 女
test.sayHello()  // hello, I am undefined

返回字符串的结果和没有返回值是一样的效果。 由此我们可以得到第二版代码:

function newMethod(context, ...args) {
    // 1. 创建一个空对象
    var obj = Object.create(null)
    // 或使用 obj = {}
    // 2. 将属性和方法添加至这个对象
    Object.setPrototypeOf(obj, context.prototype)
    var ret = context.apply(obj, args)
    return typeof ret === 'object' ? ret : obj
}