面试官:你能讲讲new对象过程中发生了什么?

432 阅读4分钟

这是我参与更文挑战的第16天,活动详情查看: 更文挑战

写在前面

很多时候,我们想要创建一个实例对象的时候,就会用到new这个操作符。 但是我们很多时候并不知道它的内部具体做了什么。而且,这也是一个面试高频题,面试的时候面试官经常问能说说new的过程中发生了什么等等。今天,就带你们一起来学学new的过程。

例子

既然我们要知道它的内部做了什么,我们先通过几个例子来看看,通过例子来分析分析。

后退,我要开始秀代码了!!!

// 构造函数
function Dog (name) {
   this.name= name
   this.getName = function () {
      return this.name
   }
}
Dog.prototype.bark = function () {
 return `汪汪`
}
// new实例
var dogA = new Dog('旺财')
console.log(typeof dogA) // 'object'
console.log(dogA.name) // '旺财'
console.log(dogA.getName()) // '旺财'
console.log(dogA.bark()) // 汪汪

image.png

从上面打印的结果我们可以看到,实例后的dogA是一个对象,具有name属性和getName函数,并且继承了Dog构造函数prototype上的bark方法。

我们可以得出结论:

  1. new内部会创建一个对象
  2. 构造函数里的this指向这个对象
  3. 继承了构造函数prototype上面的方法

等等,这样就结束了吗?

没有,没有,这里我们还忽略了一种情况, 上面我们的Dog函数里没有写return语句。所以,new一个实例如果构造函数没有return,则会默认返回创建的对象。

那如果有return语句,情况还是一样的吗?

后退,我又要开始秀代码了!!!

// 构造函数
function Dog (name) {
   this.name= name
   return '我是构造函数Dog'
}
// new实例
var dogB = new Dog('旺财')
console.log(dogB) // Dog {name: "旺财"}

image.png

从上面打印的结果我们可以看到,虽然我在构造函数里面写了return一个字符串,但是结果还是打印了dogB实例

再看个例子,我又又又要秀代码了!!!

// 构造函数
function Dog (name) {
   this.name= name
   return {name: '我是个普通对象'}
}
// new实例
var dogC = new Dog('旺财')
console.log(dogC) // {name: '我是个普通对象'}

image.png

从上面打印的结果我们可以看到,我在构造函数里面写了return一个新对象,结果打印了新的对象。

所以构造函数里return什么类型的值是有区别的,如果你return了新对象,则会返回新对象,其它情况则会返回实例对象。

总结一下过程:

  1. new内部会创建一个对象
  2. 构造函数里的this指向这个对象
  3. 继承了构造函数prototype上面的方法
  4. 如果构造函数返回的是对象,返回会是这个对象,其它情况会返回实例对象

如果我们要通过代码实现这个过程,该怎么写呢?

代码实现new的过程

依照上面的过程,我们可以这样实现:

function NewFn (fn, ...args) {
   const obj = Object.create(fn.prototype)
   const res = fn.apply(obj, args)
   return res instanceof Object ? res : obj 
}

解释:

  1. 先通过Object.create创建新对象,并且把fn.prototype赋给obj的原型上,这样就可以继承fn的原型上的方法
  2. 接着执行fn函数,使用apply方法把fnthis指向obj
  3. 最后根据第二步函数执行返回的res,判断如果resObject的实例,则返回res;否则返回obj

下面我们用上面例子验证一下:

// 构造函数
function Dog (name) {
   this.name= name
   this.getName = function () {
      return this.name
   }
}
Dog.prototype.bark = function () {
 return `汪汪`
}
function NewFn (fn, ...args) {
   const obj = Object.create(fn.prototype)
   const res = fn.apply(obj, args)
   return res instanceof Object ? res : obj 
}
var dogA = NewFn(Dog, '旺财')
console.log(typeof dogA) // 'object'
console.log(dogA.name) // '旺财'
console.log(dogA.getName()) // '旺财'
console.log(dogA.bark()) // 汪汪
function Dog (name) {
   this.name= name
   return '我是构造函数Dog'
}
// new实例
var dogB = NewFn(Dog, '旺财')
console.log(dogB) // Dog {name: "旺财"}
// 构造函数
function Dog (name) {
   this.name= name
   return {name: '我是个普通对象'}
}
// new实例
var dogC = NewFn(Dog, '旺财')
console.log(dogC) // {name: '我是个普通对象'}

运行通过,完美!

总结

总结一下,new对象过程中发生了以下过程:

  1. new内部会创建一个对象
  2. 构造函数里的this指向这个对象
  3. 继承了构造函数prototype上面的方法
  4. 如果构造函数返回的是对象,返回会是这个对象,其它情况会返回实例对象。

希望对你们有帮助~