new操作符及其模拟实现

new操作符做了什么

使用new操作符调用函数时,会自动执行下面的操作

  1. 创建一个全新的对象
  2. 新对象执行[[prototype]]连接,即将新对象的__proto__指向调用函数的prototype
  3. 新对象绑定到函数调用的this
  4. 如果函数没有返回其他对象,那么new中的函数调用会自动返回新对象。
1
2
3
4
function foo(a) {
this.a = a
}
var bar = new foo(2)

使用new调用foo(…)时,会构造一个新对象并把它绑定到foo(…)调用中的this上。

new的模拟实现

基于第一部分分析的四个步骤,很容易构造出以下模拟函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//模拟new,第一个参数为new调用的构造函数
function new2() {
//创建一个新对象
var obj = Object.create(Object.prototype) //这里其实可以用new Object(),讲道理 没有new看着更像模拟..

//提取第一个参数作为构造函数。使用shift的原因是,shift会改变原数组方便构造函数调用参数
var Constructor = [].shift.call(arguments)

//执行[[prototype]]连接
obj.__proto__ = Constructor.prototype

//将构造函数的this绑定到新对象
Constructor.apply(obj, arguments);

return obj
}

可以在浏览器中测试一下模拟的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
function person(name, age){
this.name = name
this.age = age
}
person.prototype.school = 'njupt'
person.prototype.sayName = function () {
console.log(`my name is ${this.name}`)
}
var person1 = new2(person, 'izayoih', '21')
console.log(person1.name) // izayoih
console.log(person1.age) // 21
console.log(person1.school) // njupt
person1.sayName() // my name is izayoih

但这种没有考虑返回值的问题比如返回了一个对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
function person(name,age) {
this.age = age
this.hobby = 'Games'
return {
name: name,
school: 'njupt'
}
}
var person1 = new person('izayoih', '21')
console.log(person1.name) // izayoih
console.log(person1.age) // undefined
console.log(person1.school) // njupt
console.log(person1.hobby) // undefined

在实例person1中只能访问返回对象中的属性。

或者只返回基本类型:

1
2
3
4
5
6
7
8
9
10
function person(name,age) {
this.age = age
this.hobby = 'Games'
return 'njupt'
}
var person1 = new person('izayoih', '21')
console.log(person1.name) // izayoih
console.log(person1.age) // undefined
console.log(person1.school) // njupt
console.log(person1.hobby) // undefined

这种情况下返回值正确但没有处理返回值。

总结一下就是判断返回值是否是一个对象,如果是一个对象,那么就返回该对象,如果不是或者没有返回值,那么就和正常情况一样,不处理构造函数的返回值。

1
2
3
4
5
6
7
8
9
10
11
function new2() {
var obj = Object.create(Object.prototype)

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

obj.__proto__ = Constructor.prototype

var retVal = Constructor.apply(obj, arguments)

return typeof retVal === 'object' ? retVal : obj
}

以上为修改之后的模拟new函数。