简体   繁体   English

如果 function 通过属性添加到 object,则 function.name 返回空字符串

[英]function.name returns empty string if the function is added to an object by property

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?为什么这两个函数的name属性有区别?

The technical explanation: NamedEvaluation技术说明:NamedEvaluation

The specification tells you:规范告诉你:

({ test: function(){} })

13.2.5.5 Runtime Semantics: PropertyDefinitionEvaluation 13.2.5.5 运行时语义:PropertyDefinitionEvaluation

PropertyDefinition : PropertyName : AssignmentExpression PropertyDefinitionPropertyName : AssignmentExpression

  1. Let propKey be the result of evaluating PropertyName .propKey成为评估PropertyName的结果。
  2. […] […]
  3. If IsAnonymousFunctionDefinition ( AssignmentExpression ) is true, then如果IsAnonymousFunctionDefinition ( AssignmentExpression ) 为真,则
    1. Let propValue be?propValue成为? NamedEvaluation of AssignmentExpression with argument propKey .带参数propKeyAssignmentExpressionpropKey
  4. Else,别的,
    1. Let exprValueRef be the result of evaluating AssignmentExpression .exprValueRef为评估AssignmentExpression的结果。
    2. Let propValue be?propValue成为? GetValue ( exprValueRef ).获取值 (exprValueRef )
  5. […] […]

In step 3, IsAnonymousFunctionDefinition of the expression function(){} returns true , because it is a function definition , and it lacks a BindingIdentifier .在步骤 3 中,表达式function(){}IsAnonymousFunctionDefinition返回true ,因为它是 function 定义,并且缺少 BindingIdentifier

Therefore, a NamedEvaluation is performed: the function is created with "test" as the value of the name property.因此,将执行NamedEvaluation :创建 function,并将"test"作为name属性的值。


test.print = function(){};

13.15.2 Runtime Semantics: Evaluation 13.15.2 运行时语义:评估

AssignmentExpression : LeftHandSideExpression = AssignmentExpression赋值表达式: LeftHandSideExpression =赋值表达式

  1. Let lref be the result of evaluating LeftHandSideExpression .lref为评估LeftHandSideExpression的结果。
  2. […] […]
  3. If IsAnonymousFunctionDefinition ( AssignmentExpression ) and IsIdentifierRef of LeftHandSideExpression are both true , then如果LeftHandSideExpressionIsAnonymousFunctionDefinition ( AssignmentExpression ) 和IsIdentifierRef都为true ,则
    1. Let rval be NamedEvaluation of AssignmentExpression with argument lref .[[ReferencedName]].rval为带有参数lref .[[ReferencedName]] 的AssignmentExpressionNamedEvaluation
  4. Else,别的,
    1. Let rref be the result of evaluating AssignmentExpression .rref为评估AssignmentExpression的结果。
    2. Let rval be?rval成为? GetValue ( rref ).获取值 (rref )
  5. Perform?履行? PutValue ( lref , rval ).放置值( lref rval )。
  6. […] […]

In step 3,在第 3 步中,

  • IsAnonymousFunctionDefinition of the expression function(){} returns true , just like it does in the other snippet,表达式function(){}IsAnonymousFunctionDefinition返回true ,就像在另一个片段中一样,
  • but IsIdentifierRef of the expression test.print returns false : it's a MemberExpression , not an Identifier .但是表达式test.printIsIdentifierRef返回false :它是MemberExpression ,而不是Identifier

Therefore, step 4, the “Else” case, is taken and no NamedEvaluation is performed.因此,采取步骤 4,即“Else”情况,不执行NamedEvaluation


The rationale: security理由:安全

The only difference is the additional IsIdentifierRef step, which is also the key to finding the rationale (on esdiscuss.org):唯一的区别是额外的IsIdentifierRef步骤,这也是找到基本原理的关键(在 esdiscuss.org 上):

2015-07-25 14:22:59 UTC adbergi at web.de writes: 2015-07-25 14:22:59 UTC adbergi 在 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: [如果] function(或类)定义被分配给 object 的属性,[function 自动发生:给出目标标识符的名称] 不会

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): 2015-07-26 19:48:15 UTC Allen Wirfs-Brock写道(强调我的):

[T]he possibility that the property key is a symbol is a primary reason that this expression form does not set the name property . [T]属性键是符号的可能性是这种表达形式没有设置name属性的主要原因。

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. name属性可能会通过 function object 泄漏它最初分配给的变量的名称。 But there isn't much someone could do with a local variable name, outside of the originating function.但是除了原始的 function 之外,没有人可以对局部变量名做太多事情。 But a leaked property name potentially carries a greater capability.但是泄露的属性名称可能具有更大的功能。

Allen goes on, 2015-07-26 20:33:07 UTC :艾伦继续说, 2015-07-26 20:33:07 UTC

TC39 reached consensus on automatically assigning the name property for expression forms like: TC39 就自动为表达式 forms 分配name属性达成共识,例如:

Identifier = FunctionExpression标识符=函数表达式

and so it is part of ES2015.所以它是 ES2015 的一部分。 We did not have consensus on doing the same for:我们没有就对以下方面做同样的事情达成共识

MemberExpression .成员表达式. IdentifierName = FunctionExpression标识符名称=函数表达式

or或者

MemberExpression [ IdentifierName ] = FunctionExpression MemberExpression [ IdentifierName ] = FunctionExpression

so it is not part of ES2015.所以它不是 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. TJ Crowder 于 2016 年 12 月 13 日09:03:40 UTC发表的另一条评论指出,目前尚不清楚这些“各种反对意见”是什么。 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:它们链接到原始提案(来自 wiki.ecmascript.org,2016-09-15,最后可用版本),该提案确实在示例中列出了您的预期行为:

 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.该线程提到了某些讨论此问题的 TC39 会议记录,但未提供链接。 Allen is claiming that this kind of information leak is the reason why TC39 couldn't reach a consensus on allowing this behavior. Allen 声称这种信息泄露是 TC39 无法就允许这种行为达成共识的原因。 They mention 2017-01-28 15:46:54 UTC :他们提到2017-01-28 15:46:54 UTC

[F]or cache[getUserSecret(user)] = function(){}; [F] 或cache[getUserSecret(user)] = function(){}; it would leak the secret user info as the value of name .它会将秘密用户信息作为name的值泄漏。

Even though, as TJ Crowder mentions 2017-01-28 16:11:26 UTC , the same thing is possible with:即使,正如 TJ Crowder 提到的2017-01-28 16:11:26 UTC ,同样的事情是可能的:

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

There isn't an easy workaround with anonymous functions匿名函数没有简单的解决方法

“Is there a way to get the name of a function declared [like test.print = function(){}; “有没有办法获得声明的 function 的名称 [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.您可以遍历对象的条目并找到值与 function 匹配的键名的候选者。 But if the function is referenced by another key name, you may get multiple results.但是如果 function 被另一个键名引用,您可能会得到多个结果。 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 ''.您如何定义 print 是使用匿名函数并访问它们的 name 属性将返回 ''。 Read more here 在这里阅读更多

If you still want to make the code work you can change your code like this:如果您仍然想让代码工作,您可以像这样更改代码:

 test.print = function print() {

    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM