我有以下JavaScript代码:
(function() {
function f(){ alert(1); }
return f();
function f(){ alert(2); }
})();
你能解释为什么警告弹出2而不是1?
谢谢,
答案 0 :(得分:9)
这涉及到执行进入函数时会发生什么:省略了很多细节,所有函数声明(你使用过的样式)都被处理了,只有之后> em>一步一步执行代码执行。因此,您的return
语句对选择哪个函数声明没有影响。并且所选择的声明始终是源代码顺序中的最后一个(这包含在 - 非常粗糙的散文中 - 在规范的Section 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,在撰写本文时)将忽略流控制语句,只选择最后声明的函数(因此您得到的反直觉结果是 second {{1}使用函数),其他引擎(Firefox的SpiderMonkey,IE11的JScript)有效地重写您的代码,将这些声明转换为表达式,因此您得到第一个bar
。例如,它会因引擎而异。好消息是,如果您在严格模式下尝试此操作,那么所有这三个(V8,SpiderMonkey和IE11的JScript)将失败而不是选择一个(V8和SpiderMonkey)在控制台中有明确的错误消息; JScript只有令人惊讶的“bar
未定义”,但......)。
如果你想做之类上面的内容,但是跨引擎有效且一致,请使用表达式:
bar
在kangax's Named Function Expressions Demystified page上有一个有趣的探索。
答案 1 :(得分:2)
有些东西被称为“吊装” - 它是范围/功能激活解决过程的必然结果(没有太多细节)。
这意味着在进入变量函数的范围时,函数声明将被处理和存储,就好像它们是在作用域的开头一样。作业部分保留在原来的位置。
以下代码:
function()
{
console.log(a)
var a = 5;
}
将打印“undefined”,这是因为,简单地说,它等同于:
function()
{
var a;
console.log(a)
a = 5;
}
在你的例子中 - 有一件重要的事情需要记住 - 这些是函数声明 - 因此它们会被处理为任何变量,但由于它们“包含”了函数体,所以整个声明(包括等同于a = 5
)是“悬挂的”。
在变量处理阶段 - 如果已经处理了同名变量 - 它的值和属性被替换。
所以你的代码相当于:
(function() {
function f(){ alert(2); }
return f();
})();
答案 2 :(得分:1)
因为匿名函数的主体不像你期望的那样是程序性的。
而是它是一个具有属性的对象。并且允许前向引用 - 也就是说,一个函数中的代码可以引用稍后在对象中定义的属性。
由于您具有相同属性的重复定义,后者优先,有效地覆盖第一个属性。因此,您的代码段相当于(删除第一个被覆盖的定义):
(function() {
return f();
function f(){ alert(2); }
})();
此时警报包含2
。
答案 3 :(得分:0)
因为首先解析代码,所以f()函数被承保,而不是执行,并调用最后一个f()函数。