如果函数通过属性添加到对象中,function.name将返回空字符串

3
请参考以下代码示例:

考虑以下代码示例:

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属性不同?

1
其中一个执行了NamedEvaluation,另一个则没有。 - Sebastian Simon
当您将一个函数分配给现有对象上的键时,该函数不会被命名。这是TC39的设计决策,因为它被认为是过多的信息泄漏。 - Yousaf
非常感谢,但是有没有办法获取以以下方式声明的函数的名称,还是它将始终没有名称? - vitkorelmer
2个回答

3

技术解释:NamedEvaluation

规范告诉你:

({ test: function(){} })

13.2.5.5 运行时语义学:属性定义求值

属性定义 : 属性名 : 赋值表达式

  1. propKey成为评估属性名的结果。
  2. [...]
  3. 如果是匿名函数定义(赋值表达式)为真,那么
    1. propValue成为带有参数propKey赋值表达式命名求值的结果?。
  4. 否则,
    1. exprValueRef成为评估赋值表达式的结果。
    2. propValue成为?GetValue(exprValueRef)的结果。
  5. [...]
在第三步中,表达式function(){}IsAnonymousFunctionDefinition返回true,因为它是函数定义,且缺少BindingIdentifier
因此,执行命名评估:使用"test"作为name属性的值创建函数。
test.print = function(){};

13.15.2 运行时语义:求值

赋值表达式左手表达式 = 赋值表达式

  1. 评估左手表达式的结果为lref
  2. [...]
  3. 如果是匿名函数定义赋值表达式)和左手表达式IsIdentifierRef两者都为true,则
    1. 使用参数lref.[[ReferencedName]]评估赋值表达式NamedEvaluation的结果为rval
  4. 否则,
    1. 评估赋值表达式的结果为rref
    2. 使用? GetValue(rref),获取rval
  5. 执行? PutValue(lref, rval)。
  6. [...]
在第三步中,
  • 表达式function(){}IsAnonymousFunctionDefinition返回true,就像其他代码片段一样。
  • 但是,表达式test.printIsIdentifierRef返回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(){}; // "property"
但请记住,当时这仍然是一份未完成的提案。 当提案被纳入规范时,似乎经历了一些改变,但提案文章没有改变以反映这些变化。
这个线程提到了某些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)) // Or `.map(({description}) => description)`
);

你最好这样定义方法:

test.print = function print(){};

然而,考虑使用方法语法代替:

({
  test(){},
  print(){}
})

0

你所定义的print是使用匿名函数,访问它们的名称属性将返回''。在这里阅读更多。

如果你仍然想让代码工作,你可以像这样改变你的代码:

 test.print = function print() {

    }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接