彻底捋清楚 new 的实现(让小白也能懂)

566
原文链接: github.com

先给出最终版,后面会解析难点

实现

  1. 声明一个中间对象
  2. 将该中间对象的原型指向构造函数的原型
  3. 将构造函数的this,指向该中间对象
  4. 返回该中间对象,即返回实例对象
function newF() {
  // 创建一个新的对象
  let obj = {};
  // 取出第一个参数,该参数就是我们将会传入的构造函数,比如在调用new(P)的时候,Constructor就是P本身
  // arguments会被shift去除第一个参数,剩余的就是构造器P的参数
  let Constructor = [].shift.call(arguments);
  // 将obj的原型指向构造函数,此时obj可以访问构造函数原型中的属性
  obj.__proto__ = Constructor.prototype;
  // 改变构造函数的this的指向,使其指向obj, 此时obj也可以访问构造函数中的属性了
  let result = Constructor.apply(obj, arguments);
  // 确保 new 出来的是个对象 返回的值是什么就return什么
  return typeof result === 'object' ? result : obj 
}

难点

其实这短短的几行代码里面,浓缩了几个知识点,请先自行查阅

  • 如何绑定this, call、apply使用
  • arguments使用
  • 原型链基础

这两行代码,很多同学会在这里懵逼

let Constructor = [].shift.call(arguments);
let result = Constructor.apply(obj, arguments);

复制如下代码到控制台打印一下

function P(firstName, lastName) {
  this.age = 10;
  this.getName = function() {
    return `${firstName} ${lastName}`;
  };
}

function newF() {
  let obj = new Object();
  console.log('刚开始时的arguments', arguments);
  let Constructor = [].shift.call(arguments);
  console.log('被shift后的arguments', arguments);
  console.log('- - - - -- - - -- -- - - ');
  console.log('Constructor', Constructor);
  console.log('- - - - -- - - -- -- - - ');
  obj.__proto__ = Constructor.prototype;
  let result = Constructor.apply(obj, arguments);
  console.log('绑定this时的arguments', arguments)
}

let p = newF(P, 'amanda', 'kelake');

36a384f9-b811-4dad-be39-cb2e54066878

如图,刚开始时传入的arguments代表的是传入newF的参数,第一个参数arguments[0]自然就是传入的构造器P

let Constructor = [].shift.call(arguments);

shift后(直接从数组里面删除元素),构造器P被拿出,arguments这时候代表的就是构造器P所需要的传入参数firstName, lastName
然后把构造器P的this指向将要return的新实例对象,并把剩余参数传入

let result = Constructor.apply(obj, arguments);

使用

function P(firstName, lastName) {
  this.age = 10;
  this.getName = function() {
    return `${firstName} ${lastName}`;
  };
}

function newF() {
  let obj = new Object();
  let Constructor = [].shift.call(arguments);
  obj.__proto__ = Constructor.prototype;
  let result = Constructor.apply(obj, arguments);
  return typeof result === 'object' ? result : obj 
}

let p = newF(P, 'amanda', 'kelake');
p.getName();
// "amanda kelake"

后记

感谢您耐心看到这里,希望有所收获!

如果不是很忙的话,麻烦点个star⭐【Github博客传送门】,举手之劳,却是对作者莫大的鼓励。

我在学习过程中喜欢做记录,分享的是自己在前端之路上的一些积累和思考,希望能跟大家一起交流与进步,更多文章请看【amandakelake的Github博客】