[英]Need to understand Javascript function hoisting example
我读了Javascript Hoisting的概念。它让我感到很困惑,但是我看到了一些例子,并且知道吊装实际上是做什么的。
所以基本上“ Hoisting是JavaScript将所有声明移动到当前作用域顶部(到当前脚本或当前函数的顶部)的默认行为。 ”
但我无法理解以下实施:
var is_android = true;
if (is_android) {
function foo() {
alert('I am Android');
}
} else {
function foo() {
alert('I am NOT Android');
}
}
foo();
输出在警告框中显示“ 我不是Android ”。
我想知道为什么即使is_android
值为true,也会从else块调用foo()
。
任何帮助将不胜感激。
tl; dr:不要在块中使用看起来像函数声明的东西,尤其不是条件。
事实上,大多数浏览器都以错误的方式解释这段代码。 它们将函数定义视为函数声明 ,即使函数声明不允许在块内,因为函数声明不是语句 ,它是源元素 。
这一般如何工作:
在代码执行之前,解释器会查找所有变量和函数声明,而不管它们在何处 ,并在当前/新环境中为它们创建绑定。 然后它开始实际执行代码。
因此,假设函数定义被解释为声明 ,因为有两个声明,最后一个声明获胜。 您的代码基本上变为:
function foo() {
alert('I am Android');
}
function foo() {
alert('I am NOT Android');
}
var is_android;
is_android = true;
if (is_android) {
} else {
}
foo();
其他引擎会以不同的方式解释它 ,但仍然不正确(IMO,见下文):
在以下脚本中,零函数永远不会被定义,也不能被调用,因为'if(0)'将其条件评估为false:
if (0) { function zero() { document.writeln("This is zero."); } }
注意:某些JavaScript引擎(不包括SpiderMonkey)错误地将任何函数表达式视为函数定义。 即使使用always-false if条件,这也会导致零被定义。 有条件地定义函数的更安全的方法是匿名定义函数并将其分配给变量:
if (0) { var zero = function() { document.writeln("This is zero."); } }
但是在这种情况下,如果函数定义真的被解释为函数表达式 (类似于(function zero() { ... })
),则函数的名称将无法在包含范围内访问,并且函数将只是迷路了。
在Javascript中,两个相似的外观之间存在细微差别
function square(x) { return x*x; }
和
var square = function(x) { return x*x; }
最大的区别在于,在第一种情况下,将函数对象分配给square
名称是在输入范围时完成的,而不是在到达线时。 这允许调用稍后在源代码中定义的函数...例如:
console.log(cube(12));
function cube(x) { return x*x*x; }
是一个有效的脚本,即使调用发生在函数定义“之前”(顺便说一下,允许这种代码是IMO规则在语言中的原因),它也会起作用。
在第二种情况下,赋值只是一个常规语句,并且当控制流通过它时(和如果)执行它。
如果您希望该代码段按预期工作,则只需更改代码即可
function <name> (...) { ... }
至
var <name> = function (...) { ... }
PS:您也可以在第二种形式中重复该名称,但通常不会这样做,而是使用匿名函数。
http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
这是我读过有关招待的最好的文章。
声明,名称和吊装
在JavaScript中,名称以四种基本方式之一进入范围:
语言定义:默认情况下,所有范围都是名称this和arguments。 形式参数:函数可以具有命名形式参数,其范围限定为该函数的主体。 函数声明:它们的形式为函数foo(){}。 变量声明:这些声明采用var foo;形式。 函数声明和变量声明总是被JavaScript解释器无形地移动(“提升”)到其包含范围的顶部。 显然,功能参数和语言定义的名称已经存在。 这意味着代码如下:
function foo() {
bar();
var x = 1;
}
实际上解释如下:
function foo() {
var x;
bar();
x = 1;
}
事实证明,包含声明的行是否会被执行并不重要。 以下两个函数是等效的:
function foo() {
if (false) {
var x = 1;
}
return;
var y = 1;
}
function foo() {
var x, y;
if (false) {
x = 1;
}
return;
y = 1;
}
请注意,声明的赋值部分未被提升。 只有名字被悬挂。 函数声明不是这种情况,整个函数体也将被提升。 但请记住,声明函数有两种常用方法。 考虑以下JavaScript:
function test() {
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { // function expression assigned to local variable 'foo'
alert("this won't run!");
}
function bar() { // function declaration, given the name 'bar'
alert("this will run!");
}
}
test();
在这种情况下,只有函数声明将其主体提升到顶部。 名称'foo'被悬挂,但身体被遗忘,在执行期间被分配。
这涵盖了吊装的基础知识,这并不像看起来那么复杂或令人困惑。 当然,这是JavaScript,在某些特殊情况下会有一些复杂性。
您可能已经了解了声明函数以避免提升的方法,但如果不是:您可以将其写为:
var is_android = true;
if (is_android) {
var foo = function() {
alert(5);
};
} else {
var foo = function() {
alert(7);
};
}
foo();
在javascript解释器评估条件语句之前,不会评估和foo()。
你在混合概念。
从这段代码开始:
function foo() {
alert('1');
}
function foo() {
alert('2');
}
foo();
这不会给出错误。 而只是警报2.因为foo()的第二个定义会覆盖第一个。
立即尝试以下代码:
if (false) {
function foo() { alert('false');}
}
foo();
这只是警告错误。 即使foo是在未执行的块内定义的(如果为false),函数声明总是由JavaScript处理。
记住这两个例子很容易理解代码中发生的事情:你定义两次函数foo,第二个定义覆盖第一个。
您在代码中尝试的内容非常接近“条件编译”,并且可以在JavaScript中将此行为模拟为将变量声明为函数:
if (true) {
var foo = function() {alert ('true');}
}
else {
var foo = function() {alert ('false');}
}
foo();
此代码只是警告真实。 注意,现在foo被定义为一个变量(包含一个函数,但是一个变量)。
问候
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.