259

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?

Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
jhamm
  • 24,124
  • 39
  • 105
  • 179
  • 5
    Isn't the point of arrow syntax to *not* give the function a name? – AstroCB Jan 16 '15 at 05:02
  • 72
    Not necessarily, arrow functions maintain lexical scope so having a shorthand to produce named functions (useful for a stack trace) with lexical scope would be pretty useful – Matt Styles Mar 06 '15 at 17:13
  • 6
    What is wrong with the second snippet? – Bergi Jul 08 '15 at 22:22
  • You most certainly can reference an assigned name inside the body of the function: var sayHello = (name) => { console.log(sayHello); }; And in the case of recursive functions, you'll often do exactly that. Not a super useful example, but here's a function that returns itself if it doesn't get its argument: var sayHello = (name) => name?console.log(name+ ' says hello'):sayHello; sayHello()('frank'); //-> "frank says hello" – Dtipson Jan 14 '16 at 22:13
  • @AstroCB: No, not at all. The point is to have a lexical `this`. Still plenty of reasons you'd want to give those functions names. – T.J. Crowder May 27 '16 at 15:59
  • 4
    The title of the question is **hugely** misleading compared to its content, because you've ruled out the way you name arrow functions (which is the second snippet). – T.J. Crowder May 27 '16 at 16:06

8 Answers8

290

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. (It also works for traditional anonymous function expressions.)

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

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

This is currently defined in Assignment Operators > Runtime Semantics: Evaluation where it does the abstract NamedEvalution operation (currently step 1.c.i). (You can see everywhere this applies by hovering your mouse over NamedEvalution in the header there and clicking "References".) (Previously, before ES2019, Assignment Operators > Runtime Semantics: Evaluation used the abstract SetFunctionName operation, step 1.e.iii, but in ES2019 onward this specification abstraction was replaced with NamedEvalution.)

Similiarly, PropertyDefinitionEvaluation uses NamedEvalution 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.

Note: For this name inference to occur, the function expression has to be directly assigned to the target. For instance, this doesn't infer the name:

const sayHello = (void 0, (name) => {
    console.log(name + ' says hello');
});
console.log(sayHello.name); // ""

That's because the function expression isn't being directly assigned to the const, it's an operand to a further operator (in that case, the comma operator, but it would be the same for [say] true && (name) => { }).

For example, in Chrome, Edge (Chromium-based, v79 onward), 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 "Legacy" Edge 13 and above with an experimental flag, or "Chromium" Edge 79 onward), 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 Legacy 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.


Side note: What if you don't want a function to get its name from the identifier you're assigning to? That, suppose you don't want example.name to be "example" here?

const example = () => {};
console.log(example.name); // "example"

You can avoid it by using any expression that doesn't use NamedEvaluation. Probably the most popular way to do this sort of thing is the comma operator:

const example = (0, () => {});
//              ^^^−−−−−−−−−^
console.log(example.name); // ""

The 0 there can be anything you want, it's evaluated and then thrown away so 0 is a popular choice. Passing the function through the comma operator breaks the direct link between the assignment and the function expression, preventing NamedEvaluation from providing the name example for the function. (This is similar to other famous uses of the comma operator, like (0, object.example)() which calls object.example without making object the value of this within the call, or (0, eval)("code"), which does an eval, but not in the current scope as it normally would.)

(Thank you to Sebastian Simon for raising this point in the comments.)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • How do I prevent the name from being set (like in older engines)? – trusktr Mar 30 '18 at 04:19
  • 1
    @trusktr: I can't see why you'd want to, but if you want to prevent it: Don't do the thing(s) above. For instance, while `let f = () => { /* do something */ };` assigns a name to the function, `let g = (() => () => { /* do something */ })();` doesn't. – T.J. Crowder Mar 30 '18 at 06:03
  • That's what I ended up doing. I'd rather have anonymous classes generated by my class inheritance library to be anonymous than have names of internal variables that the user of my class inheritance library doesn't need to think about, and I'd like the end user to see a name only if they provided a name for the class. (http://github.com/trusktr/lowclass) – trusktr Mar 31 '18 at 00:29
  • 5
    @trusktr: The one exception they made may suit your purposes, then: If you assign a function to a property on an *existing* object, the name is not set: `o.foo = () => {};` does not give the function a name. This [was intentional](https://stackoverflow.com/a/41912102/157247) to prevent information leakage. – T.J. Crowder Mar 31 '18 at 09:34
  • Looks like with react-native on some devices still doesn't return the name, or react and react-native/react-native-debugger do not pick the name up – Zorgatone Jul 03 '20 at 09:54
  • The next-to-last factorial example is not *entirely* equivalent to what you call an "old style" named function because the name `fact` is not defined in the *local* scope of the invocation but in its immediate closure. – Ron Inbar Oct 03 '20 at 19:03
  • @RonInbar - If I'm reading you right, you're saying `fact` isn't added to the scope when you do `console.log(function fact(n) { /*...*/ });`, right? If so, quite correct, it isn't; and the arrow function version does define `fact` in that scope. That's part of why the last version is there. :-) – T.J. Crowder Oct 04 '20 at 07:34
  • @T.J.Crowder No, that's not what I meant. I meant that if you place a breakpoint inside the function, you will see the following frames: `[{name: "Local: fact", bindings: { 'fact': *f* fact(n) { ... }, 'n': 6, 'this': global } }, { name: "Closure", bindings: { ... } }, { name: "Global", ... }]` while if you do the same with the arrow function you'll see `[{name: "Local: factorial", bindings: { 'n': 6, 'this': undefined } }, { name: "Closure", bindings: { 'factorial': n => { ... } } }, { frame: "Global", ... }]`. In a named function, the name of the function is a variable like its parameters. – Ron Inbar Oct 04 '20 at 12:43
  • @RonInbar - I see what you're referring to. Yes, that's true, but it doesn't really matter for the example provided, since it closes over the identifier. (Not sure why I used `let`, though; I've changed it to `const`.) – T.J. Crowder Oct 04 '20 at 12:54
  • 1
    Just for completeness: in order to prevent the name from being inferred, all you have to do is to avoid any production that results in a [NamedEvaluation](//tc39.es/ecma262/#sec-runtime-semantics-namedevaluation) (in the spec text, hover over the heading and click “References (18)” to see where it is used). Something like ``const sayHello = (0, (name) => console.log(`${name} says hello`));`` will do. – Sebastian Simon Dec 11 '21 at 00:42
  • Thanks @SebastianSimon! That's changed since I wrote this, looks like as of ES2019 they refactored the spec a bit, dropping the SetFunctionName I referred to originally and using NamedEvaluation instead. I've updated the answer, and added that point about "what if you don't want that?' at the end. :-) – T.J. Crowder Dec 11 '21 at 10:49
  • 1
    I also note that the same name behavior appears to be in effect for function non-arrow function expressions. ie. `const foo = function() {}; foo.name // foo` and `const foo = (0, function() {}); foo.name // ''` – Ben Aston Oct 07 '22 at 16:52
  • 1
    @BenAston - Good point! I'll note that above. The inference mechanism actually predates arrow functions, but only as an extra thing that some JavaScript engines did. The inference got codified and added to the spec at the same time arrow functions were. – T.J. Crowder Oct 07 '22 at 16:57
  • That's the answer I was looking for. Nice one. – Ben Aston Oct 07 '22 at 17:04
88

No. The arrow syntax is a shortform for anonymous functions. Anonymous functions are, well, anonymous.

Named functions are defined with the function keyword.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 19
    As was stated by @DenysSéguret, isn't a _shortform for anonymous functions_. You'll get a _hard binded_ function. You can see the transpiled result here http://bit.do/es2015-arrow to understand better what this implies... – sminutoli Mar 18 '16 at 20:06
  • 24
    The first word of this answer is correct **for the question asked** because the OP ruled out the way you name an arrow function. But the remainder of the answer is simply incorrect. Look in the specification for where the [abstract `SetFunctionName` operation](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-setfunctionname) is used. `let a = () => {};` defines a **named** function (the name is `a`) both according to the spec and in modern engines (throw an error in it to see); they just don't support the `name` property yet (but it's in the spec and they'll get there eventually). – T.J. Crowder May 27 '16 at 16:03
  • 4
    @DenysSéguret: You *can* simply name them, it's in the spec. You do it the way the OP ruled out in the question, which gives the function (not just the variable) a true name. – T.J. Crowder Aug 12 '16 at 13:59
  • 5
    @T.J.Crowder Thanks for that bit of information, and for your [useful answer](http://stackoverflow.com/a/37488652/263525). I didn't knew this (very very weird) feature. – Denys Séguret Aug 12 '16 at 16:03
  • 6
    This answer is not correct. The question was answered correctly by @T.J.Crowder below – Drenai Jan 09 '17 at 09:56
  • @sminutoli your bit.do/es2015-arrow link is broken – Devin Rhode Sep 22 '19 at 23:02
  • @DevinGRhode I can reach the url... maybe was offline for a moment – sminutoli Jan 22 '20 at 18:11
  • var sayHello = name => { console.log(name + ' says hello'); }; console.log(sayHello.name); // logs sayHello – Király István Jan 27 '21 at 06:45
53

If by 'named', you mean you want the .name property of your arrow function to be set, you're in luck.

If an arrow function is defined on the right-hand-side of an assignment expression, the engine will take the name on the left-hand-side and use it to set the arrow function's .name, e.g.

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

sayHello.name //=== 'sayHello'

Having said that, your question seems to be more 'can I get an arrow function to hoist?'. The answer to that one is a big ol' "no", I'm afraid.

Gajus
  • 69,002
  • 70
  • 275
  • 438
hughfdjackson
  • 631
  • 5
  • 4
  • 1
    In the current version of chrome, at least, sayHello.name is still ""; Note that like all functions, sayHello is an object, so you can define arbitrary properties to it if you want. You can't write to "name" as it is read-only, but you could sayHello.my_name="sayHello"; Not sure what that gets you though, since you can't do/reference that within the body of the function. – Dtipson Jan 14 '16 at 22:07
  • 2
    I was wrong: while name isn't directly mutable, you can configure it like this: Object.defineProperty(sayHello,'name',{value:'sayHello'}) – Dtipson Jan 15 '16 at 16:07
  • 1
    "If an arrow function is defined on the right-hand-side [..]" what is the source for this? I cannot find it in the spec. Just need a reference to quote. – Gajus Feb 04 '16 at 12:29
  • 1
    @Dtipson: That's just because modern engines haven't yet implemented setting the `name` property everywhere the ES2015 spec requires them to; they'll get to it. It's one of the last things on the compliance list for Chrome and even Chrome 50 doesn't have it (Chrome 51 finally will). [Details](http://stackoverflow.com/a/37488652/157247). But the OP specifically ruled out doing this (bizarrely). – T.J. Crowder May 27 '16 at 16:52
  • Can I get this name in toString? I tried overriding it, but then I don't know how to access the function body. – Qwerty Oct 26 '17 at 14:13
  • if you pass in an object as a first parameter it doesn't work but good to know - meaning if you need to debug you could pass first param as string and then pass in your object – James Daly Aug 01 '19 at 01:44
  • How can I get the `sayHello` name from within `sayHello` itself? Is there something like: `const sayHello = () => console.log('Name of function: ', ...name...)` which would output `Name of function: sayHello`? I cannot access `sayHello` from within the function itself because it is controlled by the client code. Thank you! – tonix Jul 10 '20 at 08:05
0

It appears that this will be possible with ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

The example given is:

class PostInfo extends React.Component {
  handleOptionsButtonClick = (e) => {
    this.setState({showOptionsModal: true});
  }
}

The body of ES6 arrow functions share the same lexical this as the code that surrounds them, which gets us the desired result because of the way that ES7 property initializers are scoped.

Note that to get this working with babel I needed to enable the most experimental ES7 stage 0 syntax. In my webpack.config.js file I updated the babel loader like so:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},
Josh Unger
  • 6,717
  • 6
  • 33
  • 55
Hamish Currie
  • 491
  • 5
  • 12
  • 7
    That's not exactly a named arrow function. This is a shortcut for creating instance methods in the constructor, and I wonder how it is relevant here? – Bergi Jul 09 '15 at 03:19
  • Hi @Bergi, I'm not sure if this was the intention of the original author, but I was trying to create a named function with the same this scope as the object. If we don't use this technique, and we want to have the appropriate scope we have to bing it in in the class constructor. `this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);` – Hamish Currie Jul 10 '15 at 04:38
  • 8
    Uh, you can just as easily use `constructor() { this.handleOptionsButtonClick = (e) => {…}; }` which is totally equivalent to the code in your answer but already works in ES6. No need to use `.bind`. – Bergi Jul 10 '15 at 04:43
  • 1
    Btw, I think you are confusing "named function" with "(instance) method" and "scope" with "`this` context" – Bergi Jul 10 '15 at 04:44
  • the question was asking regarding arrow functions and ES6/ES2015 your answer is talking about ES7/React. you can't answer from Jörg W Mittag is correct – dciccale Dec 02 '15 at 09:12
  • 1
    @tdecs: Yes, you can; Jörg is mistaken. `let a = () => {};` defines a **named** arrow function called `a`. [Details](http://stackoverflow.com/a/37488652/157247). – T.J. Crowder May 27 '16 at 16:50
0

Actually it looks like one way to name arrow functions (at least as of chrome 77...) is to do this:

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

enter image description here

Devin Rhode
  • 23,026
  • 8
  • 58
  • 72
  • could theoretically create a babel macro `fn('yourName', ()=>{})`, which could maintain 100% correct arrow function semantics, or better yet a babel plugin for "named arrow function syntax": `fn yourName() => {}`. Either of these would compile down to the code above. I think named arrows would be so BADA55 – Devin Rhode Sep 22 '19 at 23:56
-1

THIS IS ES6

Yeah I think what you're after is something like this:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"
Dan Lowe
  • 51,713
  • 20
  • 123
  • 112
  • I tried this example, it works fine. Any good reason for giving negative votes? Does this usage creating any unwanted side effects or am i missing any important point? Your help in clarifying my doubt will be appreciated. – FullMoon Dec 23 '16 at 06:41
  • @GaneshAdapa: I expect the downvotes are because this is suggesting doing exactly what the OP said he/she didn't want to do, without giving any further information. – T.J. Crowder Mar 14 '17 at 10:23
-1

in order to write named arrow function you can fellow the bellow example, where I have a class named LoginClass and inside this class I wrote an arrow named function, named successAuth class LoginClass {

    constructor() {

    }

    successAuth = (dataArgs)=> { //named arow function

    }

}
BERGUIGA Mohamed Amine
  • 6,094
  • 3
  • 40
  • 38
  • 1
    It's always better to add an explanation to the code you're posting as an answer, so that it will helps visitors understand why this is a good answer. – abarisone Jul 12 '16 at 11:46
  • This does what the OP said he doesn't want to do, and relies on [this proposal](https://tc39.github.io/proposal-class-public-fields/) which is only at Stage 2 and probably isn't even going to make ES2017 (but I think it's likely to make ES2018, and transpilers have supported it for months). It also effectively duplicates several previous answers, which isn't useful. – T.J. Crowder Feb 01 '17 at 09:43
-2

You could skip the function part and the arrow part to create functions. Example:

 class YourClassNameHere{

   constructor(age) {
     this.age = age;
   }

   foo() {
     return "This is a function with name Foo";
   }

   bar() {
     return "This is a function with name bar";
   }

 }

let myVar = new YourClassNameHere(50);
myVar.foo();
scre_www
  • 2,536
  • 4
  • 20
  • 30