yliu

时来天地皆同力,运去英雄不自由

模拟实现new


new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

上面的定义有点抽象,我们来通过一个例子看 new 运算符扮演的角色

function Foo(name, age) {
  this.name = name;
  this.age = age;
}
Foo.prototype.getName = function() {
  return this.name;
};
Foo.prototype.foo = "foo";
var f = new Foo("zhangsan", 18);
f.getName(); // zhangsan
f.foo; // foo

上面通过 new 创建了一个新的对象,并且这个对象通过原型链访问到了原型上的方法和属性,所以 new 运算符就是执行构造函数返回一个实例对象。

new 运算符在执行时执行了下面四步:

  1. 创建一个空的简单 JavaScript 对象(即{});
  2. 链接该对象(即设置该对象的构造函数)到另一个对象 ;
  3. 将步骤 1 新创建的对象作为 this 的上下文 ;
  4. 如果该函数没有返回对象,则返回 this。

实现

根据上面的执行步骤我们来尝试实现下,当然 new 操作符肯定是模拟不了了,下面通过函数的形式展示

function newCall(fn) {
  var args = Array.prototype.slice.call(arguments, 1);
  var obj = {};
  obj.__proto__ = fn.prototype;
  var result = fn.apply(obj, args);
  return result && typeof result === "object" ? result : obj;
}

Ok,到这里就实现了一个模拟 new 的操作,我们测试一下

function Otaku(name, age) {
  this.name = name;
  this.age = age;

  this.habit = "Games";
}

Otaku.prototype.strength = 60;

Otaku.prototype.sayYourName = function() {
  console.log("I am " + this.name);
};

function newCall(fn) {
  var args = Array.prototype.slice.call(arguments, 1);
  var obj = {};
  obj.__proto__ = fn.prototype;
  var result = fn.apply(obj, args);
  return result && typeof result === "object" ? result : obj;
}

var person = newCall(Otaku, "Kevin", "18");

console.log(person.name); // Kevin
console.log(person.habit); // Games
console.log(person.strength); // 60

撒花 ✿✿ ヽ(°▽°)ノ ✿,不过额外提醒一下__proto__是浏览器实现的非标准属性,一般情况下不推荐使用,不过既然是模拟 es5 推荐的Object.setPrototypeOf肯定没办法用了,只能将就着用了