我从一个模块导出以下ES6类:
export class Thingy {
hello() {
console.log("A");
}
world() {
console.log("B");
}
}
并从另一个模块导入它:
import {Thingy} from "thingy";
if (isClass(Thingy)) {
// Do something...
}
如何检查变量是否为类?不是类实例,而是类声明?
换句话说,我将如何在上面的例子中实现isClass
函数?
我会在前面说清楚,任何任意函数都可以是构造函数。如果您区分“类”和“功能”,那么您的API设计选择很差。例如,假设某些东西必须是class
,那么使用Babel或Typescript的任何人都将被检测为class
,因为他们的代码将被转换为函数。这意味着您要求使用您的代码库的任何人通常必须在ES6环境中运行,因此您的代码在旧环境中将无法使用。
您在此处的选项仅限于实现定义的行为。在ES6中,一旦解析了代码并处理了语法,就没有太多特定于类的行为。你所拥有的只是一个构造函数。你最好的选择就是做
if (typeof Thingy === 'function'){
// It's a function, so it definitely can't be an instance.
} else {
// It could be anything other than a constructor
}
如果有人需要执行非构造函数,请为其公开单独的API。
显然,这不是您正在寻找的答案,但重要的是要明确这一点。
正如这里提到的另一个答案,你确实有一个选项,因为函数上的.toString()
需要返回一个类声明,例如:
class Foo {}
Foo.toString() === "class Foo {}" // true
然而,关键是,只有在可行的情况下才适用。它是100%规范兼容的实现
class Foo{}
Foo.toString() === "throw SyntaxError();"
目前没有任何浏览器可以做到这一点,但有几个嵌入式系统专注于JS编程,并且为了保存程序本身的内存,它们一旦被解析就会丢弃源代码,这意味着它们没有源代码可以从.toString()
,这是允许的。
同样,通过使用.toString()
,您可以对未来验证和通用API设计进行假设。说你做
const isClass = fn => /^\s*class/.test(fn.toString());
因为这依赖于字符串表示,它很容易破坏。
以装饰者为例:
@decorator class Foo {}
Foo.toString() == ???
这个.toString()
是否包括装饰者?如果装饰器本身返回function
而不是类,该怎么办?
如果要确保该值不仅是一个函数,而且实际上是一个类的构造函数,您可以将该函数转换为字符串并检查其表示形式。 The spec dictates the string representation of a class constructor。
function isClass(v) {
return typeof v === 'function' && /^\s*class\s+/.test(v.toString());
}
另一种解决方案是尝试将值调用为正常函数。类构造函数不能作为普通函数调用,但错误消息可能因浏览器而异:
function isClass(v) {
if (typeof v !== 'function') {
return false;
}
try {
v();
return false;
} catch(error) {
if (/^Class constructor/.test(error.message)) {
return true;
}
return false;
}
}
缺点是调用函数可能会产生各种未知的副作用......
也许这可以帮助
let is_class = (obj) => {
try {
new obj();
return true;
} catch(e) {
return false;
};
};
关于什么:
function isClass(v) {
return typeof v === 'function' && v.prototype.constructor === v;
}