【问题标题】:function.name returns empty string if the function is added to an object by property如果函数通过属性添加到对象,则 function.name 返回空字符串
【发布时间】:2021-05-31 07:31:49
【问题描述】:

考虑这个代码示例:

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(){} }

为什么这两个函数的name 属性有区别?

【问题讨论】:

  • 一个执行NamedEvaluation,另一个不执行。
  • 当您将函数分配给现有对象上的键时,函数不会获得名称。这是 TC39 的设计决定,因为它被认为是过多的信息泄露。
  • 非常感谢,但是有没有办法获取以下面的方式声明的函数的名称,还是无论如何都没有名称?

标签: javascript function object


【解决方案1】:

技术解释:NamedEvaluation

规范告诉你:

({ test: function(){} })

13.2.5.5 Runtime Semantics: PropertyDefinitionEvaluation

PropertyDefinition : PropertyName : AssignmentExpression

  1. propKey 为评估 PropertyName 的结果。
  2. […]
  3. 如果IsAnonymousFunctionDefinition(AssignmentExpression) 为真,那么
    1. propValue 是 ? AssignmentExpressionNamedEvaluation,带有参数 propKey
  4. 否则,
    1. exprValueRef 为评估 AssignmentExpression 的结果。
    2. propValue 是 ? GetValue(exprValueRef).
  5. […]

在第 3 步中,表达式function(){}IsAnonymousFunctionDefinition 返回true,因为它是is a function definition,它是lacks a BindingIdentifier

因此,执行了NamedEvaluation:该函数使用"test" 作为name 属性的值创建。


test.print = function(){};

13.15.2 Runtime Semantics: Evaluation

AssignmentExpression : LeftHandSideExpression = AssignmentExpression

  1. lref 为评估 LeftHandSideExpression 的结果。
  2. […]
  3. 如果 LeftHandSideExpressionIsAnonymousFunctionDefinition(AssignmentExpression) 和 IsIdentifierRef 都是 true,那么
    1. rvalAssignmentExpressionNamedEvaluation,参数为 lref。[[ReferencedName]]。
  4. 否则,
    1. rref 为评估 AssignmentExpression 的结果。
    2. rval 是 ? GetValue(rref).
  5. 执行? PutValue(lref, rval).
  6. […]

在第 3 步中,

  • 表达式 function(){}IsAnonymousFunctionDefinition 返回 true,就像它在另一个 sn-p 中所做的一样,
  • 但表达式test.printIsIdentifierRef 返回false:它是MemberExpression,而不是标识符

因此,采用第 4 步,即“Else”情况,不执行NamedEvaluation


基本原理:安全

唯一的区别是额外的 IsIdentifierRef 步骤,这也是 key to findingrationale(在 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(){}
})

【讨论】:

    【解决方案2】:

    您定义打印的方式是使用 匿名 函数并访问它们的 name 属性将返回 ''。阅读更多here

    如果您仍然想让代码正常工作,您可以像这样更改代码:

     test.print = function print() {
    
        }
    
    

    【讨论】:

      猜你喜欢
      • 2013-05-24
      • 1970-01-01
      • 1970-01-01
      • 2015-08-07
      • 2023-01-26
      • 1970-01-01
      • 2022-01-07
      • 2020-05-09
      相关资源
      最近更新 更多