Determining if an object can be constructed

# Michael Haufe (8 years ago)

The question came up recently on CLJS on how to determine if an object is safe to call as a Constructor.

Traditionally one uses

try {... x = new foo() ... } catch(e) { ... x = foo() }

In the list of well-known symbols there doesn't seem to be a way to reference [[Construct]] to perform a test to avoid the try/catch. In the following though there is reference to Symbol.create which doesn't seem to be in the spec but seems to be the one to look for. Can we get clarification on if it is possible to determine if an object can be constructed before attempting it?

# T.J. Crowder (8 years ago)

Related: There's a proposal for Reflect.isConstructor (and Reflect.isCallable) which addresses this very gap. There seem to be some questions around Reflect.isCallable (whether it should be true or false for class constructors, since you can't [[Call]] them), but I didn't see issues raised around Reflect.isConstructor. Seems a bit stalled, though.

# Claude Pache (8 years ago)

Le 16 janv. 2017 à 18:00, Michael Haufe <tno at thenewobjective.com> a écrit :

The question came up recently on CLJS [1] on how to determine if an object is safe to call as a Constructor.

The two following procedures will determine whether an object is a constructor without running it, according to ES6. (Whether it is safe to call it as constructor... you need first a definition of "safe".)

(1) Using class/extends:

function isConstructor1(f) {
    if (f === null)
        return false
    try {
        class c extends f { }
        return true
    }
    catch (e) {
       return false
    }
}

(2) Using Proxy:

function isConstructor2(f) {
    var p = new Proxy(f, { construct() { return {} } })
    try {
        new p
        return true
    }
    catch (e) {
       return false
    }
}
# Isiah Meadows (8 years ago)

Out of curiosity, why are classes specified to have a [[Call]] internal method instead of special-casing typeof and friends to work with them? Somewhat of a tangent, but just a curious question on the design decision.

# Oriol _ (8 years ago)

See my stackoverflow.com/questions/39334278/check-if-object-is-a-constructor-isconstructor

I also thought the extends trick could be useful, but it fails 3 of my tests. It doesn't work for bounded normal functions and some proxy objects.

The proper way is the second one in Claude's post: using a proxy object.

There is only a problem: it doesn't work for revoked proxies, because they can't be used as a proxy target.

In my opinion that restriction is somewhat stupid, because you can use a revocable proxy as a proxy target, and revoke it later.

But I guess it's too late to change that. Maybe some day we will have Object.isConstructor.

;Oriol

# Claude Pache (8 years ago)

Le 17 janv. 2017 à 23:56, Oriol _ <oriol-bugzilla at hotmail.com> a écrit :

See my stackoverflow.com/questions/39334278/check-if-object-is-a-constructor-isconstructor, stackoverflow.com/questions/39334278/check-if-object-is-a-constructor-isconstructor

I also thought the extends trick could be useful, but it fails 3 of my tests. It doesn't work for bounded normal functions and some proxy objects.

Yes, I missed the fact that subclassing implies a concrete access to the prototype property of the superclass in order to construct the subclass' prototype. That is the root of the failures.

The proper way is the second one in Claude's post: using a proxy object.

There is only a problem: it doesn't work for revoked proxies, because they can't be used as a proxy target.

In my opinion that restriction is somewhat stupid, because you can use a revocable proxy as a proxy target, and revoke it later.

But I guess it's too late to change that.

It is not too late to change that point, in that the risk of breakage may reasonably be judged almost inexistant. The hard part is to convince people that the inability to use a revoked proxy as proxy target is troublesome enough in order to justify the change. (Also, knowing why there is such a restriction may help to judge.)

# Claude Pache (8 years ago)

Le 17 janv. 2017 à 23:48, Isiah Meadows <isiahmeadows at gmail.com> a écrit :

Out of curiosity, why are classes specified to have a [[Call]] internal method instead of special-casing typeof and friends to work with them? Somewhat of a tangent, but just a curious question on the design decision.

I guess that class constructors could have been specified without a [[Call]] internal method, with the cost of amending all the places where “constructible” implicitly implies “callable”. But why (and how) do you need “special-casing typeof and friends”? (The way the question is formulated, avoiding special-casing would be an argument for the current design. But I miss what are the special cases you have in mind, especially regarding typeof.)

# Isiah Meadows (8 years ago)

Inline.

On Thu, Jan 19, 2017, 03:26 Claude Pache <claude.pache at gmail.com> wrote:

Le 17 janv. 2017 à 23:48, Isiah Meadows <isiahmeadows at gmail.com> a écrit :

Out of curiosity, why are classes specified to have a [[Call]] internal method instead of special-casing typeof and friends to work with them? Somewhat of a tangent, but just a curious question on the design decision.

I guess that class constructors could have been specified without a [[Call]] internal method, with the cost of amending all the places where “constructible” implicitly implies “callable”.

FWIW, calling methods that don't have [[Call]] already throws a TypeError. So it wouldn't affect necessarily all sites, especially if you continue to check for [[Call]] in Array.prototype.forEach (like what is currently done), etc.

But why (and how) do you need “special-casing typeof and friends”? (The

way the question is formulated, avoiding special-casing would be an argument for the current design. But I miss what are the special cases you have in mind, especially regarding typeof.)

I was specifically referring to typeof (class {}) === "function". The "and friends" was in reference to things like the callback in Array.prototype.forEach, which IIUC doesn't currently throw for classes if the array has no members.

Sorry for the poor phrasing there.

# Andrea Giammarchi (8 years ago)

If you are looking for isClass or similar you can also rely on Function.prototype.toString decompilation, which is de facto consistent.

const isClass = (fn) =>
  typeof fn === 'function' &&
  !isClass.toString.call(fn).indexOf('class ');

Of course if you transpile code, including classes, that'd be useless (but then you'll have many other problems anyway)

Best

# Andrea Giammarchi (8 years ago)

actually, let me make it faster against big functions ^_^

const isClass = (fn) =>
  typeof fn === 'function' &&
  /^class /.test(isClass.toString.call(fn));
# Claude Pache (8 years ago)

Le 19 janv. 2017 à 13:18, Isiah Meadows <isiahmeadows at gmail.com> a écrit :

Inline.

On Thu, Jan 19, 2017, 03:26 Claude Pache <claude.pache at gmail.com <mailto:claude.pache at gmail.com>> wrote:

Le 17 janv. 2017 à 23:48, Isiah Meadows <isiahmeadows at gmail.com <mailto:isiahmeadows at gmail.com>> a écrit :

Out of curiosity, why are classes specified to have a [[Call]] internal method instead of special-casing typeof and friends to work with them? Somewhat of a tangent, but just a curious question on the design decision.

I guess that class constructors could have been specified without a [[Call]] internal method, with the cost of amending all the places where “constructible” implicitly implies “callable”.

FWIW, calling methods that don't have [[Call]] already throws a TypeError. So it wouldn't affect necessarily all sites, especially if you continue to check for [[Call]] in Array.prototype.forEach (like what is currently done), etc.

But why (and how) do you need “special-casing typeof and friends”? (The way the question is formulated, avoiding special-casing would be an argument for the current design. But I miss what are the special cases you have in mind, especially regarding typeof.)

I was specifically referring to typeof (class {}) === "function". The "and friends" was in reference to things like the callback in Array.prototype.forEach, which IIUC doesn't currently throw for classes if the array has no members.

If you intend to have no observable difference, I don’t see the point to spec it another way just for introducing special cases here and there.

About typeof. If you insist that a class has no [[Call]] internal method, I think that typeof class {} must not be "function". I have heard complaints that classes look like callable objects, but are not usable as callable objects in practice. It would not be nice to maintain that gotcha by an explicit special case.

# Claude Pache (8 years ago)

Le 19 janv. 2017 à 13:46, Andrea Giammarchi <andrea.giammarchi at gmail.com> a écrit :

If you are looking for isClass or similar you can also rely on Function.prototype.toString decompilation, which is de facto consistent.

const isClass = (fn) =>
  typeof fn === 'function' &&
  !isClass.toString.call(fn).indexOf('class ');

Of course if you transpile code, including classes, that'd be useless (but then you'll have many other problems anyway)

It is also useless when Function#toString() is not correctly implemented, which is currently the case for Firefox, see: bugzilla.mozilla.org/show_bug.cgi?id=1216630, bugzilla.mozilla.org/show_bug.cgi?id=1216630

# Andrea Giammarchi (8 years ago)

Doh! Good point ... I didn't know Firefox was different from every other modern engine :-/

# Till Schneidereit (8 years ago)

It's not, in that it has bugs like all of them. It's obviously unfortunate that the fix for this particular bug hasn't landed yet.

# Scott Sauyet (8 years ago)

Michael Haufe <tno at thenewobjective.com> wrote:

The question came up recently on CLJS [1] on how to determine if an object is safe to call as a Constructor.

The original question was a response to

|| You cannot determine whether a function can be called as a constructor || unless you try it.

that asked the more specific question

| What would count as positive evidence that a function can actually be | called reasonably as a constructor function?

I was wondering if there was any way to distinguish a function like this

    var fn = (function() {
      var g = {};
      return function() {return g;};
    }());

which will pass all the proposed tests here, but still does not actually serve as a constructor function as is made clear by

    // no errors here
    var obj1 = new fn();
    var obj2 = new fn();

    obj1.foo = 'bar'
    obj2; //=> {foo: 'bar'}

Without defining constructor functions explicitly, one would certainly hope that they would create new objects on each invocation, that there is some sort of difference between constructor functions and factory functions.

I wasn't really expecting there to be a positive answer. But I was curious and raised the issue. (For all I know, this is wandering into issues similar to the Halting Problem.)

But does anyone have a suggestion for a test that would reliably tell whether a function can serve as a true constructor in this manner?

# Bradley Meck (8 years ago)

I am unsure the exact question.

new will allocate an object with a prototype for sure, but it is never guaranteed to return that object even with classes.

let a = {};
class Foo {
  constructor() {
    return a;
  }
}
const g = new Foo();
console.log(new Foo() === g);

If the question is "How can an external observer know if a constructor returns this vs some other object?" the answer is, you cannot.

# Michał Wadas (8 years ago)

It would be equivalent to solving halting problem.

Any function like "isConstructor" have to return three possible values - "yes" (native constructors, classes without constructor), "maybe" (can't determine), "no" (arrow functions, methods, generators).

# Hagbard Celine (8 years ago)

a reflection on if and how "this" is used inside a function, would reveal what inner-interface it uses to some object, also also if it mutates its properties. every function mutating this-bound objects is potentiallyt a cinstructor. afaik:

new Foo(); is aequivalent to Foo.call(Object.create(Foo.prototype)) with an implicit "return this" at Foo"s end.

# Scott Sauyet (8 years ago)

Bradley Meck wrote:

I am unsure the exact question.

I certainly agree that it's not a well-formulated question. Perhaps in the end it's not even coherent. But it seems to make some sense. I suspect that the answer is simply that it cannot be done. But I don't know how to demonstrate that.

new will allocate an object with a prototype for sure, but it is never guaranteed to return that object even with classes.

let a = {};
class Foo {
  constructor() {
    return a;
  }
}
const g = new Foo();
console.log(new Foo() === g);

Which demonstrates that Foo by such a criterion is not a reasonable constructor function. I think that makes sense. It clearly isn't one.

If the question is "How can an external observer know if a constructor returns this vs some other object?" the answer is, you cannot.

No, I don't think that's the central point. But perhaps the examples above do capture something fundamental. Even if we cannot prove it about an arbitrary function, that seems like a minimal criteria for a function to be a reasonable constructor:

A function, Foo can be used as a constructor function if

  1. every call to new Foo(...args) creates a distinct object not reference-equal to any other such call.
  2. ??

Does that make sense? Are there other important criteria?

Of course this doesn't help us answer whether a particular function actually meets these criteria, but it's a start.

# Bradley Meck (8 years ago)
  1. every call to new Foo(...args) creates a distinct object not

reference-equal to any other such call.

Would this mean

function factory() { return []; }

would qualify as a constructor?

# Scott Sauyet (8 years ago)

Bradley Meck wrote:

  1. every call to new Foo(...args) creates a distinct object not reference-equal to any other such call.

Would this mean

function factory() { return []; }

would qualify as a constructor?

I'm thinking of that as a necessary, but not a sufficient condition.

If it fails that test it cannot be a constructor. It's clear that there are other tests. It's not clear to me what they are.

# Jordan Harband (8 years ago)

The reality here is that we're all playing around what the definition of "constructor" and "callable" mean.

In the spec, "constructors" are defined as having a [[Construct]] internal slot; something is "callable" if it has a [[Call]] internal slot. By that definition (tc39.github.io/ecma262/#sec-isconstructor and tc39.github.io/ecma262/#sec-iscallable), a class constructor is indeed callable (because it's defined to have [[Call]] throw a TypeError). Similarly, as defined in the spec, typeof class Foo {} is "function" only because it has a [[Call]] internal slot ( tc39.github.io/ecma262/#table-35); if constructors lacked a [[Call]] slot (such that IsConstructor returned true for them), then typeof someConstructor would return "object".

Obviously the committee could have chosen to define these things differently; but this is how things are defined.

I'm going to claim that conceptually - eg, to the majority of users - something is a constructor when it's intended to be used with new. In other words, "what it returns" isn't actually the point - if you're "meant" to use it with new, it's a constructor, otherwise, it's a function - and functions that work with both can be thought of as a dual constructor/factory (the factory calls the constructor for you). However, that conceptual definition is not one that can be programmatically determined, because "programmer intention" in this regard simply isn't inherently enshrined in an observable way, whether choosing class or function.

Personally I think it would be wonderful to expose IsConstructor (IsCallable is already exposed via typeof returning "function"), but I feel quite confident that neither actually address any of the use cases in this thread.

In addition, "safe to call" or "safe to use with new" all depends on your definition of "safe", and it's clear from this thread that that does not have a universally agreed-upon definition either.

It might be useful to distill things down to 1) concrete use cases for calling or instantiating a function where you don't already know what it does, and can't assert in your human documentation what you expect; 2) a definition of "constructor", either a different one if you don't agree with mine above, or if you do, a form of mine that can be programmatically determined, and 3) a definition of "safe" which means more than "doesn't throw an exception".

# Isiah Meadows (8 years ago)

I'll point out that all I need is some way to detect anything that is callable except for classes. Basically, anything that has a [[Call]] that doesn't throw unconditionally without entering a different scope (like classes, but not %ThrowTypeError%).

And to clarify, these would both be included per above, since it does enter a different scope:

// New language scope
function foo() {
  throw new TypeError()
}

// New native scope (%ThrowTypeError%)
"use strict"
var foo = Object.getOwnPropertyDescriptor(
  arguments, "callee"
).get

Reflect.isConstructor isn't sufficient for my needs, since it would still consider the first foo above to be a constructor.

# Jordan Harband (8 years ago)

Unfortunately, the only practical way to know that without risking side effects is to read the human-produced documentation on the function, or to have a human read the code.

# Michał Wadas (8 years ago)

Actually it's possible to create pretty good heuristics to decide if it's possible to call function as constructor, but there is no way to be sure it's really safe.

# Isiah Meadows (8 years ago)

To clarify, I'm wanting to know if it's a callable that isn't a class. Technically, I could use Function.prototype.toString for most practical purposes of mine, but that'd be super slow (I'd need it in a warm loop), and wouldn't catch native construct-only classes like Map and Set (which are still typeof f === "function").

var funcToString = Function.prototype.toString

function isCallable(f) {
    return typeof f === "function" &&
        /^\s*class(\s*\{|\s+[^(])/.test(funcToString.call(f))
}

Isiah Meadows me at isiahmeadows.com

# Andrea Giammarchi (8 years ago)

as Claude already mentioned, that ain't going to work on Firefox due following bug: bugzilla.mozilla.org/show_bug.cgi?id=1216630

# Claude Pache (8 years ago)

Le 24 janv. 2017 à 19:12, Isiah Meadows <isiahmeadows at gmail.com> a écrit :

To clarify, I'm wanting to know if it's a callable that isn't a class. Technically, I could use Function.prototype.toString for most practical purposes of mine, but that'd be super slow (I'd need it in a warm loop), and wouldn't catch native construct-only classes like Map and Set (which are still typeof f === "function").

var funcToString = Function.prototype.toString

function isCallable(f) {
   return typeof f === "function" &&
       /^\s*class(\s*\{|\s+[^(])/.test(funcToString.call(f))
}

Your isCallable function has a flaw: it will treat differently:

// ES6 class
class Foo {
    constructor() {
        // ...
    }  
}

and:

// pre-ES6 almost-compatible approximation of a class
function Foo() {
    if (!(this instanceof Foo))
        throw new TypeError("Foo must be invoked with 'new'");
    // ...
}

You are unable to guess that a function defined with the function keyword is intended to be a constructor or a function.

# Isiah Meadows (8 years ago)

Yeah, I'm aware. I'm keeping my previous "prototype with a specific method" this time around. Much easier IMHO.