new关键字(操作符)在底层究竟做了什么?
一、概念理解
函数调用之前带有关键字new,它就构成了构造函数调用。
与普通函数调用在实参处理、调用上下文、返回值方面不同。
一)实参处理
相同点:如果有实参,先计算这些实参表达式,然后传入函数内部。
不同点:如果没有实参,构造函数允许形参,构造函数在调用时,允许省略实参列表和圆括号。
例如:下列两段代码是等价的。
let obj = new Object()
let obj = new Object
二)调用上下文
构造函数创建一个新的对象,并初始化它,并将这个对象作为调用上下文。即this指针指向这个对象
普通函数的this指针则在调用函数时确定,谁调用该函数,this就指向谁。
三)返回值
造函数通常不使用return关键字。它们初始化新对象,并在函数执行完后显式返回。判断构造函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
普通函数如果有return关键字,则返回return关键字后的数据,否则返回undefined。
在js中,数据存储在内存中。而内存分为栈内存和堆内存。
栈内存中包含着Number、String等基本数据类型的数据以及定义变量 。
堆内存中包含着Object等引用数据类型的数据。
每个函数在创建后都自动拥有一个prototype属性,这个属性是一个对象,这个对象中包含唯一一个不可枚举的属性constructor。其值是一个函数对象。
二、new的执行过程
- 首先创建了一个新对象。
- 将空对象的原型 _proto_ 指向构造函数的 prototype 属性。
- 让构造函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)。
- 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
三、手写new
function newFunc(Func,...args) {
// 1.创建一个新对象
let newObj = {}
// 2.将新对象和构造函数通过原型链连接
newObj.__proto__ = Func.prototype
// 3.将构造函数的this绑定到新对象上
const result = Func.apply(newObj,args)
// 4.根据返回值类型判断,如果是值类型返回newObj,如果是引用类型返回正常引用类型
return result instanceof Object ? result : newObj
}