Determining if an object can be constructed

Isiah Meadows isiahmeadows at gmail.com
Tue Jan 24 18:12:14 UTC 2017


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"`).

```js
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


On Mon, Jan 23, 2017 at 1:49 PM, Jordan Harband <ljharb at gmail.com> wrote:
> 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.
>
> On Mon, Jan 23, 2017 at 8:19 AM, Isiah Meadows <isiahmeadows at gmail.com>
> wrote:
>>
>> 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:
>>
>> ```js
>> // 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.
>>
>>
>> On Sat, Jan 21, 2017, 01:14 Jordan Harband <ljharb at gmail.com> wrote:
>>>
>>> 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 (https://tc39.github.io/ecma262/#sec-isconstructor and
>>> https://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
>>> (https://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".
>>>
>>> On Fri, Jan 20, 2017 at 6:52 PM, Scott Sauyet <scott at sauyet.com> wrote:
>>>>
>>>> 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.
>>>>
>>>>   -- Scott
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at mozilla.org
>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>
>


More information about the es-discuss mailing list