How do you check the difference between an ECMAScript 6 class and function?

I did some research and found out that the prototype object [spec 19.1.2.16] of ES6 classes seem to be non-writeable, non-enumerable, non-configurable.

Here's a way to check:

class F { }

console.log(Object.getOwnPropertyDescriptor(F, 'prototype'));
// {"value":{},"writable":false,"enumerable":false,"configurable":false

A regular function by default is writeable, non-enumerable, non-configurable.

function G() { }

console.log(Object.getOwnPropertyDescriptor(G, 'prototype'));
// {"value":{},"writable":true,"enumerable":false,"configurable":false}

ES6 Fiddle: http://www.es6fiddle.net/i7d0eyih/

So an ES6 class descriptor will always have those properties set to false and will throw an error if you try to define the descriptors.

// Throws Error
Object.defineProperty(F, 'prototype', {
  writable: true
});

However with a regular function you can still define those descriptors.

// Works
Object.defineProperty(G, 'prototype', {
  writable: false
});

It's not very common that descriptors are modified on regular functions so you can probably use that to check if it's a class or not, but of course this is not a real solution.

@alexpods' method of stringifying the function and checking for the class keyword is probably the the best solution at the moment.


I think the simplest way to check if the function is ES6 class is to check the result of .toString() method. According to the es2015 spec:

The string representation must have the syntax of a FunctionDeclaration FunctionExpression, GeneratorDeclaration, GeneratorExpression, ClassDeclaration, ClassExpression, ArrowFunction, MethodDefinition, or GeneratorMethod depending upon the actual characteristics of the object

So the check function looks pretty simple:

function isClass(func) {
  return typeof func === 'function' 
    && /^class\s/.test(Function.prototype.toString.call(func));
}

Ran some performance benchmarks on the different approaches mentioned in this thread, here is an overview:


Native Class - Props Method (fastest by 56x on large examples, and 15x on trivial examples):

function isNativeClass (thing) {
    return typeof thing === 'function' && thing.hasOwnProperty('prototype') && !thing.hasOwnProperty('arguments')
}

Which works because the following is true:

> Object.getOwnPropertyNames(class A {})
[ 'length', 'name', 'prototype' ]
> Object.getOwnPropertyNames(class A { constructor (a,b) {} })
[ 'length', 'name', 'prototype' ]
> Object.getOwnPropertyNames(class A { constructor (a,b) {} a (b,c) {} })
[ 'length', 'name', 'prototype' ]
> Object.getOwnPropertyNames(function () {})
[ 'length', 'name', 'arguments', 'caller', 'prototype' ]
> Object.getOwnPropertyNames(() => {})
> [ 'length', 'name' ]

Native Class - String Method (faster than regex method by about 10%):

/**
 * Is ES6+ class
 * @param {any} value
 * @returns {boolean}
 */
function isNativeClass (value /* :mixed */ ) /* :boolean */ {
    return typeof value === 'function' && value.toString().indexOf('class') === 0
}

This may also be of use for determining a Conventional Class:

// Character positions
const INDEX_OF_FUNCTION_NAME = 9  // "function X", X is at index 9
const FIRST_UPPERCASE_INDEX_IN_ASCII = 65  // A is at index 65 in ASCII
const LAST_UPPERCASE_INDEX_IN_ASCII = 90   // Z is at index 90 in ASCII

/**
 * Is Conventional Class
 * Looks for function with capital first letter MyClass
 * First letter is the 9th character
 * If changed, isClass must also be updated
 * @param {any} value
 * @returns {boolean}
 */
function isConventionalClass (value /* :any */ ) /* :boolean */ {
    if ( typeof value !== 'function' )  return false
    const c = value.toString().charCodeAt(INDEX_OF_FUNCTION_NAME)
    return c >= FIRST_UPPERCASE_INDEX_IN_ASCII && c <= LAST_UPPERCASE_INDEX_IN_ASCII
}

I'd also recommend checking out my typechecker package which includes the use cases for the above - via the isNativeClass method, isConventionalClass method, and a isClass method that checks for both types.


Since the existing answers address this problem from an ES5-environment perspective I thought it might be worth offering an answer from an ES2015+ perspective; the original question doesn’t specify and today many people no longer need to transpile classes, which alters the situation a bit.

In particular I wanted to note that it is possible to definitively answer the question "can this value be constructed?" Admittedly, that’s not usually useful on its own; the same fundamental problems continue to exist if you need to know if a value can be called.

Is something constructable?

To start I think we need to clarify some terminology because asking whether a value is a constructor can mean more than one thing:

  1. Literally, does this value have a [[construct]] slot? If it does, it is constructable. If it does not, it is not constructable.
  2. Was this function intended to be constructed? We can produce some negatives: functions that cannot be constructed were not intended to be constructed. But we can’t also say (without resorting to heuristic checks) whether a function which is constructable wasn’t meant to used as a constructor.

What makes 2 unanswerable is that functions created with the function keyword alone are both constructable and callable, but such functions are often intended for only one of these purposes. As some others have mentioned, 2 is also a fishy question — it is akin to asking "what was the author thinking when they wrote this?" I don’t think AI is there yet :) While in a perfect world perhaps all authors would reserve PascalCase for constructors (see balupton’s isConventionalClass function), in practice it would not be unusual to encounter false positives/negatives with this test.

Regarding the first version of this question, yes, we can know if a function is constructable. The obvious thing to do is to try constructing it. This isn’t really acceptable though because we don’t know if doing so would have side effects — it seems like a given that we don’t know anything about the nature of the function, since if we did, we wouldn’t need this check). Fortunately there is a way to construct a constructor without really constructing it:

const isConstructable = fn => {
  try {
    new new Proxy(fn, { construct: () => ({}) });
    return true;
  } catch (err) {
    return false;
  }
};

The construct Proxy handler can override a proxied value’s [[construct]], but it cannot make a non constructable value constructable. So we can "mock instantiate" the input to test whether this fails. Note that the construct trap must return an object.

isConstructable(class {});                      // true
isConstructable(class {}.bind());               // true
isConstructable(function() {});                 // true
isConstructable(function() {}.bind());          // true
isConstructable(() => {});                      // false
isConstructable((() => {}).bind());             // false
isConstructable(async () => {});                // false
isConstructable(async function() {});           // false
isConstructable(function * () {});              // false
isConstructable({ foo() {} }.foo);              // false
isConstructable(URL);                           // true

Notice that arrow functions, async functions, generators and methods are not double-duty in the way "legacy" function declarations and expressions are. These functions are not given a [[construct]] slot (I think not many realize that "shorthand method" syntax is does something — it’s not just sugar).

So to recap, if your question is really "is this constructable," the above is conclusive. Unfortunately nothing else will be.

Is something callable?

We’ll have to clarify the question again, because if we’re being very literal, the following test actually works*:

const isCallable = fn => typeof fn === 'function';

This is because ES does not currently let you create a function without a [[call]] slot (well, bound functions don’t directly have one, but they proxy down to a function that does).

This may seem untrue because constructors created with class syntax throw if you try to call them instead of constructing them. However they are callable — it’s just that their [[call]] slot is defined as a function that throws! Oy.

We can prove this by converting our first function to its mirror image.

// Demonstration only, this function is useless:

const isCallable = fn => {
  try {
    new Proxy(fn, { apply: () => undefined })();
    return true;
  } catch (err) {
    return false;
  }
};

isCallable(() => {});                      // true
isCallable(function() {});                 // true
isCallable(class {});                      // ... true!

Such a function is not helpful, but I wanted to show these results to bring the nature of the problem into focus. The reason we can’t easily check whether a function is "new-only" is that the answer is not modeled in terms of "absence of call" the way "never-new" is modeled in terms of "absence of construct". What we’re interested in knowing is buried in a method we cannot observe except through its evaluation, so all that we can do is use heuristic checks as a proxy for what we really want to know.

Heuristic options

We can begin by narrowing down the cases which are ambiguous. Any function which is not constructable is unambiguously callable in both senses: if typeof fn === 'function' but isConstructable(fn) === false, we have a call-only function such as an arrow, generator, or method.

So the four cases of interest are class {} and function() {} plus the bound forms of both. Everything else we can say is only callable. Note that none of the current answers mention bound functions, but these introduce significant problems to any heuristic check.

As balupton points out, the presence or absence of a property descriptor for the 'caller' property can act as an indicator of how a function was created. A bound function exotic object will not have this own-property even if the function it wraps does. The property will exist via inheritence from Function.prototype, but this is true also for class constructors.

Likewise, toString for a BFEO will normally begin 'function' even if the bound function was created with class. Now, a heuristic for detecting BFEOs themselves would be to see if their name begins 'bound ', but unfortunately this is a dead end; it still tells us nothing about what was bound — this is opaque to us.

However if toString does return 'class' (which won’t be true for e.g. DOM constructors), that’s a pretty solid signal that it’s not callable.

The best we can do then is something like this:

const isDefinitelyCallable = fn =>
  typeof fn === 'function' &&
  !isConstructable(fn);

isDefinitelyCallable(class {});                      // false
isDefinitelyCallable(class {}.bind());               // false
isDefinitelyCallable(function() {});                 // false <-- callable
isDefinitelyCallable(function() {}.bind());          // false <-- callable
isDefinitelyCallable(() => {});                      // true
isDefinitelyCallable((() => {}).bind());             // true
isDefinitelyCallable(async () => {});                // true
isDefinitelyCallable(async function() {});           // true
isDefinitelyCallable(function * () {});              // true
isDefinitelyCallable({ foo() {} }.foo);              // true
isDefinitelyCallable(URL);                           // false

const isProbablyNotCallable = fn =>
  typeof fn !== 'function' ||
  fn.toString().startsWith('class') ||
  Boolean(
    fn.prototype &&
    !Object.getOwnPropertyDescriptor(fn, 'prototype').writable // or your fave
  );

isProbablyNotCallable(class {});                      // true
isProbablyNotCallable(class {}.bind());               // false <-- not callable
isProbablyNotCallable(function() {});                 // false
isProbablyNotCallable(function() {}.bind());          // false
isProbablyNotCallable(() => {});                      // false
isProbablyNotCallable((() => {}).bind());             // false
isProbablyNotCallable(async () => {});                // false
isProbablyNotCallable(async function() {});           // false
isProbablyNotCallable(function * () {});              // false
isProbablyNotCallable({ foo() {} }.foo);              // false
isProbablyNotCallable(URL);                           // true

The cases with arrows point out where we get answers we don’t particularly like.

In the isProbablyNotCallable function, the last part of the condition could be replaced with other checks from other answers; I chose Miguel Mota’s here, since it happens to work with (most?) DOM constructors as well, even those defined before ES classes were introduced. But it doesn’t really matter — each possible check has a downside and there is no magic combo.


The above describes to the best of my knowledge what is and isn’t possible in contemporary ES. It doesn’t address needs that are specific to ES5 and earlier, though really in ES5 and earlier the answer to both of the questions is always "true" for any function.

The future

There is a proposal for a native test that would make the [[FunctionKind]] slot observable insofar as revealing whether a function was created with class:

https://github.com/caitp/TC39-Proposals/blob/master/tc39-reflect-isconstructor-iscallable.md

If this proposal or something like it advances, we would gain a way to solve this problem concretely when it comes to class at least.

* Ignoring the Annex B [[IsHTMLDDA]] case.