如何检查变量是否是ES6类声明?

时间:2015-06-10 14:14:56

标签: javascript class prototype ecmascript-6

我从一个模块导出以下ES6类:

export class Thingy {
  hello() {
    console.log("A");
  }

  world() {
    console.log("B");
  }
}

从另一个模块导入它:

import {Thingy} from "thingy";

if (isClass(Thingy)) {
  // Do something...
}

如何检查变量是否为类?不是类实例,而是类声明

换句话说,我将如何在上面的示例中实现isClass函数?

6 个答案:

答案 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不理解但愿意与开发人员一起去的一切都是对象,例如

  • 布尔值可以是对象(如果使用 new 关键字定义)
  • 数字可以是对象(如果用 new 关键字定义)
  • 字符串可以是对象(如果用 new 关键字定义)
  • 日期总是对象
  • 数学总是对象
  • 正则表达式总是对象
  • 数组总是对象
  • 函数总是对象
  • 对象始终是对象

那么类到底是什么? 重要 类是用于创建对象的模板,在这一点上它们不是对象。 当您在某处创建类的实例时,它们成为对象,该实例被视为对象。 所以不要害怕,我们需要筛选

  • 我们正在处理哪种类型的对象
  • 然后我们需要筛选出它的属性。
  • 函数始终是对象,它们始终具有原型和参数属性。
  • 箭头函数实际上是老式函数的糖衣,没有这个或更多简单返回上下文的概念,因此即使您尝试定义它们也没有原型或参数。
  • 类是可能函数的蓝图,没有参数属性但有原型。这些原型在实例上成为事后对象。

所以我试图捕获并记录我们检查的每次迭代和结果。

希望能帮到你

'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 修复了两个误报:

  1. 它适用于在类主体之前没有空格的匿名类:
    • isClass(class{}) // true
  2. 它适用于本机类:
    • 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;
}