JavaScript 会提升变量声明。这意味着 var
表达式和 function
声明都将会被提升到当前作用域的顶部。
bar();
var bar = function() {};
var someValue = 42;
test();
function test(data) {
if (false) {
goo = 1;
} else {
var goo = 2;
}
for(var i = 0; i < 100; i++) {
var e = data[i];
}
}
上面代码在运行之前将会被转化。JavaScript 将会把 var
表达式和 function
声明提升到当前作用域的顶部。
// var 表达式被移动到这里
var bar, someValue; // 缺省值是 'undefined'
// 函数声明也会提升
function test(data) {
var goo, i, e; // 没有块级作用域,这些变量被移动到函数顶部
if (false) {
goo = 1;
} else {
goo = 2;
}
for(i = 0; i < 100; i++) {
e = data[i];
}
}
bar(); // 出错:TypeError,因为 bar 依然是 'undefined'
someValue = 42; // 赋值语句不会被提升规则(hoisting)影响
bar = function() {};
test();
主意test函数中的goo=1变量, 第一次出现的时候,并没有使用var,在我们的理解中,foo被隐式的声明成了全局变量,
但是 在后面又出现了一个 var goo=2,声明了是一个局部的变量,根据变量提升的规则,
var goo会被提升到顶部, var goo = 'undefined'
所以 goo是一个局部变量。
// 来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则
var myvar = 'my value';
(function() {
alert(myvar); // undefined
var myvar = 'local value';
})();
这一段代码说明了很多问题。
var myvar ='my value';
(function(){
var myvar = undefined // 变量声明被提升到了这个位置。
alert(myvar);// undefined
var myvar ='local value';
})();
在《悟透Javascript》里有这样一个例子
var myFunc = function(){
alert('Hello');
}
myFunc(); // 输出Hello
myFunc = function(){
alert('yeah');
}
myFunc(); 第二次调用输出 yeah
这没什么好奇怪的。 然后我们再看下面一段代码
function myFunc(){
alert('Hello');
}
myFunc(); // 第一次调用 输出yeah;很奇怪吧
function myFunc(){
alert('yeah');
}
myFunc(); // 还是输出 yeah!
这里面还是变量申明别提前的原因,不做多解释
引用书中原话
原来,JavaScript 执行引擎并非一行一行地分析和执行程序,而是一段一段地分析执行的。而且,在
同一段程序的分析执行中,定义式的函数语句会被提取出来优先执行。函数定义执行完之后,才会按顺序
执行其他语句代码。也就是说,在第一次调用 myfunc之前,第一个函数语句定义的代码逻辑,已被第二
个函数定义语句覆盖了。所以,两次都调用都是执行最后一个函数逻辑了。
如果把这个 JavaScript 代码分成两段,例如将它们写在一个html中,并用<script/> 标签将其分成
这样的两块
< script >
function myfunc ()
{
alert("hello");
};
myfunc(); // 这里调用 myfunc,输出 hello
</script >
< script >
function myfunc ()
{
alert("yeah");
};
myfunc(); // 这里调用 myfunc,输出 yeah
</script >
所以总结如下:
名称解析顺序
JavaScript 中的所有作用域,包括全局作用域,都有一个特别的名称 this 指向当前对象。
函数作用域内也有默认的变量 arguments,其中包含了传递到函数中的参数。
比如,当访问函数内的 foo
变量时,JavaScript 会按照下面顺序查找:
- 当前作用域内是否有
var foo
的定义。 - 函数形式参数是否有使用
foo
名称的。 - 函数自身是否叫做
foo
。 - 回溯到上一级作用域,然后从 #1 重新开始。