为什么第二个函数声明获胜,即使我在它之前返回
Why does second function declaration win even though I return before it?
我有以下JavaScript代码:
(function() {
function f(){ alert(1); }
return f();
function f(){ alert(2); }
})();
你能解释一下为什么警报会弹出2而不是1吗?
谢谢,
这涉及到当执行进入函数时会发生什么:省略了很多细节,所有函数声明(您使用的样式(都会被处理,只有在之后才会逐步执行代码。因此,return
语句对选择哪个函数声明没有影响。选择的声明始终是源代码顺序中的最后一个声明(这在规范的第10.5节中用极其夸张的散文进行了介绍(。
如果您使用函数表达式,结果将从根本上不同,这些表达式作为逐步代码的一部分进行评估:
(function() {
var f;
f = function(){ alert(1); };
return f();
f = function(){ alert(2); };
})();
从技术上讲,此代码是不正确的(因为您有一个始终遵循return
的代码(,但它说明了差异:您看到的是alert(1)
而不是alert(2)
,因为这些表达式在达到它们之前不会求值。
您可以在本规范的第10.4.3节和第10.5节中阅读更多关于执行进入函数时会发生什么的信息(在第一步代码之前,声明并不是唯一要做的事情(。
用你的新知识,一个小测验:这里发生了什么?(注意:永远不要这样做。(
function foo() {
if (true) {
function bar() {
alert(1);
}
}
else {
function bar() {
alert(2);
}
}
bar();
}
foo();
答案是:各不相同,不要那样做一些引擎将使用第一个bar
,其他引擎将使用第二个,其他引擎则称其为语法错误。这是因为这实际上是一个错误,所以引擎可以自由地做他们认为最好的事情。如果您仔细研究语言语法,您会发现将函数声明放在其直接包含范围内的分支内是无效的。它们必须处于该范围的最高级别。随着你对声明的新理解,原因应该是显而易见的:它们与范围内的执行流无关,因此自然不能根据执行流来选择它们。
那么现实世界中会发生什么呢?遗憾的是,如果你处于"宽松"模式(不严格(,通常不会出错。一些引擎(例如,截至本文撰写之时,Chrome的V8(会忽略流控制语句,只选择最后声明的函数(因此,您会得到使用第二个bar
函数的反直觉结果(,其他引擎(Firefox的SpiderMonkey、IE11的JScript(会有效地动态重写您的代码,将这些声明转换为表达式,所以你得到了第一个CCD_ 7。例如,它会因发动机而异。好消息是,如果您在严格模式下尝试此操作,所有这三个(V8、SpiderMonkey和IE11的JScript(都将失败,而不是选择一个(V8和SpiderMonkeys在控制台中有清晰的错误消息;JScript只有令人惊讶的"bar
未定义",但是…(。
如果你想做一些类似的事情,但在引擎之间有效且一致,请使用表达式:
function foo() {
var bar;
if (true) {
bar = function() {
alert(1);
};
}
else {
bar = function() {
alert(2);
};
}
bar();
}
foo();
在kangax的命名函数表达式解密页面上有一个有趣的探索。
有一种叫做"提升"的东西——它是范围/功能激活解决过程的必然结果(不需要太多细节(。
这意味着,在进入变量和函数的作用域时,函数声明将被处理和存储,就像它们是在作用域的开头一样。作业部分留在原地。
所以下面的代码:
function()
{
console.log(a)
var a = 5;
}
将打印"未定义",这是因为,简单地说,它相当于:
function()
{
var a;
console.log(a)
a = 5;
}
在您的示例中,有一件重要的事情需要记住-这些是函数声明-因此它们将像任何变量一样进行处理,但由于它们"包含"函数体,因此整个声明(包括等效的a = 5
(将被"提升"。
在变量处理阶段,如果已经处理了同名变量,则替换其值和属性。
所以你的代码相当于:
(function() {
function f(){ alert(2); }
return f();
})();
因为匿名函数的主体并不像您所期望的那样是过程性的。
相反,它是一个具有属性的对象。并且允许正向引用——也就是说,一个函数中的代码可以引用稍后在对象中定义的属性。
由于同一属性有重复的定义,因此后一个定义优先,有效地覆盖了第一个定义。因此,您的代码片段相当于(删除第一个被覆盖的定义(:
(function() {
return f();
function f(){ alert(2); }
})();
在这一点上,警报包含CCD_ 10应该不会让您感到惊讶。
因为代码是首先解析的,所以f((函数是被编写的,而不是被执行的,最后一个f((功能是被调用的。
- 使用返回函数sinde.attr()jquery元素
- 从自执行函数返回函数的Javascript性能命中率
- Node Express Handlebars帮助程序未返回函数的结果
- 未在Firefox中执行PageMethod的返回函数
- 对返回函数的函数感到困惑
- 从承诺返回不返回函数会导致警告
- 从函数返回函数而不调用返回的函数
- Javascript,闭包中的返回函数如何与外部函数连接
- 为什么Coderbyte.com's的Javascript模板喜欢返回函数的原始参数
- 对象函数返回函数而不是值
- 从外部函数(数组)了解返回函数(x)
- 从Javascript类对象返回函数
- 调用Typescript setter don't返回函数,尽管关联的getter可以工作
- 使用依赖注入在 JavaScript 中返回函数
- 通过单击JSP和javascript加载两个返回函数
- 需要说明:无法理解返回函数的javascript
- 简单的onClick返回函数不起作用
- 为什么这个闭包返回函数
- JS函数返回函数供以后使用-未定义参数
- Coffeescription类中的方法返回函数而不是字符串