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 重新开始。
http://bonsaiden.github.io/JavaScript-Garden/zh/#core.eval