As we all know, we can define functions with and without a name:
var/let/const foo = function() {}
function bar() {}
Where foo
's function hasn't got it's own name, but bar
does.
console.log(foo.name) --> ''
console.log(bar.name) --> 'bar'
Is it possible to define the name of a function after defining it?
So doing something makes console.log(foo.name)
return something else than ''
As we all know...
var/let/const foo = function() {}
...
foo's function hasn't got its own name...
As of ES2015+, foo's function does indeed have a name: foo
. ES2015 added a lot of places where functions get names even when defined with "anonymous" function expressions. This includes simple assignments like that, assignments to properties (including computed property names), and other such. Search the spec for "SetFunctionName" for details.
But as squint points out in a comment, in ES2015+, a function's name can be set via Object.defineProperty
:
Object.defineProperty(foo, "name", {value: "aNameForFoo"});
You can't just assign to foo.name
because name
is defined as non-writable. But it's configurable (you can swap out the property with a new one), so the above works. (Details on how it's defined in the spec.)
Note that Function#name
is new as of ES2015, and as it was a quite minor feature, it was one of the lowest priorities for JavaScript engine developers. I believe Chrome 51 was the first browser with a JavaScript engine that correctly implements the spec. That'll change quickly at this stage.
But as Assimilater points out, if you just want the function created by the expression to have a name and you don't need it defined at runtime, just use a named function expression (NFE):
var foo = function bar() {
// Note ------^^^^
}
//console.log(bar); // Would throw a ReferenceError, `bar` is not defined in this scope
console.log(foo.name); // "bar"
NFEs have been around forever, long before ES2015. They used to be problematic in some engines (very early Safari, IE8 and earlier), but modern engines (IE9+) handle them correctly.
Here's an example of:
// "Inferred" name is foo
var foo = function() {
throw new Error();
};
try {
foo();
} catch (e) {
console.log(e.stack);
}
// Change it to "updatedName"
Object.defineProperty(foo, "name", {
value: "updatedName"
});
try {
foo();
} catch (e) {
console.log(e.stack);
}
// Example of NFE
var foo2 = function bar() {
// Note --------^^^^
console.log("bar is defined within the function: " + bar.name);
throw new Error();
};
//console.log(bar); // Would throw an error, `bar is not
// defined here
console.log(foo2.name); // "bar"
try {
foo2();
} catch (e) {
console.log(e.stack);
}
On a browser that implements ES2015's Function#name
correctly (very few do as of this writing, Chrome 51 being one of the first) and which implements Error#stack
, that outputs:
Error at foo (http://stacksnippets.net/js:15:9) at http://stacksnippets.net/js:18:3 Error at updatedName (http://stacksnippets.net/js:15:9) at http://stacksnippets.net/js:28:3 bar bar is defined within the function: bar Error at bar (http://stacksnippets.net/js:37:9) at http://stacksnippets.net/js:43:3
A note about "inferred" names for functions defined by anonymous expressions. Consider:
let Nifty = {
stuff: {
here: {
foo: function() { throw new Error(); }
}
}
};
let f = Nifty.stuff.here.foo;
console.log(f.name); // "foo"
One could reasonably think the function would end up being Nifty.stuff.here.foo
, or foo
. It's the latter. (This ES2015 name "inference" came out of efforts at Google and Mozilla to provide useful names for functions in their dev tools. At one stage, one or the other — I forget which — was using the full name, but it appears not to have been popular.)
Perhaps worth noting: You can choose a runtime name when creating it, as of ES2015, via a computed property name:
var name = "function" + Math.floor(Math.random() * 10000);
var obj = {[name]: function() { throw new Error(); }};
var foo = obj[name];
try {
foo();
} catch (e) {
console.log(e.stack);
}
Output on a browser supporting this stuff:
Error at function3608 (http://stacksnippets.net/js:14:39) at http://stacksnippets.net/js:17:3
But now that I know you can do the Object.defineProperty
trick, well, that seems less clever. :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With