56

As of ES2015 (ES6), functions have proper names (including an official name property), and names are assigned when the function is created in a variety of ways in addition to the obvious function declaration and named function expression, such as assigning to variables (function's name is set to the variable's name), assigning to object properties (function's name is set to the property's name), even default values for function parameters (function's name is set to the parameter's name). But assigning to a property on an existing object (e.g., not in an object initializer) doesn't assign that property's name to the function. Why not? Surely there must be a specific reason it was not desirable/possible. What was it?

To be clear: I'm not asking how to work around it. I'm asking what prevents this seemingly-obvious case from being handled when so many others (including default parameter values!) are. There must be a good reason.

Please don't speculate or theorize. TC39 had a reason for not including it. I'm interested in what that reason was. I've been through the TC39 meeting notes but haven't found it yet. The closest I've found so far is Allen Wirfs-Brock replying to Bergi to say there was no consensus for doing it for that form because of "various objections," but sadly he didn't say what those objections were.

Details:

All of the following assign the name foo to the function on a compliant browser:

// Requires a compliant browser

// Assigning to a variable or constant...
// ...whether in the initializer...
{
    let foo = function() { };
    console.log("1:", foo.name); // "foo"
}
{
    const foo = function() { };
    console.log("2:", foo.name); // "foo"
}
// ...or later...
{
    let foo;
    foo = function() { };
    console.log("3:", foo.name); // "foo"
}
// As an initializer for an object property
{
    const obj = {
        foo: function() { }
    };
    console.log("4:", obj.foo.name); // "foo"
}
// Or as a method
{
    const obj = {
        foo() { }
    };
    console.log("5:", obj.foo.name); // "foo"
}
// Even if it's a computed property name
{
    let name = "f";
    const obj = {
        [name + "o" + "o"]() { }
    };
    console.log("6:", obj.foo.name); // "foo"
}
// As a default value for a parameter
(function(foo = function() { }) {
    console.log("7:", foo.name); // "foo"
})();
// ...and a bunch of others

But assigning to a property on an existing object, outside an object initializer, does not:

const obj = {};
obj.foo = function() { };
console.log("Nope:", obj.foo.name);

As far as I can tell, this is covered by this section of the specification, which explicitly only sets the function name if the IsIdentifierRef of the LeftHandSideExpression is true (which apparently it isn't for property references).

So reiterating from above: Why not? Surely there must be a specific reason it was not desirable/possible. What was it?

25
  • 7
    I asked this at esdiscuss, but no reasons were mentioned. Commented Dec 12, 2016 at 19:07
  • 3
    @Bergi: Thank you. How...dissatisfying. Unfortunate that AWB never came back to you with what the "various objections" were. :-( Commented Dec 12, 2016 at 19:10
  • 8
    Which name should be assigned for obj.prop1 = obj.prop2 = function() {} ? Not assigning anything is very convenient way to avoid answering controversial questions like this. Commented Dec 13, 2016 at 0:52
  • 2
    Now, with var x = y = function() ..., the first name that I see is var x, so the fact that function is named y is arguably confusing and might be considered a bug. I think the proper way to assign a name when the function is created is to use unambiguous named function syntax: var x = y = function y() {} Commented Dec 13, 2016 at 6:58
  • 4
    @artem: It makes perfect sense and is entirely consistent with how the language works. x = y = function() { }; is evaluated as y = function() { } (creating the function, which gives it its name, assigning to y), then that resulting value (an existing function reference) is assigned to x. Doing anything else would violate standard expression semantics and require extremely complicated mechanisms in the spec to achieve. Commented Dec 13, 2016 at 7:16

3 Answers 3

17

Allen Wirfs-Brock has replied on the es-discuss list with the objections that prevented the TC39 consensus on the obj.foo = function() { } form:

...for

cache[getUserSecret(user)] = function() {};

it would leak the secret user info as the value of name

and for

obj[someSymbol] = function() {}

it would leak the Symbol value as the value of name

and for

 table[n]=function() {}

name would likely be a numeric string

There are counters to those objections (particularly the last one, which is extremely weak; there are many other ways a function is automatically assigned a numeric string name), but that's not the point; the point is that those were the objections raised.

He also added that the IsPropertyReference operation that would be required (where currently there's just an IsIdentifierRef)...

...is a runtime operation and the new semantics require runtime determination of the name value. This is all extra runtime work that may slow down the creation of function closures that appear within loops.

So all in all, apparently at the time the decision was taken, those objections carried the day (and quite possibly would now as well), which is why this form doesn't automatically name functions whereas so many others do.

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

Comments

-1

I went through reading the Allen Wirfs-Brock answers, and he explicitly talks about possible security issues. I personally agree with him.

There may also be security concerns. The name property potentially leaks via the function object the name of the variable it is initially assigned to. But there isn't much someone could do with a local variable name, outside of the originating function. But a leaked property name potentially carries a greater capability.

It seems like the objections he talks about have something to do with it. If TC39 didn't explained further it's decision, it's going hard to find out why did they leave it like that :).

I'mn sorry I can't help you further.

2 Comments

This objection was put to bed, though; it would apply equally to all the other forms (well, not to the default parameter value form). Since the other forms went forward, this can't be (intentionally) why this one form didn't.
Try here: 2ality.com/2015/09/function-names-es6.html. There's something about "the name of a function is always assigned during creation". You may understand it better than me, I'm not an ES6 guru :D
-2

I'm not sure there is a specific reason.

obj.foo = function (){};

first creates a reference for the function expression in obj, then binds foo to this reference which has already a (readonly) name.

So:

obj.fooC = (new Function ());
console.log(obj.fooC.name);//'anonymous'

obj.fooFE = function (){};
console.log(obj.fooFE.name);//''

obj.fooNFE = function a_name (){};
console.log(obj.fooNFE.name);//'a_name'

is normal behaviour.

Is there any restriction to write :

obj.foo = (function foo(){});

1 Comment

"I'm not sure there is a specific reason." Allen Wirfs-Brock said there was, that there were "various objections" and therefore there was "no consensus" on that form and thus it was left out. "Is there any restriction to write" No, of course not. :-)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.