Is there an elegant way to tell Harmony\'s slim arrow functions apart from regular functions and built-in functions?
The Harmony wiki states that:
Kangax has made a great point. If you wanted perfection, you'd use an AST parser library to determine the function type, but that is likely to be overkill for many of you.
To that end, here's a method to implement using Regular Expressions:
/** Check if function is Arrow Function */
const isArrowFn = (fn) => (typeof fn === 'function') && /^[^{]+?=>/.test(fn.toString());
/* Demo */
const fn = () => {};
const fn2 = function () { return () => 4 }
isArrowFn(fn) // True
isArrowFn(fn2) // False
Logic
This assumes that all non-arrow function blocks must be surrounded by { }. I don't think that there is an environment which would render otherwise, but please let me know if I'm wrong.
Note: The regex is also written with the assumption that the function's => will always be on the first line. This can be easily changed, but again, I can't imagine anything rendering a newline before it.
How does it work?
Problem?
Leave a comment if you find a case where it doesn't work, and I'll see if we can accommodate.
ECMAScript waives a lot of its guarantees for host objects, and thus by extension, host functions. That makes the properties accessible via reflection mostly implementation-dependent with little guarantees for consistency, at least as far as the ecmascript spec is concerned, W3C specs may be more specific for browser host objects.
E.g. see
The Table 9 summarises the internal properties used by this specification that are only applicable to some ECMAScript objects. [...] Host objects may support these internal properties with any implementation-dependent behaviour as long as it is consistent with the specific host object restrictions stated in this document.
So built-in functions might be callable but have no prototype (i.e. not inherit from function). Or they could have one.
The spec says they may behave differently. But they also may implement all the standard behavior, making them indistinguishable from normal functions.
Note that I'm quoting the ES5 spec. ES6 is still undergoing revisions, native and host objects are now called exotic objects. But the spec pretty much says the same. It provides some invariants that even they must fulfill, but otherwise only says that they may or may not fulfill all optional behaviors.
Ron S's solution works great but can detect false positives:
/** Check if function is Arrow Function */
const isArrowFn = (fn) => (typeof fn === 'function') && /^[^{]+?=>/.test(fn.toString());
/* False positive */
const fn = function (callback = () => null) { return 'foo' }
console.log(
isArrowFn(fn) // true
)
I couldn't find false positives. Small change to Ron S's approach:
const isArrowFn = f => typeof f === 'function' && (/^([^{=]+|\(.*\)\s*)?=>/).test(f.toString().replace(/\s/, ''))
const obj = {
f1: () => {},
f2 () {}
}
isArrowFn(obj.f1) // true
isArrowFn(() => {}) // true
isArrowFn((x = () => {}) => {}) // true
isArrowFn(obj.f2) // false
isArrowFn(function () {}) // false
isArrowFn(function (x = () => {}) {}) // false
isArrowFn(function () { return () => {} }) // false
isArrowFn(Math.random) // false