39

I am exporting the following ES6 class from one module:

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

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

And importing it from another module:

import {Thingy} from "thingy";

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

How can I check whether a variable is a class? Not a class instance, but a class declaration?

In other words, how would I implement the isClass function in the example above?

XåpplI'-I0llwlg'I -
  • 21,649
  • 28
  • 102
  • 151
  • Happy to elaborate on my answer if you have more specifics of why you need to do this. – loganfsmyth Jun 10 '15 at 14:30
  • 2
    possible duplicate of [How do you check the difference between an ECMAScript 6 class and function?](http://stackoverflow.com/questions/29093396/how-do-you-check-the-difference-between-an-ecmascript-6-class-and-function) – balupton Aug 26 '15 at 19:55

11 Answers11

42

If you want to ensure that the value is not only a function, but really a constructor function for a class, you can convert the function to a string and inspect its representation. The spec dictates the string representation of a class constructor.

function isClass(v) {
  return typeof v === 'function' && /^\s*class\s+/.test(v.toString());
}

Another solution would be to try to call the value as a normal function. Class constructors are not callable as normal functions, but error messages probably vary between browsers:

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;
  }
}

The disadvantage is that invoking the function can have all kinds of unknown side effects...

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 2
    The second part of the statement will fail on some implementations (as the spec isn't very specific on that part). E.g. transpiled ES6 in Babel builds, however in this case you can still check if `class` exists and you can tack on `|| /_class\S+/i.test(v.toString())` to the statement for that. – Spoike Sep 28 '15 at 04:26
  • Interestingly, it seems that in the Node.js REPL, you can distinguish between `class`es and `function ƒ () {}` because `ƒ` will have an `arguments` property, while a `class` doesn't. `() => {}` will not have `prototype`, unlike a `class` or `function`. However, when running script files with Node this doesn't seem to hold… – wprl Jul 17 '16 at 16:04
  • 7
    Don't call the function to test it! What if it has side effects? What if the function is question is `fireNuclearWeapons`? – Stijn de Witt Apr 09 '17 at 19:42
  • Then why provide it as the answer? No need to stringify the error message if you can just stringify the function itself: `fn.toString() // "class ....."` Just check whether it starts with `class`. Problem with it is that it does not work for transpiled functions... You can check for `classCallCheck` like @Spoike mentioned, but it depends on Babel and feels fragile. I was hoping there would be a better way but just calling it isn't better. – Stijn de Witt Apr 09 '17 at 19:47
  • 2
    FYI `class\s+` can fail for minified classes like `var foo=class{};`. – loganfsmyth Feb 09 '18 at 23:08
  • 1
    @wprl the node REPL is not in strict mode. In strict mode, there's no difference. – Joe Hildebrand Feb 10 '21 at 18:38
  • Maybe this: let isClass = (maybeClass + "").trim().match (/^class\b/); – Panu Logic Nov 21 '21 at 21:15
20

I'll make it clear up front here, any arbitrary function can be a constructor. If you are distinguishing between "class" and "function", you are making poor API design choices. If you assume something must be a class for instance, no-one using Babel or Typescript will be be detected as a class because their code will have been converted to a function instead. It means you are mandating that anyone using your codebase must be running in an ES6 environment in general, so your code will be unusable on older environments.

Your options here are limited to implementation-defined behavior. In ES6, once code is parsed and the syntax is processed, there isn't much class-specific behavior left. All you have is a constructor function. Your best choice is to do

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
}

and if someone needs to do a non-constructor function, expose a separate API for that.

Obviously that is not the answer you are looking for, but it's important to make that clear.

As the other answer here mentions, you do have an option because .toString() on functions is required to return a class declaration, e.g.

class Foo {}
Foo.toString() === "class Foo {}" // true

The key thing, however, is that that only applies if it can. It is 100% spec compliant for an implementation to have

class Foo{}
Foo.toString() === "throw SyntaxError();"

No browsers currently do that, but there are several embedded systems that focus on JS programming for instance, and to preserve memory for your program itself, they discard the source code once it has been parsed, meaning they will have no source code to return from .toString() and that is allowed.

Similarly, by using .toString() you are making assumptions about both future-proofing, and general API design. Say you do

const isClass = fn => /^\s*class/.test(fn.toString());

because this relies on string representations, it could easily break.

Take decorators for example:

@decorator class Foo {}
Foo.toString() == ???

Does the .toString() of this include the decorator? What if the decorator itself returns a function instead of a class?

loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
  • 13
    There *is* a class. Regular ES5 constructor functions and ES 6 class constructor functions are not the same. Just try `Thingy()` to see the difference. ES6 class constructor functions will throw an error because they may not be invoked without new. This answer is helpful but it does not answer the original question and should imho not have been accepted. – Stijn de Witt Apr 09 '17 at 13:54
  • I think that's an inaccurate distinction. Certain functions cannot be used _with_ `new` (like arrow functions) and certain functions cannot be used _without_ `new` (like classes). That doesn't make them any more or less a function. Using ES6 `class` syntax is one way to create a function that cannot be created without `new`, but there are others too. Trying to differentiate between subtypes of functions is pretty much always going to be a bad idea because you'll never get it right 100% of the time. – loganfsmyth Apr 09 '17 at 19:09
  • 4
    Let me put it this way... I didn't come to this page to learn about `typeof x == 'function'`... The answer is very unsatisfying imho. – Stijn de Witt Apr 09 '17 at 19:40
  • 1
    I'm just going to note that the the regex tests false with the `\s` before the `class` – Knight Yoshi Mar 01 '19 at 23:53
  • @KnightYoshi Good catch, fixed. – loganfsmyth Mar 02 '19 at 00:06
  • See the MDN description of the "new" operator and the example: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new The Car 'class' in the example is in fact defined as a function, but we all know... it's a class. And a new Car instance is instantiated with the 'new' keyword. So.... class is just function. We cannot reliably distinguish it with other functions, but we can unreliably distinguish it somehow from other functions, like this answer said. So it's a legitimate answer to me. – Bing Ren Jun 12 '19 at 06:50
  • *Should be also detecting if `typeof` is `"object"`, because if you do this: `var cls = new Cls();` and then you do `typeof cls` it'll return `"object"`, but, good anyways! Or if you try `typeof new Cls()` it'll return the same as `typeof cls` returned. – RixTheTyrunt Mar 04 '22 at 15:27
  • It's also important to note that the `.toString()` function can be overridden and therefore cannot be relied upon. – Jonas Tomanga Apr 11 '22 at 18:35
10

Checking the prototype and its writability should allow to determine the type of function without stringifying, calling or instantiating the input.

/**
 * determine if a variable is a class definition or function (and what kind)
 * @revised
 */
function isFunction(x) {
    return typeof x === 'function'
        ? x.prototype
            ? Object.getOwnPropertyDescriptor(x, 'prototype').writable
                ? 'function'
                : 'class'
        : x.constructor.name === 'AsyncFunction'
        ? 'async'
        : 'arrow'
    : '';
}

console.log({
  string: isFunction('foo'), // => ''
  null: isFunction(null), // => ''
  class: isFunction(class C {}), // => 'class'
  function: isFunction(function f() {}), // => 'function'
  arrow: isFunction(() => {}), // => 'arrow'
  async: isFunction(async function () {}) // => 'async'
});
Ian Carter
  • 548
  • 8
  • 7
  • This returns false in my tests: ( function funk (x){ } ) . hasOwnProperty ('arguments'); . So it does not tell me that 'funk' is a plain function as opposed to a class. This happens on Node.js – Panu Logic Nov 21 '21 at 20:56
  • 1
    @PanuLogic - added a fix based on your comment and reading bugzilla.mozilla.org/show_bug.cgi?id=1606421 the `arguments` property seems to be implementation-specific (allthough it works on node>8-18 and chrome) - so by replacing the check for arguments by inspecting the writability of the prototype descriptor allows the differentiation between `function` and `class`. – Ian Carter Oct 07 '22 at 09:42
  • Good to know about the 'writability' -difference. Do you think it is based on the standard, or is it just the way it happens to be with current browser-versions? – Panu Logic Oct 11 '22 at 00:11
5

Such an old question and nearly no answer is correct except for this one and yet there is a caveat there ...

why wrong answers?

Let's start with anyone suggesting to invoke the function ... that's a disaster prone approach that should be removed from suggestions before ChatGPT would even consider that as code to suggest ... next ...

There are JS runtimes where the string representation of the function, or rest of the code, does not exist in production, maybe in debugging mode, but not necessarily in prod, quite the opposite.

This is because some JS runtime can save lot of final "bytecode" size by removing the source from pretty much all of its content.

Accordingly, everyone suggesting any string check doesn't know, or consider, these scenarios, but also any function toString method can be replaced with something else too, making most answers not bullet proof.

why no right answer?

The closest answer is the one checking writable at the prototype descriptor of any function:

  • shorthand methods such as {method(){}} won't have a prototype at all
  • transpiled classes to ES5 functions will likely have it writable unless the transpiler was very focused on this detail, like Babel, resulting in slightly slower runtime too
  • only native ES2015+ code that has not been transpiled will pass all tests: prototype exists and its writable value is exactly false
const isESClass = fn => (
  typeof fn === 'function' &&
  Object.getOwnPropertyDescriptor(
    fn,
    'prototype'
  )?.writable === false
);

It's important to understand this will fail in projects stuck into ES5 transpilation (for whatever reason) but there's fundamentally no way to guarantee a generic function, in the pre-ES2015 world, is a class or isn't, jQuery (among others) used patterns like the following and all cases are allowed:

function jQuery(...args) {
  if (!(this instanceof jQuery))
    return new jQuery(...args);
  // do everything jQuery does
}

Differently from ES2015+ classes, that utility works both as regular function and as new function, so basically there is no correct answer to this question, just a list of compromises and targets to consider.


For specs reader sake:

Andrea Giammarchi
  • 3,038
  • 15
  • 25
4

There are subtle differences between a function and a class, and we can take this advantage to distinguish between them, the following is my implementation:

// is "class" or "function"?
function isClass(obj) {

    // if not a function, return false.
    if (typeof obj !== 'function') return false;

    // ⭐ is a function, has a `prototype`, and can't be deleted!

    // ⭐ although a function's prototype is writable (can be reassigned),
    //   it's not configurable (can't update property flags), so it
    //   will remain writable.
    //
    // ⭐ a class's prototype is non-writable.
    //
    // Table: property flags of function/class prototype
    // ---------------------------------
    //   prototype  write  enum  config
    // ---------------------------------
    //   function     v      .      .
    //   class        .      .      .
    // ---------------------------------
    const descriptor = Object.getOwnPropertyDescriptor(obj, 'prototype');

    // ❗functions like `Promise.resolve` do have NO `prototype`.
    //   (I have no idea why this is happening, sorry.)
    if (!descriptor) return false;

    return !descriptor.writable;
}

Here are some test cases:

class A { }
function F(name) { this.name = name; }

isClass(F),                 // ❌ false
isClass(3),                 // ❌ false
isClass(Promise.resolve),   // ❌ false

isClass(A),                 // ✅ true
isClass(Object),            // ✅ true
lochiwei
  • 1,240
  • 9
  • 16
  • Now I'm curious if property flags in class/function prototype is described on latest ECMA spec – DrSensor Jun 25 '22 at 17:46
  • "(I have no idea why this is happening, sorry.)" ... every method shorthand/cut version has no prototype ... see `{method(){}}` and inspect that object `method` descriptor to realize there's no prototype. This answer kinda nails it except it could use a WeakMap to avoid checking same classes or functions over and over – Andrea Giammarchi Feb 25 '23 at 19:20
0

What about:

function isClass(v) {
   return typeof v === 'function' && v.prototype.constructor === v;
}
Mat
  • 1,022
  • 2
  • 9
  • 24
  • 1
    this answer is WRONG! `(function(){}).constructor` - this returns a function – disjunction Oct 22 '16 at 20:48
  • `(function(){}).constructor === window.Function` is `true`, which is what you would expect: a function is an instance of `Function`. Note that I compare with the value's *prototype* constructor and not the value's constructor. – Mat Oct 24 '16 at 14:00
  • What i meant is that the check you propose doesn't tell a class from a usual function. And that's what I was googling for before finding this answer. Yet now i realize the initial question was just telling a class from an object, which is much easier. https://runkit.com/disjunction/581f5cff1cdd06001402ea5f – disjunction Nov 06 '16 at 16:45
  • 1
    Well, yo do can do `new hello()` in your example. Javascript is such a poor language. – Mat Jan 27 '17 at 09:30
  • 5
    `v.prototype.constructor === v` is true for every non-arrow function. All functions has prototype and all functions prototype has constructor default to the function itself. – A. Matías Quezada Jul 14 '17 at 15:05
  • @A.MatíasQuezada …and all of these can be instantiated with `new`. – Bergi Aug 10 '21 at 15:36
0

This solution fixes two false positives with Felix's answer:

  1. It works with anonymous classes that don't have space before the class body:
    • isClass(class{}) // true
  2. It works with native classes:
    • 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

Shortcomings

Sadly, it still won't work with node built-in (and probably many other platform-specific) classes, e.g.:

const EventEmitter = require('events');
console.log(isClass(EventEmitter));  // `false`, but should be `true` :(
Domi
  • 22,151
  • 15
  • 92
  • 122
0

Late to the party but another approach that would satisfy compilers and intent, if i understood correctly.

function isInheritable(t) {
    try {
        return Boolean(class extends t {
        })
    } catch {
        return false;
    }
}
João Sequeira
  • 157
  • 3
  • 12
-1

Well going through some of the answers and thinks to @Joe Hildebrand for highlighting edge case thus following solution is updated to reflect most tried edge cases. Open to more identification where edge cases may rest.

Key insights: although we are getting into classes but just like pointers and references debate in JS does not confirm to all the qualities of other languages - JS as such does not have classes as we have in other language constructs.

some debate it is sugar coat syntax of function and some argue other wise. I believe classes are still a function underneath but not so much so as sugar coated but more so as something that could be put on steroids. Classes will do what functions can not do or didn't bother upgrading them to do.

So dealing with classes as function for the time being open up another Pandora box. Everything in JS is object and everything JS does not understand but is willing to go along with the developer is an object e.g.

  • Booleans can be objects (if defined with the new keyword)
  • Numbers can be objects (if defined with the new keyword)
  • Strings can be objects (if defined with the new keyword)
  • Dates are always objects
  • Maths are always objects
  • Regular expressions are always objects
  • Arrays are always objects
  • Functions are always objects
  • Objects are always objects

Then what the heck are Classes? Important Classes are a template for creating objects they are not object per say them self at this point. They become object when you create an instance of the class somewhere, that instance is considered an object. So with out freaking out we need to sift out

  • which type of object we are dealing with
  • Then we need to sift out its properties.
  • functions are always objects they will always have prototype and arguments property.
  • arrow function are actually sugar coat of old school function and have no concept of this or more the simple return context so no prototype or arguments even if you attempt at defining them.
  • classes are kind of blue print of possible function dont have arguments property but have prototypes. these prototypes become after the fact object upon instance.

So i have attempted to capture and log each iteration we check and result of course.

Hope this helps

'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'+'-------');
console.log('and DOMI\'s string is class: '+isclass('class Im a class. Do you believe me?')+'\n'+'-------');

shorter cleaner function covering strict mode, es6 modules, null, undefined and what ever not property manipulation on object.

What I have found so far is since from above discussion classes are blue print not as such object on their own before the fact of instance. Thus running toString function would almost always produce class {} output not [object object] after the instance and so on. Once we know what is consistent then simply run regex test to see if result starts with word class.

"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());
console.log(isclass('class Im a class. Do you believe me?'));

Lead from DOMI's disucssion

class A {
static hello (){console.log('hello')}
hello () {console.log('hello there')}
}

A.hello();
B = new A;
B.hello();

console.log('it gets even more funnier it is properties and prototype mashing');  

class C {
  constructor() {
    this.hello = C.hello;
  }
static hello (){console.log('hello')}
}
C.say = ()=>{console.log('I said something')} 
C.prototype.shout = ()=>{console.log('I am shouting')} 

C.hello();
D = new C;
D.hello();
D.say();//would throw error as it is not prototype and is not passed with instance
C.say();//would not throw error since its property not prototype
C.shout();//would throw error as it is prototype and is passed with instance but is completly aloof from property of static 
D.shout();//would not throw error
console.log('its a whole new ball game ctaching these but gassumption is class will always have protoype to be termed as class');
Syed
  • 696
  • 1
  • 5
  • 11
  • ironically in your implementation `isclass(isclass)` returns true. – Joe Hildebrand Feb 09 '21 at 23:53
  • @Joe Hildebrand good identification....sat down to bring it into more clarity and more tight testing of isclass. – Syed Feb 10 '21 at 10:15
  • Getting closer. Checking `types === 'function'` will prevent throwing an error for `isclass()` or `isclass(undefined)`. remove the console.log? Also, it fails for this case: ``` class Hoo { } Object.defineProperty(Hoo, 'arguments', { value: 5 }) ``` – Joe Hildebrand Feb 10 '21 at 18:13
  • It took me a few minutes to figure out what was going on, but functions don't have `arguments` or `caller` properties if you are in strict mode. So putting `use strict` at the top of this or using it with an ES6 module makes it fail. – Joe Hildebrand Feb 10 '21 at 18:29
  • 1
    @Joe Hildebrand check out new function covers almost all of the new test cases and tiny toony compared to previous one. took me a long walk to get to the short solution. thanks for update. I have kept the old update so future readers can gather on discussion as well. – Syed Feb 10 '21 at 19:34
  • Sorry, just thought of another test case: `Bar = new Proxy(Hoo, {}); isclass(Bar)` gives false. – Joe Hildebrand Feb 11 '21 at 22:14
  • Referring back to the section "..what the heck are classes" Classes are big hulk+spider man+superman+all the avengers + floating in memory until instance occurs on them. considering the LHS = RHS logic on instance LHS becomes object from the instance of class on RHS. Thus issclass(LHS) will be false where as isclass(RHS) will be true because we have not messed with RHS. – Syed Feb 12 '21 at 00:05
  • The Proxy by design is not a class it self but function method requiring instance enabling to create a proxy for another object. HOO was class and incidentally created a proxy on HOO it self. Result previous class blueprint floating in memory is sent to garbage collection and happens pretty fast these days. End result new hash object in memory. – Syed Feb 12 '21 at 00:09
  • When I was making my JS framework I was very conscious anyone could do this and whole thing fall apart. Neither wanted to creating huge bloat of encapsulating method but what if someone did this and whole app would fail e.g. keyword $ in Jquery it has reference what if reference is manipulated. – Syed Feb 12 '21 at 00:12
  • So used simply combination of const + object freeze so no accident of HOO = new Proxy(Hoo, {}); would happen. Either ways test stands correct. – Syed Feb 12 '21 at 00:23
  • Just an added note why classes would become such a beast in my opinion is ability to have both static and non static and methods in same class e.g. class abc {static hello(){alert(2)}; hello(){alert(3)};} calling abc.hello() would give 2 while bar = new abc; bar.hello() would give 3. Question how come class became an object with out instance? even then the above test will capture that. – Syed Feb 12 '21 at 00:29
  • 1
    You offer two different implementations, and both won't work correctly. The first one fails in `strict` mode, and the second is a buggy version of another answer; try: `isClass('class I'm a class. Do you believe me?')` – Domi Aug 09 '21 at 07:53
  • @Domi thak you for your insight . first of all input string provided by you needs escaping either as there are I'm would break the string through error and does, so either "class I'm a class. Do you believe me?" or 'class I\'m a class. Do you believe me?' once that is done I would counter argue both of my examples work and i would request you to recheck your observations. First one is actually running under strict mode, and second is short version not as such buggy one ..either ways as I have placed your string as example they are catching it false. kindly review your observation and opnion. – Syed Aug 10 '21 at 14:10
  • as any good practictioner I would like to help out the little escaping or gotchas'you face test case scenario, if you were to play around with strings "class {} is it class" would not be caught by the second example but would be caught correctly by the first example code. have a try? I believe i did give wide narrative on the answer's pretext :) – Syed Aug 10 '21 at 14:29
  • 1
    @Syed You are right, some of my comments were not concrete enough. Here are some snippets that demonstrate false negatives and false positives. You might want to learn more about the basics of types, and how functions work. The entire `arguments` + `properties` check is entirely off, since you are targeting the wrong thing with it. If it was correct, it would still not work in strict mode. For your first solution: `console.log(isclass(class Thisisanullclass { }), isclass({ prototype: 'hi' }))`. --- For your second solution: `console.log(isclass('class {}'), isclass('class' + (() => {})))` – Domi Aug 10 '21 at 14:32
  • isclass(class Thisisanullclass { }) is not class its bloue print infect at this point it is not even an obect on its until its instance is genrated. though a correct blue print syntx second example will not capture it as true. unless it become Thisisanullclass { } object – Syed Aug 10 '21 at 14:40
  • isclass({ prototype: 'hi' }) is not class so point debating on the subject. and second question i had already stated gotchas short version does have its limitations and seond itration will be missed as if taken as blue print it would reject if taken as string it would behave which ever way – Syed Aug 10 '21 at 14:42
  • however { prototype: 'hi' } is the very kind of blunders Js engine would throw at you and becomes hair pulling hard to catach as I had stated in pretext classes do lot more then we think they do they can be run as static and instance aka with prototype all in one code base I would place an example for discussion sake. I dea was or is class yould have prototype hence when object is thrown to engine it assumes key word assumes object would require instance and is part of class. As my examples catch objects and try to make sense { } fools it to flase possitve :( – Syed Aug 10 '21 at 14:54
-2

I'm shocked lodash didnt have the answer. Check this out - Just like Domi I just came up with a solution to fix the glitches. I know its a lot of code but its the most working yet understandable thing I could produce by now. Maybe someone can optimize it by a regex-approach:

function isClass(asset) {

    const string_match = "function";

    const is_fn = !!(typeof asset === string_match);

    if(!is_fn){

        return false;

    }else{

        const has_constructor = is_fn && !!(asset.prototype && asset.prototype.constructor && asset.prototype.constructor === asset);

        const code = !asset.toString ? "" : asset.toString();

        if(has_constructor && !code.startsWith(string_match)){
            return true;
        }

        if(has_constructor && code.startsWith(string_match+"(")){
            return false;
        }

        const [keyword, name] = code.split(" ");

        if(name && name[0] && name[0].toLowerCase && name[0].toLowerCase() != name[0]){
            return true;
        }else{
            return false;
        }

    }

}

Just test it with:

console.log({
    _s:isClass(String),
    _a:isClass(Array),
    _o:isClass(Object),
    _c:isClass(class{}),
    fn:isClass(function(){}),
    fnn:isClass(function namedFunction(){}),
    fnc:isClass(()=>{}),
    n:isClass(null),
    o:isClass({}),
    a:isClass([]),
    s:isClass(""),
    n:isClass(2),
    u:isClass(undefined),
    b:isClass(false),
    pr:isClass(Promise),
    px:isClass(Proxy)
});

Just make sure all classes have a frist uppercased letter.

-4

Maybe this can help

let is_class = (obj) => {
    try {
        new obj();
        return true;
    } catch(e) {
        return false;
    };
};
Tunaki
  • 132,869
  • 46
  • 340
  • 423
aimadnet
  • 440
  • 4
  • 12
  • 7
    add a `typeof obj == 'function'` and remove the `new` and you would have something that gets close... But I would recommend *against* calling the function to try to determine if it's an ES6 class, because 1) it may have side effects (e.g. I wonder whether `triggerNuclearBomb` is an ES6 class... let's call it to find out!) and 2 transpilers may optimize away the error checking for production builds meaning it would not work for transpiled code. – Stijn de Witt Apr 09 '17 at 13:58
  • A perfectly valid constructor may throw an error on a missing parameter, which also would return false. – Eric Haynes Jun 06 '19 at 03:01
  • would seek your review on answer above .... i have brought to light what is being discussed here – Syed Feb 10 '21 at 10:18