Javascript: 将命名函数赋值给变量时出现不一致情况(命名函数表达式)

5

有人能解释一下Internet Explorer和Firefox在以下方面的行为差异吗:

var myNamespace = (function () {
  var exposed = {};

  exposed.myFunction = function myFunction () {
    return "Works!";
  }

  console.log(myFunction()); 
  // IE: "Works!"
  // Firefox: ReferenceError: myFunction is not defined

  console.log(exposed.myFunction());
  // IE: "Works!"
  // FF: "Works!"

  return exposed;
})();

console.log(myNamespace.myFunction()); 
// IE: "Works!"
// FF: "Works!"

在Internet Explorer中,使用该方法可以让我从命名空间函数内部调用我的函数,使用myFunction()或者exposed.myFunction()都可以。
在我的命名空间函数外面,我可以使用myNamespace.myFunction()
在Firefox中,结果相同,但是裸的函数调用不起作用。
这个应该工作吗?如果不应该,为什么?
如果应该,那么这是一个已知的bug吗?

1
这可能会回答你的问题:http://stackoverflow.com/questions/2553632/detachevent-not-working-with-named-inline-functions,https://dev59.com/8k3Sa4cB1Zd3GeqPuE45。 - Felix Kling
2
简而言之:不,它不应该工作,这是IE的一个错误。 - Felix Kling
不要给数组分配任意属性,只使用数字属性名称。如果您想分配任意属性,请改用对象。请参阅 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Working_with_Objects。 - Felix Kling
同意。我已将其更改为对象,这本来就是它的目的。 - michaelward82
3个回答

6
为了防止虚假信息:
IE存在一个问题,即命名函数表达式,这就是你的情况。函数的名称应该只在函数内部可用。根据规范
FunctionExpression”被定义为:

FunctionExpression :
     function Identifieropt ( FormalParameterListopt ) { FunctionBody }

FunctionExpression中,“Identifier”可以从函数体内部引用,以允许函数递归调用自身。但是,与FunctionDeclaration不同,在FunctionExpression中,“Identifier”不能被引用,并且不影响包围FunctionExpression的范围。

但是在IE中,它不仅使名称仅在函数内部可用,而且创建两个不同的函数对象,一个分配给变量,另一个分配给您给出的函数名称。以下代码在IE中将产生false(并在其他浏览器中抛出错误):
exposed.myFunction === myFunction;

这是一个已知的 bug,如果你要编写(旧版本)IE 的代码,最好避免使用命名函数表达式。


相关问题:


太棒了!这完美地解释了为什么上面的代码在IE中“有效”,而在Firefox中不起作用。当然,在Firefox中的行为并不是我所期望的,但它符合规范,这才是最重要的。 - michaelward82
console.log(myFunction()); 是一个在IE中工作的调用本地函数的语句,根据您的规格说明,该函数应该存在。在这个上下文中,FunctionBody是exposed的最近封套,也就是裸调用的确切位置。 - Sebas
@Sebas:不,我们正在谈论 FunctionExpression 的 FunctionBody,而不是它所定义的 FunctionBody。也许你还没有看到我的更新。根据规范,它不应该存在。 - Felix Kling
我理解你的观点。也许我理解有误,但在我看来,这里存在一个问题,因为我们正在谈论声明函数及其相关作用域(在这方面我完全同意你的帖子),而实际上存在一个覆盖函数赋值。请参阅我的帖子:函数的声明明显在exposed的主体之外,尽管后来将其作为属性影响到它。但也许我误解了引擎。 - Sebas
@Sebas:说实话,我无法理解你最后的评论。手头的问题是IE在定义函数的作用域中创建了一个带有函数表达式名称的符号,这是错误的。其他所有内容都不相关。 - Felix Kling

2

This:

exposed.myFunction = function myFunction () {
    return "Works!";
}

我要更正一下,之前说这是无用的是错误的,但是显然存在缺陷(请参见Felix的帖子)。如果您想将函数声明为exposed的一部分,则不需要给它命名:

exposed.myFunction = function() {
    return "Works!";
}

然而,如果你想声明一个本地函数并且将其附加到你的数组exposed中,你可以这样做:

function myFunction () {
    return "Works!";
}

exposed.myFunction = myFunction;

在这种情况下,(exposed.myFunction === myFunction) === true;可以成功验证。同时,注意到调用console.log(myNamespace.myFunction());与调用console.log(exposed.myFunction());是相同的,因为myNamespace实际上就是exposed对象。请注意保留HTML标签,但不要添加解释,并尽可能让内容更加通俗易懂。

这是不正确的。它是一个命名函数表达式,是完全有效的 JavaScript。 - Felix Kling
如果 Firefox 允许执行“console.log(myFunction());”,那么就没有矛盾;否则就存在不一致性。 - Sebas
不,IE 应该正确实现 JavaScript。 - Felix Kling
我的意思是,如果你说第一个表达式是正确的,那么这意味着裸函数myFunction的声明应该在当前上下文中被考虑,因此可以有效地按原样调用。 - Sebas
但这不是函数表达式的定义方式。请参见我的答案。 - Felix Kling

0

另外,通过定义

exposed.myFunction = function() {}

你正在将exposed属性设置为myFunction

但它在myNamespace范围内不可用。

通过定义

exposed.myFunction = function myFunction() {}

该函数是在 myNamespace 的范围内创建的,然后作为 exposed 的属性引用。

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