面试题:模拟实现Javascript的new运算符

300 阅读2分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。

使用new运算符发生了什么

《Javascript权威指南》中的定义:new 运算符创建并初始化一个新对象。关键字 new 后跟随一个函数调用。这里的函数称为构造函数(constructor),构造函数初始化一个新创建的对象。

MDN new 运算符

也就是说 new 的作用其实就是根据构造函数创建一个新的对象,如果要模拟 new 需要了解一下这个新建的对象有些什么性质,并且和他的构造函数有什么关系

举个栗子

  • 创建一个构造函数 Foo,用 new Foo() 创建一个实例 f1
var Foo = function(name) {
  this.name = name
}
Foo.prototype.say = function() {}
var f1 = new Foo('味精王') // 实例
  • 实例中没有显式的 returnnew 之后会自动返回一个对象
  • 查看实例 f1 上的属性

图片.png

  • 实例对象的 constructor 指向该对象的构造函数
  • 实例对象的属性 __proto__ 指向该对象的构造函数的原型对象 prototype,并且继承原型属性上的方法

图片.png

关于 __proto__prototype 祭出一张神图 prototype__proto__.png

再举个显示 return 的栗子

var Foo1 = function(name) {
  this.name = name
  return 'I am Shane'
}
var Foo2 = function(name) {
  this.name = name
  return {
    age: 18
  }
}
var r1 = new Foo1('味精王')
var r2 = new Foo2('味精王')
  • 如果有显式的 return 并且是一个对象类型则会返回这个 return 的对象,否则都会无视这个 return 还是返回之前新建的对象 图片.png

小结

  1. 创建一个空对象
  2. 将空对象的 __proto__ 属性指向构造函数的 prototype,因此实例对象可以共享原型上的属性和方法
  3. 改变 this 的指向,指向这个空对象
  4. 判断构造函数的返回值,
    • 如果没有返回,则返回 this
    • 如果有返回先判断 对象类型则返回该对象,否则 返回this

模拟实现new运算符

知道了 new 的作用后就可以模拟来实现一下,这里模拟用的是一个函数,并加上了注释

关键方法 Object.create()

function newOperator(ctor) {
  // 需要传入一个构造函数
  if (typeof ctor !== 'function') {
    throw 'newOperator function the first param must be a function'
  }
  // 创建一个新对象继承构造函数的原型
  var newObj = Object.create(ctor.prototype)
  // 获取第一个参数后面的参数
  var argsArr = [].slice.call(arguments, 1)
  // 改变this指向,并传入后面的参数,并执行构造函数
  var ctorReturnResult = ctor.apply(newObj, argsArr)
  // 判断是否有手动的返回值
  var isObject = typeof ctorReturnResult === 'object' && ctorReturnResult !== null
  var isFunction = typeof ctorReturnResult === 'function'
  if (isObject || isFunction) {
    return ctorReturnResult
  }
  // 返回新建的对象
  return newObj;
}
  • 验证效果:还是使用之前的例子
var f1 = newOperator(Foo, '味精王')
var r1 = newOperator(Foo1, '味精王')
var r2 = newOperator(Foo2, '味精王')
  • 达到了new的效果 图片.png

至此,就完成了 new 运算符的模拟,谢谢您的阅读。