疑问
学习JavaScript时,我一直对new操作符感到困惑。它看起来很神秘,但仔细想想,new其实就是:
- 创建一个新对象
- 给这个对象添加属性(通过构造函数)
- 设置原型链
那如果我不用new,自己手动实现这些步骤,不是更清晰吗?
第一次尝试:最直接的方式
function Foo(name) {
this.name = name;
}
// 不用new,我能创建同样的对象吗?
const obj1 = new Foo('小红'); // 原生方式
// 我的手动实现
const obj2 = {};
obj2.__proto__ = Foo.prototype; // 设置原型链
Foo.call(obj2, '小红'); // 调用函数添加属性
console.log(obj1.name); // 小红
console.log(obj2.name); // 小红
console.log(obj1 instanceof Foo); // true
console.log(obj2 instanceof Foo); // true - 成功了!
发现问题:直接操作proto不好
研究后发现,直接操作__proto__不是最佳实践:
// ❌ 不太好的方式
obj2.__proto__ = Foo.prototype;
// ✅ 更好的方式
const obj2 = Object.create(Foo.prototype);
Foo.call(obj2, '小红');
Object.create(Foo.prototype)的作用是创建一个新对象,并将该对象的原型链直接指向Foo.prototype
封装成函数:我的第一个myNew
既然思路可行,我就把它封装成函数:
function myNew(constructor, name) {
const obj = Object.create(constructor.prototype);
constructor.call(obj, name);
return obj;
}
const obj3 = myNew(Foo, '小刚');
console.log(obj3 instanceof Foo); // true
遇到问题:参数不灵活
但这样只能传一个参数,我需要支持多个参数:
// 改进:支持多个参数
function myNew(constructor, ...args) {
const obj = Object.create(constructor.prototype);
constructor.apply(obj, args);
return obj;
}
function Person(name, age) {
this.name = name;
this.age = age;
}
const person = myNew(Person, '小明', 18);
console.log(person.age); // 18 - 完美!
边界情况:构造函数可能返回对象
通常我们不会用有return的函数作为构造函数,但为防止有人误用,JavaScript规定:当构造函数返回 对象类型 时,new操作符直接返回该对象而非新实例;若返回 基本类型,则忽略return值正常创建实例。这是一种有效的防御机制。:
function SpecialFoo(name) {
this.name = name;
return { custom: '特殊对象' }; // 返回对象的情况
}
// 我的最终版本
function myNew(constructor, ...args) {
const obj = Object.create(constructor.prototype);
const result = constructor.apply(obj, args);
// 如果构造函数返回对象,就使用该对象
if (result && (typeof result === 'object' || typeof result === 'function')) {
return result;
}
return obj;
}
const specialObj = myNew(SpecialFoo, '测试');
console.log(specialObj); // { custom: '特殊对象' }
完整测试
function Foo(name) {
this.name = name;
}
Foo.prototype.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
// 对比测试
const obj1 = new Foo('原生new');
const obj2 = myNew(Foo, '手写new');
obj1.sayHello(); // Hello, 原生new
obj2.sayHello(); // Hello, 手写new
console.log(obj1 instanceof Foo); // true
console.log(obj2 instanceof Foo); // true
总结
new其实是语法糖- 原型链的本质就是对象的
__proto__指向构造函数的prototype - 构造函数的
this就是新创建的对象 - 其实
new就是帮我们干活而已