Definition of name property in assignment expression

Since ECMAScript 6, most function objects have a name property defined.

Now, if an anonymous function expression is assigned to a variable or is part of a property definition inside an object initializer, the identifier of the variable or the name of the property will be the value of the name property of the function object.

const a = function () {};
console.log(a.name); // a

const object = {
  b : function () {}
};
console.log(object.b.name); // b

I don't have problems to understand the spec regarding the documented semantic of this behaviour, but I can't really see, why an assignment to a left hand side member expression as in the following example does not affect the name property of the function, which seems to be the case.

const object = {};
object.c = function () {};
console.log(object.c.name); //

As part of the member expression, there is obviously an indentifier which could (and should?) be used as the value of the name property. Even if the property name would be an expression inside brackets, this should be no problem, since using computed property names inside an object initializer does not prevent the name property of the anonymous function to be defined either.

It would be great, if someone could confirm that the observed behaviour is conforming to the spec and anyway, explain in short the specific semantics that apply to this syntax.


Solution 1:

This first snippet is described under assignment operators:

e. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then

i. Let hasNameProperty be HasOwnProperty(rval, "name").

ii. ReturnIfAbrupt(hasNameProperty).

iii. If hasNameProperty is false, perform SetFunctionName(rval, GetReferencedName(lref)).

When you assign to a MemberExpression, as in your last snippet, IsIdentifierRef(LeftHandSideExpression) is false and no conversion takes place.

If you search the standard for IsAnonymousFunctionDefinition you'll find a couple of other cases where this logic is used (object initializers, destructuring assignments).

IsIdentifierRef is defined twice (here and here), and both definitions boil down to "if an expression is IdentifierReference return true otherwise return false", where IdentifierReference is an Identifier (the yield stuff is for backward compatibility with non-strict code).