5

There is no obvious difference between an arrow function and a regular function.

({}).toString.call(function () {})
"[object Function]"
({}).toString.call(() => {})
"[object Function]"

or

console.dir( (function () {}) )
function anonymous()
    arguments: null
    caller: null
    length: 0
    name: ""
    prototype: Object
    __proto__: ()
    <function scope>
console.dir( (() => {}) )
function anonymous()
    arguments: (...)
    caller: (...)
    length: 0
    name: ""
    __proto__: ()
    <function scope>

The behaviour of the two is different though and there is a valid use case for being able to tell the two apart.

How to programmatically distinguish an arrow function from a regular function?

Gajus
  • 69,002
  • 70
  • 275
  • 438
  • So basically some way to know if an anonymous function or an arrow function was used as an argument? I wouldn't think there was anything like that available, but maybe there is ? – adeneo Dec 12 '15 at 19:51
  • Your answer is probably in the this object since a regular function assigns the this object to itself but an arrow function assigned the this object to whatever the this object is outside of the arrow function – Binvention Dec 12 '15 at 19:53
  • Why do you need that in runtime? "The behaviour of the two is different though" --- it is the same: they both accept arguments and return a result. – zerkms Dec 12 '15 at 19:54
  • @zerkms Those are only two features that define function behaviour. As others have pointed out, there are more, e.g. the lexical `this` in arrow functions. – Gajus Dec 12 '15 at 20:02
  • @GajusKuizinas that's right, but that should not matter for you in runtime. As a client the only thing you can to do with a function is to pass arguments and retrieve a result. From that perspective they both behave the same. What is the real world case you will apply this to? – zerkms Dec 12 '15 at 20:03
  • @zerkms In the specific use case where I ran into this requirement, I am building a debugging tool (a function that formats object for `console.log` in a console program). I needed to distinguish between arrow and regular function. – Gajus Dec 12 '15 at 20:05
  • @GajusKuizinas and for that purpose you need to distinguish between them just because those are different constructs, not because they behave differently, as you explained in the question. So what would be a reason to distinguish between them because of *different behaviour* (not different syntax)? – zerkms Dec 12 '15 at 20:07
  • In terms of different behaviour, `.call` and `.apply` behaviour would be different. As far as I understand, in the context of arrow functions, the context value would be ignored. – Gajus Dec 12 '15 at 20:08
  • @GajusKuizinas "In terms of different behaviour, .call and .apply behaviour would be different" --- I cannot agree with that. `.call` and `.apply` behave the same: they accept a context and arguments and apply those to a function. – zerkms Dec 12 '15 at 20:09
  • Another example is `new (() => {})`. This produces `Uncaught TypeError: () => {} is not a constructor(…)` error. – Gajus Dec 12 '15 at 20:10
  • possible duplicate of [How can I differentiate between an arrow function, class and a normal function?](http://stackoverflow.com/q/31936822/1048572) – Bergi Oct 11 '16 at 02:10

2 Answers2

4

The best I can think of is using toString:

let isArrowFunction;

isArrowFunction = (fn) => {
    console.log(fn.toString());

    return fn.toString().indexOf('function') !== 0;
};

console.log(isArrowFunction(() => {}) === true);
console.log(isArrowFunction((foo: string) => {}) === true);
console.log(isArrowFunction(function () {}) === false);

See:

(function () {}).toString();
"function () {}"

(() => {}).toString();
"() => {}"
Gajus
  • 69,002
  • 70
  • 275
  • 438
  • A more complete implementation can be found https://github.com/gajus/is-arrow-function and https://www.npmjs.com/package/isarrowfunction and an alternative implementation by @ljharb https://www.npmjs.com/package/is-arrow-function – Gajus Dec 12 '15 at 22:31
2

Uhm, the requirements are a bit weird, but I made some tests and:

typeof (() => {}).prototype === "undefined"

Is true, while:

typeof (function () {}).prototype === "undefined"

Is false, so:

function isArrow(x)
{
  return typeof (x.prototype) === "undefined"
}

Fiddle here: https://jsfiddle.net/87kn67ov/

Jcl
  • 27,696
  • 5
  • 61
  • 92
  • I can replicate this in Chrome, though I do not comprehend how this works, esp. given that `(() => {}).call` and other `Function` prototype methods exist. – Gajus Dec 12 '15 at 20:00
  • I can't really tell, just dumped a few anonymous functions and some arrow functions and checked the differences. According to: http://tc39wiki.calculist.org/es6/arrow-functions/ . `Arrow functions are like built-in functions in that both lack .prototype and any [[Construct]] internal method`, but that doesn't explain why anonymous function do have a .prototype – Jcl Dec 12 '15 at 20:02
  • 1
    `Object.getPrototypeOf(() => {}) === Function.prototype` note that this is true. – Gajus Dec 12 '15 at 20:03
  • Yes, they seem (according to the ES6 wiki) to lack the `.prototype` field, they actually have a `__proto__`. I'm really not that much into javascript to explain WHY it happens, just figured out by experimenting, really – Jcl Dec 12 '15 at 20:04
  • 2
    @jcl—arrow functions aren't constructors, so they don't need a public prototype property. But since they are functions, their internal `[[Prototype]]` is *Function.prototype* and they can be assigned a public *prototype* property. So while checking for arrow functions by looking for their public prototype may work some or even most of the time, it's not guaranteed. It's all in the spec, it just takes some time to wade through it… – RobG Dec 12 '15 at 21:33
  • if this were reliable that would be cool, but until then, unfrotunately, the toString() way is probably more reliable! :/ – Alexander Mills Feb 08 '16 at 22:06