我从一个模块导出以下ES6类:
export class Thingy {
hello() {
console.log("A");
}
world() {
console.log("B");
}
}
从另一个模块导入它:
import {Thingy} from "thingy";
if (isClass(Thingy)) {
// Do something...
}
如何检查变量是否为类?不是类实例,而是类声明?
换句话说,我将如何在上面的示例中实现isClass
函数?
答案 0 :(得分:27)
如果要确保该值不仅是一个函数,而且实际上是一个类的构造函数,您可以将该函数转换为字符串并检查其表示形式。 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;
}
}
缺点是调用该函数可能会产生各种未知的副作用......
答案 1 :(得分:12)
我预先在这里说清楚,任何任意函数都可以是构造函数。如果你要区分" class"和"功能",您正在做出糟糕的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
而不是类?
答案 2 :(得分:1)
也许这可以帮助
let is_class = (obj) => {
try {
new obj();
return true;
} catch(e) {
return false;
};
};
答案 3 :(得分:0)
仔细阅读一些答案,并认为@Joe Hildebrand 突出显示了边缘情况,因此更新了以下解决方案以反映大多数尝试过的边缘情况。对可能存在边缘情况的更多识别持开放态度。
关键见解:虽然我们正在研究类,但就像 JS 中的指针和引用争论一样,并不能证实其他语言的所有品质 - JS 本身没有我们在其他语言结构中所拥有的类。
有些人争论它是函数的糖衣语法,有些人争论其他明智的。我相信类仍然是底层的一个功能,但不像糖衣那么重要,而是更像是可以放在类固醇上的东西。类会做功能不能做或不费心升级它们去做的事情。
所以暂时将类作为函数来处理打开另一个潘多拉盒子。 JS中的一切都是对象,JS不理解但愿意与开发人员一起去的一切都是对象,例如
那么类到底是什么? 重要 类是用于创建对象的模板,在这一点上它们不是对象。 当您在某处创建类的实例时,它们成为对象,该实例被视为对象。 所以不要害怕,我们需要筛选
所以我试图捕获并记录我们检查的每次迭代和结果。
希望能帮到你
'use strict';
var isclass,AA,AAA,BB,BBB,BBBB,DD,DDD,E,F;
isclass=function(a) {
if(/null|undefined/.test(a)) return false;
let types = typeof a;
let props = Object.getOwnPropertyNames(a);
console.log(`type: ${types} props: ${props}`);
return ((!props.includes('arguments') && props.includes('prototype')));}
class A{};
class B{constructor(brand) {
this.carname = brand;}};
function C(){};
function D(a){
this.a = a;};
AA = A;
AAA = new A;
BB = B;
BBB = new B;
BBBB = new B('cheking');
DD = D;
DDD = new D('cheking');
E= (a) => a;
F=class {};
console.log('and A is class: '+isclass(A)+'\n'+'-------');
console.log('and AA as ref to A is class: '+isclass(AA)+'\n'+'-------');
console.log('and AAA instance of is class: '+isclass(AAA)+'\n'+'-------');
console.log('and B with implicit constructor is class: '+isclass(B)+'\n'+'-------');
console.log('and BB as ref to B is class: '+isclass(BB)+'\n'+'-------');
console.log('and BBB as instance of B is class: '+isclass(BBB)+'\n'+'-------');
console.log('and BBBB as instance of B is class: '+isclass(BBBB)+'\n'+'-------');
console.log('and C as function is class: '+isclass(C)+'\n'+'-------');
console.log('and D as function method is class: '+isclass(D)+'\n'+'-------');
console.log('and DD as ref to D is class: '+isclass(DD)+'\n'+'-------');
console.log('and DDD as instance of D is class: '+isclass(DDD)+'\n'+'-------');
console.log('and E as arrow function is class: '+isclass(E)+'\n'+'-------');
console.log('and F as variable class is class: '+isclass(F)+'\n'+'-------');
console.log('and isclass as variable function is class: '+isclass(isclass)+'\n'+'-------');
console.log('and 4 as number is class: '+isclass(4)+'\n'+'-------');
console.log('and 4 as string is class: '+isclass('4')+'\n'+'-------');
更短的更简洁的函数,涵盖了严格模式、es6 模块、null、undefined 以及对象上的任何非属性操作。
到目前为止我所发现的是因为从上面的讨论中,类是蓝图,而不是实例之前的对象。因此,在实例之后运行 toString 函数几乎总是会产生 class {} 输出而不是 [object object] 等等。一旦我们知道什么是一致的,那么只需运行正则表达式测试,看看结果是否以词类开头。
"use strict"
let isclass = a =>{
return (!!a && /^class.*{}/.test(a.toString()))
}
class A {}
class HOO {}
let B=A;
let C=new A;
Object.defineProperty(HOO, 'arguments', {
value: 42,
writable: false
});
console.log(isclass(A));
console.log(isclass(B));
console.log(isclass(C));
console.log(isclass(HOO));
console.log(isclass());
console.log(isclass(null));
console.log(HOO.toString());
//proxiy discussion
console.log(Proxy.toString());
//HOO was class and returned true but if we proxify it has been converted to an object
HOO = new Proxy(HOO, {});
console.log(isclass(HOO));
console.log(HOO.toString());
答案 4 :(得分:0)
此解决方案使用 Felix's answer 修复了两个误报:
isClass(class{}) // true
isClass(Promise) // true
isClass(Proxy) // true
function isClass(value) {
return typeof value === 'function' && (
/^\s*class[^\w]+/.test(value.toString()) ||
// 1. native classes don't have `class` in their name
// 2. However, they are globals and start with a capital letter.
(globalThis[value.name] === value && /^[A-Z]/.test(value.name))
);
}
const A = class{};
class B {}
function f() {}
console.log(isClass(A)); // true
console.log(isClass(B)); // true
console.log(isClass(Promise)); // true
console.log(isClass(Promise.resolve)); // false
console.log(isClass(f)); // false
遗憾的是,它仍然不适用于 node
内置(可能还有许多其他平台特定的)类,例如:
const EventEmitter = require('events');
console.log(isClass(EventEmitter)); // `false`, but should be `true` :(
答案 5 :(得分:-1)
怎么样:
function isClass(v) {
return typeof v === 'function' && v.prototype.constructor === v;
}