技术解释:NamedEvaluation
规范告诉你:
({ test: function(){} })
属性定义 : 属性名 :
赋值表达式
- 令propKey成为评估
属性名
的结果。
- [...]
- 如果是匿名函数定义(赋值表达式)为真,那么
- 令propValue成为带有参数
propKey
的赋值表达式的命名求值的结果?。
- 否则,
- 令exprValueRef成为评估赋值表达式的结果。
- 让propValue成为?GetValue(exprValueRef)的结果。
- [...]
在第三步中,表达式
function(){}
的
IsAnonymousFunctionDefinition返回
true,因为它
是函数定义,且
缺少BindingIdentifier。
因此,执行
命名评估:使用
"test"
作为
name
属性的值创建函数。
test.print = function(){};
赋值表达式 :左手表达式 =
赋值表达式
- 评估左手表达式的结果为lref。
- [...]
- 如果是匿名函数定义(赋值表达式)和左手表达式的IsIdentifierRef两者都为true,则
- 使用参数lref.[[ReferencedName]]评估赋值表达式 的NamedEvaluation的结果为rval。
- 否则,
- 评估赋值表达式的结果为rref。
- 使用? GetValue(rref),获取rval。
- 执行? PutValue(lref, rval)。
- [...]
在第三步中,
- 表达式
function(){}
的IsAnonymousFunctionDefinition返回true,就像其他代码片段一样。
- 但是,表达式
test.print
的IsIdentifierRef返回false:它是一个MemberExpression,而不是Identifier。
因此,在第四步中,“否则”情况将发生,并且不执行
NamedEvaluation。
原理:安全
唯一的区别是多了一个额外的IsIdentifierRef步骤,这也是找到原理(在esdiscuss.org上)的{{key}}:
2015年7月25日14:22:59 UTC a.d.bergi at web.de写道:
[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.
这是其中一篇回复:
2015-07-26 19:48:15 UTC Allen Wirfs-Brock写道(强调是我的):
属性键可能是符号的可能性是此表达式形式不设置name
属性的主要原因。
还可能存在安全问题。 name
属性可能通过初始分配给它的变量的函数对象泄漏其名称。但是,除了源函数之外,没有多少人可以使用本地变量名。但是,泄露的属性名称可能具有更大的功能。
Allen接着说2015-07-26 20:33:07 UTC:
TC39达成共识,对于以下表达式形式自动分配
name
属性:
Identifier =
FunctionExpression
因此它是ES2015的一部分。我们
没有就以下内容达成共识:
MemberExpression .
IdentifierName =
FunctionExpression
或者
MemberExpression [
IdentifierName ]
=
FunctionExpression
因此它不是ES2015的一部分。在采用这个方案之前,必须克服
各种异议。
另一条评论,2016年12月13日09:03:40 UTC由T.J. Crowder发表,指出那些“各种反对意见”不清楚。他们链接到原始提案(存档自wiki.ecmascript.org,2016年9月15日,最后可用版本),其中确实通过示例列出了您期望的行为:
obj.property = function(){};
但请记住,当时这仍然是一份未完成的提案。
当提案被纳入规范时,似乎经历了一些改变,但提案文章没有改变以反映这些变化。
这个线程提到了某些TC39会议记录,在那里讨论了这个问题,但没有提供链接。
艾伦声称这种信息泄漏是TC39无法就允许这种行为达成共识的原因。
他们提到
2017-01-28 15:46:54 UTC:
“对于
cache[getUserSecret(user)] = function(){};
,它将用户秘密信息泄漏为
name
的值。”
尽管正如T.J. Crowder在
2017-01-28 16:11:26 UTC所提到的那样,相同的事情也是可能的:
cache = {
[getUserSecret(user)]: function() {}
};
匿名函数没有简单的解决方法
"有没有办法获取声明的函数的名称[例如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))
);
你最好这样定义方法:
test.print = function print(){};
然而,考虑使用方法语法代替:
({
test(){},
print(){}
})