How do I write a named arrow function in ES2015?

jhamm picture jhamm · Jan 16, 2015 · Viewed 86.1k times · Source

I have a function that I am trying to convert to the new arrow syntax in ES6. It is a named function:

function sayHello(name) {
    console.log(name + ' says hello');
}

Is there a way to give it a name without a var statement:

var sayHello = (name) => {
    console.log(name + ' says hello');
}

Obviously, I can only use this function after I have defined it. Something like following:

sayHello = (name) => {
        console.log(name + ' says hello');
    }

Is there a new way to do this in ES6?

Answer

T.J. Crowder picture T.J. Crowder · May 27, 2016

How do I write a named arrow function in ES2015?

You do it the way you ruled out in your question: You put it on the right-hand side of an assignment or property initializer where the variable or property name can reasonably be used as a name by the JavaScript engine. There's no other way to do it, but doing that is correct and fully covered by the specification.

Per spec, this function has a true name, sayHello:

var sayHello = name => {
    console.log(name + ' says hello');
};

This is defined in Assignment Operators > Runtime Semantics: Evaluation where it calls the abstract SetFunctionName operation (that call is currently in step 1.e.iii).

Similiarly, Runtime Semantics: PropertyDefinitionEvaluation calls SetFunctionName and thus gives this function a true name:

let o = {
    sayHello: name => {
        console.log(`${name} says hello`);
    }
};

Modern engines set the internal name of the function for statements like that already; Edge still has the bit making it available as name on the function instance behind a runtime flag.

For example, in Chrome or Firefox, open the web console and then run this snippet:

"use strict";
let foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

On Chrome 51 and above and Firefox 53 and above (and Edge 13 and above with an experimental flag), when you run that, you'll see:

foo.name is: foo
Error
    at foo (http://stacksnippets.net/js:14:23)
    at http://stacksnippets.net/js:17:3

Note the foo.name is: foo and Error...at foo.

On Chrome 50 and earlier, Firefox 52 and earlier, and Edge without the experimental flag, you'll see this instead because they don't have the Function#name property (yet):

foo.name is: 
Error
    at foo (http://stacksnippets.net/js:14:23)
    at http://stacksnippets.net/js:17:3

Note that the name is missing from foo.name is:, but it is shown in the stack trace. It's just that actually implementing the name property on the function was lower priority than some other ES2015 features; Chrome and Firefox have it now; Edge has it behind a flag, presumably it won't be behind the flag a lot longer.

Obviously, I can only use this function after I have defined it

Correct. There is no function declaration syntax for arrow functions, only function expression syntax, and there's no arrow equivalent to the name in an old-style named function expression (var f = function foo() { };). So there's no equivalent to:

console.log(function fact(n) {
    if (n < 0) {
        throw new Error("Not defined for negative numbers");
    }
    return n == 0 ? 1 : n * fact(n - 1);
}(5)); // 120

You have to break it into two expressions (I'd argue you should do that anyway):

const fact = n => {
    if (n < 0) {
        throw new Error("Not defined for negative numbers.");
    }
    return n == 0 ? 1 : n * fact(n - 1);
};
console.log(fact(5));

Of course, if you have to put this where a single expression is required, you can always...use an arrow function:

console.log((() => {
    const fact = n => {
        if (n < 0) {
            throw new Error("Not defined for negative numbers.");
        }
        return n == 0 ? 1 : n * fact(n - 1);
    };
    return fact(5);
})()); // 120

I ain't sayin' that's pretty, but it works if you absolutely, positively need a single expression wrapper.