new 运算符模拟实现——手写题

147 阅读3分钟

你好,我是南一。这是我在准备面试八股文的笔记,如果有发现错误或者可完善的地方,还请指正,万分感谢🌹

模拟实现new

这里我采用函数,来模拟new运算符

MDN对new操作符的描述

new 关键字会进行如下的操作:

  1. 创建一个空的简单 JavaScript 对象(即{});
  2. 为步骤 1 新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象 ;
  3. 将步骤 1 新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this

据此可以写出第一版New函数

    function Father(name, age) {
      this.name = name;
      this.age = age;
      // return {
      //   a: 'a'
      // }
    }

    function New(...rest) {
      //创建一个新的对象
      const obj = {};
      //获取构造函数
      const ConstructedFunction = rest.shift();
      //这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性。
      obj.__proto__ = ConstructedFunction.prototype;
      //让构造函数的 this 指向这个新的对象, 并拿到执行结果
      const result = ConstructedFunction.apply(obj, rest);
      //返回新对象, 如果构造函数返回一个对象,那new就返回这个对象,否则返回新建的对象
      return result instanceof Object ? result : obj;
    }

    console.log(New(Father, 'baba', 45));
    console.log(new Father('baba', 45));

提出问题

如果第一个参数不是构造函数怎么办呢?

抛出错误处理!!!

  //如果第一个参数不是构造函数 就抛出错误
  if (!(ConstructedFunction instanceof Function)) {
    //抛出错误ConstructedFunction不是一个构造函数
    return new TypeError(`${ConstructedFunction + ''} is not a constructor`)
  }

ES5有个APIObject.create()

用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)

new改进版

function Father(name, age) {
  this.name = name;
  this.age = age;
}
function New(...rest) {
  //获取第一个参数 构造函数
  const ConstructedFunction = rest.shift();
  //如果第一个参数不是构造函数 就抛出错误
  if (!(ConstructedFunction instanceof Function)) {
    //抛出错误ConstructedFunction不是一个构造函数
    return new TypeError(`${ConstructedFunction + ''} is not a constructor`)
  }
  //创建一个新的对象  Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。
  //这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性。
  const obj = Object.create(ConstructedFunction.prototype);
  //让构造函数的 this 指向这个新的对象, 并拿到执行结果
  const result = ConstructedFunction.apply(obj, rest);
  //返回新对象, 如果构造函数返回一个对象,那new就返回这个对象,否则返回新建的对象
  return result instanceof Object ? result : obj;
}
console.log(New(Father, 'baba', 45)); // Father { name: 'baba', age: 45 }

如果面试官不给用ES6扩展符...

利用arguments对象获取参数,ES6不给用,那const也都改成var

function Father(name, age) {
  this.name = name;
  this.age = age;
}
function New() {
  //用Array.prototype.slice.call(arguments),将arguments对象转数组
  var rest = Array.prototype.slice.call(arguments)
  //获取第一个参数 构造函数
  var ConstructedFunction = rest.shift();
  //如果第一个参数不是构造函数 就抛出错误
  if (!(ConstructedFunction instanceof Function)) {
    //抛出错误ConstructedFunction不是一个构造函数
    return new TypeError(`${ConstructedFunction + ''} is not a constructor`)
  }
  //创建一个新的对象  Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。
  //这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性。
  var obj = Object.create(ConstructedFunction.prototype);
  //让构造函数的 this 指向这个新的对象, 并拿到执行结果
  var result = ConstructedFunction.apply(obj, rest);
  //返回新对象, 如果构造函数返回一个对象,那new就返回这个对象,否则返回新建的对象
  return result instanceof Object ? result : obj;
}
console.log(New(Father, 'baba', 45)); // Father { name: 'baba', age: 45 }

参考文章

MDN

面试官问:能否模拟实现JS的new操作符