263

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?

6
  • 5
    Isn't the point of arrow syntax to not give the function a name? Commented Jan 16, 2015 at 5: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 Commented Mar 6, 2015 at 17:13
  • 6
    What is wrong with the second snippet? Commented Jul 8, 2015 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" Commented Jan 14, 2016 at 22:13
  • 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). Commented May 27, 2016 at 16:06

8 Answers 8

299

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.)

Sign up to request clarification or add additional context in comments.

14 Comments

@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.
@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 to prevent information leakage.
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 (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.
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 // ''
@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.
|
86

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

Named functions are defined with the function keyword.

7 Comments

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 bit.do/es2015-arrow to understand better what this implies...
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 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).
@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 Thanks for that bit of information, and for your useful answer. I didn't knew this (very very weird) feature.
@sminutoli your bit.do/es2015-arrow link is broken
|
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.

7 Comments

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.
I was wrong: while name isn't directly mutable, you can configure it like this: Object.defineProperty(sayHello,'name',{value:'sayHello'})
"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.
@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. But the OP specifically ruled out doing this (bizarrely).
Can I get this name in toString? I tried overriding it, but then I don't know how to access the function body.
|
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'},

6 Comments

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?
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);
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.
Btw, I think you are confusing "named function" with "(instance) method" and "scope" with "this context"
@tdecs: Yes, you can; Jörg is mistaken. let a = () => {}; defines a named arrow function called a. Details.
|
0

There are several ways to name an arrow function without a var statement.

  1. const x = () => console.log('with a const')
  2. let x = () => console.log('with a let')
  3. const a = { x: () => console.log('as a property') }

In some environments you could do this, assigning it to a temporary variable and calling it immediately, without using any var, const or let.

(x = () => console.log('x'))()

Or similarly, ((x = () => console.log('x')), x())

Perhaps the most legitimate way which doesn't require any assignment at all is this:

({ z: () => console.log('z') }).z()

Or, alternatively:

({ a() { console.log('a'); }}).a()

Comments

-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"

2 Comments

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.
@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.
-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

    }

}

2 Comments

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.
This does what the OP said he doesn't want to do, and relies on this proposal 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.
-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();

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.