Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify implementation reasoning #16

Closed
medikoo opened this issue Nov 7, 2016 · 7 comments
Closed

Clarify implementation reasoning #16

medikoo opened this issue Nov 7, 2016 · 7 comments
Labels

Comments

@medikoo
Copy link

medikoo commented Nov 7, 2016

I just approached this package, and was a bit puzzled over it's complexity. To my understanding check for "is callable" in ES5+ world is as simple as typeof value === 'function'.

What do I miss? Are there any new ES2015+ introductions that do not fall into that rule (class, async function, arrow function return 'function' as expected)

  • Are there any cases in ES5+ world, where a callable value will not return 'function' for typeof check
  • Are there any cases in ES5+ world, where non callable value will return 'function' for typeof check?

I'll be grateful for clarification.

@ljharb
Copy link
Member

ljharb commented Nov 7, 2016

Regular expressions in older browsers have a typeof value of "function".

Some native host objects return typeof function, but are not actually functions.

Separately, classes are not callable - so although there's no way to accurately detect this (sadly), typeof class Foo {} === 'function' but you can only construct it.

In other words, when typeof is not "function" it's definitely not callable (see the bailout here), but when it is function, there's more checks that need to be done.

@ljharb ljharb added the question label Nov 7, 2016
@medikoo
Copy link
Author

medikoo commented Nov 7, 2016

Regular expressions in older browsers have a typeof value of "function".

Sure, but they're callable :) you can do regex(foo) and it will work

Some native host objects return typeof function, but are not actually functions.

They're also callable, and work with all native functions (e.g. [].forEach) that expects functions.

To my understanding callable is any object which has internal [[Call]] slot, and above two fall into that.

Separately, classes are not callable - so although there's no way to accurately detect this (sadly), typeof class Foo {} === 'function' but you can only construct it.

They crash when not invoked with new, but I'd say it's a characteristics of validation mechanism.
I take class syntax as only a sugar, and what we really receive is a typical function, just with some extra behaviours. Additionally in ES2015+ there are class constructors that do not crash, and can be called without issues as Constructor() (e.g. Array).

Anyway thanks for explanation, it's clearer for me, on why you decided on such route.

@ljharb
Copy link
Member

ljharb commented Nov 7, 2016

True, you are correct that those regexes are callable :-) this module started as "is-function" and ended up a bit more generic.

class is not merely syntax sugar - a class constructor genuinely lacks a [[Call]] slot, so it's not the same as a function that throws when new.target is undefined.

typeof foo === 'function' is quite safe for most use cases :-)

@ljharb ljharb closed this as completed Nov 7, 2016
@medikoo
Copy link
Author

medikoo commented Nov 8, 2016

a class constructor genuinely lacks a [[Call]] slot

Not true, if it wouldn't have that slot, any try to do Constructor() will crash with TypeError: Constructor is not a function, additionally in such case typeof Constructor per spec could not return 'function'

After looking more thoroughly into ES2015 spec, I must say that this package is very confusing, especially that it doesn't reflect native ECMAScript isCallable behavior.

@medikoo
Copy link
Author

medikoo commented Nov 8, 2016

The only valid argument I see here, is that it might be helpful to reject class constructors (for scenarios when we indeed expect plain functions). Still, first it will have to be confirmed whether there's a solid method to detect such constructors without false positives, I'm not sure if that's possible (?)

I would also definitely rename this package to something as e.g. is-plain-callable (or is-plain-function if you wish to also reject non-function instances), as it's certainly not about isCallable as we know it from ES or user land (e.g. some other old isCallable implementation, from ES3 era, when we dealt with callable host objects which returned 'object' for typeof check)

@ljharb
Copy link
Member

ljharb commented Nov 8, 2016

Thanks for the suggestion - I'm going to keep the name as-is.

@aikar
Copy link

aikar commented Sep 7, 2017

@ljharb I would suggest putting this explanation at the top of the readme, as I'm sure many are confused on what is the point of this. I at least looked at closed issues :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants