深入浅出:从JS的new运算符到手写ES5/ES6版实现
一、JS中的new运算符本质
当我们使用new Person('awei',20)
时,JS引擎会默默完成以下关键步骤:
javascript
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayName = function() {
console.log(this.name)
}
const awei = new Person('awei', 20)
执行流程解析:
-
创建纯净对象 :在堆内存中开辟新空间,生成空对象
{}
-
建立原型链 :将空对象的
__proto__
指向构造函数的prototype
-
绑定执行上下文 :将构造函数内部的
this
指向这个新对象 -
执行构造函数 :相当于执行
Person.call(obj, 'awei', 20)
-
返回结果处理:
- 构造函数无返回值时自动返回新对象
- 若返回非对象类型则仍返回新对象
- 若返回对象则直接使用该返回值
二、ES5环境手写new实现
ini
function objectFactory() {
const obj = new Object();
const Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
const ret = Constructor.apply(obj, arguments);
return typeof ret === 'object' ? ret : obj;
}
// 测试用例
let awei = objectFactory(Person, 'awei', 20)
实现要点:
-
通过
arguments
处理参数(类数组转数组) -
使用
shift
方法分离构造函数 -
显式设置原型链(比JSON创建对象更灵活)
-
处理构造函数返回值:
- 返回对象时优先采用
- 非对象类型返回新实例
三、ES6环境优化版实现
javascript
function objectFactory(fn, ...args) {
const obj = {};
obj.__proto__ = fn.prototype;
const ret = fn.apply(obj, args);
return ret instanceof Object ? ret : obj;
}
// 支持特殊返回值
function Person() {
return { tag: 'custom object' }
}
const instance = objectFactory(Person)
升级优化点:
- 参数处理:使用剩余参数
...args
替代arguments - 空对象创建:对象字面量
{}
更高效 - 返回值判断:
instanceof
检测更严谨 - 支持箭头函数:通过原型链动态绑定
四、核心差异对比
特性 | ES5实现 | ES6实现 |
---|---|---|
参数处理 | arguments+shift | 剩余参数...args |
空对象创建 | new Object() | 对象字面量{} |
原型设置 | 显式__proto__赋值 | 同左 |
返回值判断 | typeof检测 | instanceof检测 |
构造函数类型支持 | 仅标准函数 | 支持箭头函数(需特殊处理) |
特殊场景处理:
- 当构造函数返回
null
时,typeof null === 'object'
但null instanceof Object === false
- 箭头函数作为构造函数时需配合
Object.setPrototypeOf
处理 - 需要兼容
Symbol
、BigInt
等基本类型返回值时需扩展类型判断
理解new运算符的底层实现,不仅能帮助开发者应对面试考点,更能深入理解原型链工作机制。当我们需要创建具有特殊初始化逻辑的对象,或需要自定义对象创建过程时,手写new的实现方式将展现出强大的灵活性。