我知道有很多关于模块模式、原型继承、ES6 类等的问题,并且都有很好的答案。这不是讨论不同的模式或 ES6 类的缺点。 我的问题是关于两种具体方法的区别和优缺点,在现有问题中我找不到明确的答案。
我喜欢模块模式带来的灵活性,并且我完全理解依赖原型链的陷阱、限制和缺点(无论是否使用
class
关键字或带有原型的传统函数)。但是,我希望(有时需要)我创建的对象可以通过
instanceof
“识别”。我知道两种方法结合模块模式(以及进行对象组合的能力),同时将对象的原型与创建它的函数联系起来。
方法一:类似模块模式,但是我们手动设置返回对象的原型
function Bar() {
let privateVar = 1;
const someMethod = () => {
// do something
};
return Object.setPrototypeOf({ someMethod }, Bar.prototype);
}
const b = new Bar() // can omit new as well
console.log(b, b instanceof Bar); // gets a nice "Bar" name in the console + instanceof true
方法2:包装类并在构造函数中分配所有方法
class Foo {
constructor() {
let privateVar = 1
this.someMethod = () => {
// do something
}
}
}
const f = new Foo() // must call with new
console.log(f, f instanceof Foo); // same as Method 1
问题
不。只有您的第一个片段不支持子类化,因为它确实将原型硬编码为
Bar.prototype
。编写此代码的通常方法是仍然在构造函数中使用
this
:function Bar() {
let privateVar = 1;
this.someMethod = () => {
// do something
};
}
如果您想处理呼叫者忘记
new
关键字的情况,有多种方法可以解决此问题:
function Bar() {
if (!(new.target && this instanceof Bar)) throw new Error('Invalid call');
let privateVar = 1;
this.someMethod = () => {
// do something
};
}
function Bar() {
let privateVar = 1;
const someMethod = () => {
// do something
};
if (new.target) {
this.someMethod = someMethod;
} else {
return { __proto__: Bar.prototype, someMethod };
}
}
function Bar() {
const self = new.target ? this : { __proto__: Bar.prototype };
let privateVar = 1;
self.someMethod = () => {
// do something
};
return self;
}
从人们期望在原型上找到属性/方法的意义上来说,它们都是不好的吗?是的,但实际上这不再是普遍预期的。有很多代码在实例上创建函数值自己的属性(使用 ES5 风格、ES6
class
语法或 ES2022 类字段),无论是用于创建绑定方法还是私有状态上的闭包还是其他。
如果您在文档中指出这一点,以及子类应满足的契约期望,将会有所帮助。