new 的原理以及实现

272 阅读2分钟

new的原理以及实现

new 用不用的区别?

只要通过new 操作符来调用,那他就是构造函数。 (其实并不准确,应该是函数的构造调用) 不通过new操作符调用,那他就是普通函数。一般规定构造函数首字母大写

new的作用

实例化构造函数: 这个实例是包含实例成员和原型成员。
也就是实例化构造函数之后,当前对象可以访问构造函数的属性和和原型方法

new的实现

  1. new会返回一个新的对象。所以首先要: 创建一个新对象 var obj = {}
  2. 这个新的对象可以访问到的构造函数的属性和原型方法,所以我们通过setPrototypeOf 将二者联系起来。或者直接用x.__proto = y.prototype也可
  3. 改变当前this的指向,并且传入剩余参数
  4. 判断构造函数返回值是否为对象,如果为对象就使用构造函数返回的值,否则使用 obj,这样就实现了忽略构造函数返回的原始值
function _new (fn, ...args) {
   let obj = {}
   Object.setPrototypeOf(obj, fn.prototype)
   let result = fn.apply(obj, args)
   return result instanceof Object ? result : obj 
}
function Test (name) {
   this.name = name
}
const test = _new (Test, '测试')
console.log('测试', test)

如果考虑到兼容,用兼容性代码实现

function _new () {
   let fn = [].shift.call(arguments)
   let obj = {}
   Object.setPrototypeOf(obj, fn.prototype)
   let result = fn.apply(obj, arguments)
   return result instanceof Object ? result : obj 
}
function Test (name) {
   this.name = name
}
const test = _new (Test, '测试')
console.log('测试', test)

这样就不用传参,不用剩余参数,利用arguments。实现了传参,这里有个疑惑的点,关于arguments

我们来梳理一下

  1. 在使用new的时候,我们需要传递参数,第一个肯定是函数,剩余的是传入的其他参数

关键代码: [].shift.call(arguments)

代码解析

1.1 arguments 是类数组,用于获取传入的参数。

1.2 [].shift.call(arguments) 相当于

Array.prototype.shift.call(arguments),  就是将arguments转化为数组,并且调用shift()方法

1.3 shift()  方法从数组中删除第一个元素,并返回该元素的值。

  • 这块就相当妙了 这是我们使用new ,调用函数,传入的参数 _new (Test, '测试')

刚开始的时候,arguments 获取传入的参数

[Arguments] { '0': [Function: Test], '1': '测试' }

调用

[].shift.call(arguments)

得到第一个参数,[Function: Test]。这个参数就是传入的函数

此时arguments,变为[Arguments] { '0': '测试' }

紧接着,我们要获取第一个参数之外的,所有参数,传递给apply。arguments被shift之后,就剩下了除第一个参数之外的所有剩余参数,所以arguments就是除第一个参数之外的所有剩余参数

fn.apply(obj, arguments)

完美利用shift 和arguments ,对第一个函数参数,和其他普通参数,进行取值