Bootstrap

new的实现原理

new的实现原理

1. new 的原理

JavaScript 中 new 关键词的作用就是执行构造函数,然后返回一个实例对象;在 new 的过程中,根据构造函数的定义,来确定是否需要传递参数。

function Dog(name) {
  this.name = name;
  this.color = '黑色';
}
const d = new Dog('小黑');
console.log(d.color); // 黑色
console.log(d.name); // 小黑

这段代码很容易理解,d 是通过 Dog 这个构造函数生成的一个实例对象。
再来看看一个示列,把上面示列的 new 关键词去掉

function Dog(name) {
  this.name = name;
  this.color = '黑色';
}
const p = Dog('小黑');
console.log(color); // 黑色
console.log(name); // 小黑
console.log(d); // undefined
console.log(d.color); // TypeError: Cannot read property 'color' of undefined
console.log(d.name); // TypeError: Cannot read property 'name' of undefined

从上面代码中可以看到,去掉 new 关键词后,执行 Dog 函数返回的是 undefind。在上面的代码中,this 的指向是全局的 window 对象,所以执行 Dog 其实是给全局环境的 name 和 color 属性赋值了。

那么 new 在生成实例对象过程中到底执行了哪些步骤呢?

总结下来,执行的步骤大致分为以下几步:

创建一个新对象
将构造函数的作用域赋值给新对象(this 指向新创建的对象)
执行构造函数的代码(给新对象添加属性)
返回新对象

构造函数中有 return

在日常写代码中,我们基本上不会在构造函数中加 return;那么如果构造函数有 return,结果会怎么样呢?

function Dog(name) {
  this.name = name;
  return {
    color: '黑色'
  };
}
const d = new Dog('小黑');
console.log(d); // { color: '黑色' }
console.log(d.name); // undefined
console.log(d.color); // 黑色

通过这段代码我们可以看出,当构造函数 return 出来的是一个对象时,new 命令会直接返回构造函数 return 的对象,而不是通过 new 执行生成的与构造函数相关的新对象。
如果构造函数返回的不是对象,那么结果又会是怎样呢?

function Dog(name) {
  this.name = name;
  return '黑色';
}
const d = new Dog('小黑');
console.log(d); // { name: '小黑' }
console.log(d.name); // 小黑

可以看出,当构造函数 return 的不是对象,结果还是会根据 new 关键词的逻辑执行,返回构造函数的实例对象。
总结:执行 new 关键词后,总是得到一个对象,要么是构造函数的实例对象,要么是构造函数 return 语句指定的对象

new 的实现

new 的实现过程中,要注意以下两点:

实例对象可以访问私有属性
实例对象可以访问构造函数原型(constructor.prototype)所在原型链上的属性

const _new = (fn, ...rest) => {
  const nObj = Object.create(fn.prototype);
  const result = fn.call(nObj, ...rest);
  return (typeof result === 'object') ? result : nObj;
};

函数 Object.create(proto,[propertiesObject]) 的作用
Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 proto
proto

必须,新创建的对象的原型对象;该参数会被赋值给新对象的 proto

propertiesObject

可选。需要传入一个对象,该对象的属性类型参照 Object.defineProperties() 的第二个参数。传入的对象添加到新创建对象的可枚举属性(即其自身的属性,而不是原型链上的枚举属性)对象的属性描述符以及相应的属性名称。

const _new = (fn, ...rest) => {
  const nObj = Object.create(fn.prototype);
  const result = fn.call(nObj, ...rest);
  return (typeof result === 'object') ? result : nObj;
};

function Dog(name) {
  this.name = name;
  this.color = '黑色';
}
const d = _new(Dog, '小黑');
console.log(d.color); // 黑色
console.log(d.name); // 小黑
;