技术解释:NamedEvaluation
规范告诉你:
({ test: function(){} })
PropertyDefinition : PropertyName : AssignmentExpression
- 让 propKey 为评估
PropertyName 的结果。
- […]
- 如果IsAnonymousFunctionDefinition(AssignmentExpression) 为真,那么
- 让 propValue 是 ? AssignmentExpression 的 NamedEvaluation,带有参数
propKey。
- 否则,
- 让 exprValueRef 为评估 AssignmentExpression 的结果。
- 让 propValue 是 ? GetValue(exprValueRef).
- […]
在第 3 步中,表达式function(){} 的IsAnonymousFunctionDefinition 返回true,因为它是is a function definition,它是lacks a BindingIdentifier。
因此,执行了NamedEvaluation:该函数使用"test" 作为name 属性的值创建。
test.print = function(){};
AssignmentExpression : LeftHandSideExpression = AssignmentExpression
- 令 lref 为评估 LeftHandSideExpression 的结果。
- […]
- 如果 LeftHandSideExpression 的 IsAnonymousFunctionDefinition(AssignmentExpression) 和 IsIdentifierRef 都是 true,那么
- 让 rval 为 AssignmentExpression 的 NamedEvaluation,参数为 lref。[[ReferencedName]]。
- 否则,
- 让 rref 为评估 AssignmentExpression 的结果。
- 让 rval 是 ? GetValue(rref).
- 执行? PutValue(lref, rval).
- […]
在第 3 步中,
-
表达式
function(){} 的 IsAnonymousFunctionDefinition 返回 true,就像它在另一个 sn-p 中所做的一样,
- 但表达式
test.print 的IsIdentifierRef 返回false:它是MemberExpression,而不是标识符。
因此,采用第 4 步,即“Else”情况,不执行NamedEvaluation。
基本原理:安全
唯一的区别是额外的 IsIdentifierRef 步骤,这也是 key to finding 和 rationale(在 esdiscuss.org 上):
2015-07-25 14:22:59 UTC a.d.bergi at web.de 写道:
[如果]一个函数(或类)定义被分配给一个对象的属性,[函数自动被赋予目标标识符的名称]不会发生:
var o = {};
o.someProperty = function() { … };
o.otherProperty = class { … };
我看不出有什么理由不这样做。 [它] 似乎是有利的,并使语言更加一致。我很确定它不会破坏任何兼容性。
这是回复之一:
2015-07-26 19:48:15 UTC Allen Wirfs-Brock 写道(强调我的):
[T]他属性键是符号的可能性是这种表达形式没有设置name属性的主要原因。
还可能存在安全问题。 name 属性可能会通过函数对象泄漏它最初分配给的变量的名称。但是除了原始函数之外,没有人可以对局部变量名做太多事情。但泄露的属性名称可能具有更强大的功能。
艾伦继续说,2015-07-26 20:33:07 UTC:
TC39 就自动为以下表达式分配 name 属性达成共识:
标识符=函数表达式
所以它是 ES2015 的一部分。我们没有就这样做达成共识:
MemberExpression . IdentifierName = FunctionExpression
或
MemberExpression [ IdentifierName ] = FunctionExpression
所以它不是 ES2015 的一部分。有各种反对意见在我们采纳之前必须克服。
另一条评论,2016-12-13 09:03:40 UTC 来自 T.J.克劳德表示,目前尚不清楚这些“各种反对意见”是什么。
它们链接到original proposal(存档自 wiki.ecmascript.org,2016-09-15,最后一个可用版本),确实在示例中列出了您的预期行为:
obj.property = function(){}; // "property"
但请记住,当时这仍然是一个未完成的提案。
当提案被规范接受时,它似乎发生了一些变化,但提案文章并没有改变以反映这些变化。
此线程提到了某些讨论此问题的 TC39 会议记录,但未提供链接。
艾伦声称这种信息泄露是 TC39 无法就允许这种行为达成共识的原因。
他们提到2017-01-28 15:46:54 UTC:
[F]或cache[getUserSecret(user)] = function(){};它会泄露秘密用户信息作为name的值。
尽管如此,作为 T.J. Crowder 提到了2017-01-28 16:11:26 UTC,同样的事情也可能发生在:
cache = {
[getUserSecret(user)]: function() {}
};
匿名函数没有简单的解决方法
“有没有办法获取声明的函数的名称 [like test.print = function(){};]?”
不是真的。
您可以遍历对象的条目并找到值与函数匹配的键名的候选者。
但是如果函数被另一个键名引用,你可能会得到多个结果。
此外,如果密钥是 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)`
);
最好的办法是这样定义方法:
test.print = function print(){};
但是,请考虑改用method syntax:
({
test(){},
print(){}
})