Consider this code example:
let test = {
test: function(){}
}
test.print = function(){}
console.log(test.print.name) // Outputs ""
console.log(test.test.name) // Outputs "test"
console.log(test) // Outputs { test: function(){}, print: function(){} }
Why is there a difference between the name
properties of these two functions?
The specification tells you:
({ test: function(){} })
13.2.5.5 Runtime Semantics: PropertyDefinitionEvaluation
PropertyDefinition : PropertyName
:
AssignmentExpression
- Let propKey be the result of evaluating
PropertyName
.- […]
- If IsAnonymousFunctionDefinition ( AssignmentExpression ) is true, then
- Let propValue be? NamedEvaluation of AssignmentExpression with argument
propKey
.- Else,
- Let exprValueRef be the result of evaluating AssignmentExpression .
- Let propValue be? GetValue ( exprValueRef ).
- […]
In step 3, IsAnonymousFunctionDefinition of the expression function(){}
returns true , because it is a function definition , and it lacks a BindingIdentifier .
Therefore, a NamedEvaluation is performed: the function is created with "test"
as the value of the name
property.
test.print = function(){};
13.15.2 Runtime Semantics: Evaluation
AssignmentExpression : LeftHandSideExpression
=
AssignmentExpression
- Let lref be the result of evaluating LeftHandSideExpression .
- […]
- If IsAnonymousFunctionDefinition ( AssignmentExpression ) and IsIdentifierRef of LeftHandSideExpression are both true , then
- Let rval be NamedEvaluation of AssignmentExpression with argument lref .[[ReferencedName]].
- Else,
- Let rref be the result of evaluating AssignmentExpression .
- Let rval be? GetValue ( rref ).
- Perform? PutValue ( lref , rval ).
- […]
In step 3,
function(){}
returns true , just like it does in the other snippet,test.print
returns false : it's a MemberExpression , not an Identifier .Therefore, step 4, the “Else” case, is taken and no NamedEvaluation is performed.
The only difference is the additional IsIdentifierRef step, which is also the key to finding the rationale (on esdiscuss.org):
2015-07-25 14:22:59 UTC adbergi at web.de writes:
[If] a function (or class) definition is assigned to a property of an object, [the function automatically being given the name of the target identifier] doesn't happen:
var o = {}; o.someProperty = function() { … }; o.otherProperty = class { … };
I don't see any reason for not doing this. [It] just seems to be advantageous and make the language more consistent. I'm quite sure it wouldn't break any compatibility.
This is one of the replies:
2015-07-26 19:48:15 UTC Allen Wirfs-Brock writes (emphasis mine):
[T]he possibility that the property key is a symbol is a primary reason that this expression form does not set the
name
property .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.
Allen goes on, 2015-07-26 20:33:07 UTC :
TC39 reached consensus on automatically assigning the
name
property for expression forms like:Identifier
=
FunctionExpressionand so it is part of ES2015. We did not have consensus on doing the same for:
MemberExpression
.
IdentifierName=
FunctionExpressionor
MemberExpression
[
IdentifierName]
=
FunctionExpressionso it is not part of ES2015. There were various objections that would have to be overcome before we could adopt that.
Another comment, 2016-12-13 09:03:40 UTC by TJ Crowder states that it's unclear what those “various objections” were. They link to the original proposal (archived from wiki.ecmascript.org, 2016-09-15, last available version) which does list your expected behavior in an example:
obj.property = function(){}; // "property"
But keep in mind that this was still an unfinished proposal back then. When the proposal got accepted into the specification, it seems that it underwent some changes, but the proposal article didn't get changed to reflect these changes.
This thread mentions certain TC39 meeting notes where this is discussed, but no link is provided. Allen is claiming that this kind of information leak is the reason why TC39 couldn't reach a consensus on allowing this behavior. They mention 2017-01-28 15:46:54 UTC :
[F]or
cache[getUserSecret(user)] = function(){};
it would leak the secret user info as the value ofname
.
Even though, as TJ Crowder mentions 2017-01-28 16:11:26 UTC , the same thing is possible with:
cache = { [getUserSecret(user)]: function() {} };
“Is there a way to get the name of a function declared [like test.print = function(){};
]?”
Not really. You could iterate through the object's entries and find candidates for key names where the value matches the function. But if the function is referenced by another key name, you may get multiple results. Also, this gets more complicated if the key is a symbol .
const test = { test: function(){} } test.print = function(){}; test.someOtherAliasForPrint = test.print; test[Symbol("someSymbolForPrint")] = test.print; console.log( "Possible function names:", Object.entries(Object.getOwnPropertyDescriptors(test)).filter(([key, { value }]) => value === test.print).map(([key]) => key) ); console.log( "Possible function symbols:", Object.getOwnPropertySymbols(test).filter((symbol) => test[symbol] === test.print).map((symbol) => String(symbol)) // Or `.map(({description}) => description)` );
Your best bet is to define the method like this:
test.print = function print(){};
However, consider using method syntax instead:
({
test(){},
print(){}
})
How you are defining print is using anonymous functions and accessing their name property will return ''. Read more here
If you still want to make the code work you can change your code like this:
test.print = function print() {
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.