模拟实现new操作符

760 阅读3分钟
// 第一个参数为构造函数: 内部具有this属性的非箭头函数, 
function _new(constructor, ...rest) {
  // 传入Object.create的对象, 最终会放到新对象的__proto__属性上
  var context = Object.create(constructor.prototype);
  // 执行构造函数, 让函数向this这个对象上添加属性, 并且把this从window改为需要return的对象
  var result = constructor.apply(context, rest);
  // new 操作符总是返回一个对象, 如果不是对象会被忽略并返回{}
  return (typeof result === 'object' && result != null) ? result : context
}

function Person(name, age){
  this.name = name;
  this.age = age;
}

var actor = _new(Person, '张三', 28)

new 命令的作用 , 执行构造函数,返回一个实例对象 var obj = new Object()

一、 将一个空对象的 __propto__ 设为构造函数的prototype属性

对象具有一个名叫 __proto__ 的属性, __proto__ 的值为对象

函数具有一个名为 prototype 的属性, prototype 属性的值为对象

=== 符号判断两个对象,结果为true, 说明两个对象指向同一块内存地址, 意味着需要用到 = 赋值

如何完全的克隆 函数.prototype 对象呢?

JavaScript 提供了Object.create()方法,用来满足这种需求。该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性。

var A = {
  print: function () {
    console.log('hello');
  }
};

// 实例对象
var B = Object.create(A);
console.log(B)

新建一个空的构造函数F,然后让F.prototype属性指向参数对象obj,最后返回一个F的实例,从而实现让该实例继承obj的属性。

Object.create = function(){
  function F() {};
    F.prototype = obj;
    return new F();
}

需要实现: return出来的对象与传入的函数使用同一个内存地址

二、 this 指向问题

  1. 不用 new 调用, 内部的 thiswindow 对象;
  2. new 调用, 内部的 thisreturn 出来的那个对象,
var Vehicle = function (){
  this.price = 1000;
};

var v = Vehicle();
v // undefined
price // 1000

这是构造函数,做的事情是向this 这个对象中添加属性;
额外提一点, 构造函数会挂载到 Vehicle.prototype.constructor 属性上, 所以有 Vehicle === Vehicle.prototype.constructor

普通函数(区别于箭头函数,箭头函数没有自己的this,也就不能做为构造函数)内部的 this 是一个对象, 独立调用时指向 window , 作为对象的方法调用时,指向调用对象

Vehicle 内部的 thiswindow 对象;

总结: 需要修改内部的this指向, 从window改到 待 return 出来的对象

最后, return 出来的对象, 需要有函数中设置的属性, 如上面的 price 属性, 如果添加属性呢? 只需要调用它, 让它向this上挂载属性就行

// 第一个参数为构造函数: 内部具有this属性的非箭头函数, 
function _new(constructor, ...rest) {
  // 传入Object.create的对象, 最终会放到新对象的__proto__属性上
  var context = Object.create(constructor.prototype);
  // 执行构造函数, 让函数向this这个对象上添加属性, 并且把this从window改为需要return的对象
  var result = constructor.apply(context, rest);
  // new 操作符总是返回一个对象, 如果不是对象会被忽略并返回{}
  return (typeof result === 'object' && result != null) ? result : context
}

function Person(name, age){
  this.name = name;
  this.age = age;
}

var actor = _new(Person, '张三', 28)

参考:

  1. 网道 - 互联网开发文档