new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一。
new 做了什么
先用一个例子来看看 new 实现了哪些功能
function Person(name,age){this.name = name;this.age = agethis.gender = 'Man'}Person.prototype.height = 210;Person.prototype.sayHi = function(){console.log('Hi '+this.name)}let person = new Person('奥特曼','18')console.log(person.name) // 奥特曼console.log(person.gender) // "Man"console.log(person.height) // 210person.sayHi() // "Hi 奥特曼"
从这个例子中我们可以推断当执行new Person()的时候发生了什么
- 创建一个空对象,创建的对象的 proto 指向 Person 函数的 prototype(实例 person 可以访问到 Person.prototype 里的属性)
- 实例 person 可以访问到 Person 构造函数里的属性
- 指向 Person 函数,其中对 this 赋值就是对这个空对象赋值(将步骤1新创建的对象作为 this 的上下文)
- 如果该函数没有返回对象,则返回 this 。
初步实现
new是一个关键字,在 js 中有特殊的使用方式,所以我们写一个函数命名为 new2 来模拟 new 的效果,用法需要稍稍的做一下改变。用法如下:
function Person(){...}// 使用 newlet person = new Person(...);// 使用 new2let person = new2(fn,...args) //第一个参数为函数,第二个参数为函数内的参数,数量不确定
因为 new 的结果是一个新对象,所以在模拟 new 的时候我们也需要创建一个新对象 obj。然后让 obj 的 __proto__指向构造函数的prototype。接着我们可以使用fn.bind(obj)(...args) 这句代码相当于我们执行 fn,假设我们传的 fn 是People,那我们就会执行这个People,在执行过程中如果有用到 this ,那这个 this 就是我们绑定的 obj。最后我们需要返回这个对象。
function new2(fn,...args){let obj = Object.create(fn.prototype) //obj 的__proto__ 指向 fn 的 prototypefn.bind(obj)(...args)//obj 在被调用的时候等同于执行 fn 函数return obj}
存在 return 或 return 的是引用类型
如果函数里没有 return 或者 return 的是非引用类型(数字、字符串、布尔、undefined、null),那么返回刚刚创建的对象。如果返回的是一个新对象或者一个函数,那么情况就会发生改变。
return 一个对象
function Person(name,age){this.name = name;this.age = agereturn{height:210,gender:'Man'}}let person = new Person('奥特曼','18');console.log(person.name) //undefinedconsole.log(person.gender) //"Man"console.log(person.height) //210console.log(person.age) //undefined
在这个例子中,构造函数返回了一个对象,在实例 person 中只能访问返回的对象中的属性。
return 一个基本类型
function Person(name,age){this.height = 210;this.age = agereturn 'smart boy'}let person = new Person('奥特曼','18');console.log(person.name) // undefinedconsole.log(person.gender) // undefinedconsole.log(person.height) // 210console.log(person.age) // "18"
结果完全颠倒过来,这次尽管有返回值,但是相当于没有返回值进行处理。
最终代码
所以我们还需要判断返回的值是不是一个对象,如果是一个对象,我们就返回这个对象,如果没有,我们该返回什么就返回什么。
function new2(fn,...args){let obj = Object.create(fn.prototype)let ret = fn.bind(obj)(...args)return typeof ret === 'object'?ret:obj;}
