var a;
function a(){
}
alert(a);//打印出a的函数体
</script>
//但是要注意区分和下面两个写法的区别:
<script>
var a=1;
function a(){
}
alert(a);//打印出1
</script>
<script>
function a(){
}
var a=1;
alert(a);//打印出1
</script>
这里有3个例外:
1 内置的名称arguments表现得很奇怪,他看起来应该是声明在函数形式参数之后,但是却在函数声明之前。这是说,如果形参里面有arguments,它会比内置的那个有优先级。这是很不好的特性,所以要杜绝在形参里面使用arguments;
2 在任何地方定义this变量都会出语法错误,这是个好特性;
3 如果多个形式参数拥有相同的名称,最后的那个具有优先级,即便实际运行的时候它的值是undefined;
命名函数
你可以给一个函数一个名字。如果这样的话,它就不是一个函数声明,同时,函数体定义里面的指定的函数名( 如果有的话,如下面的spam, 译者注)将不会被提升, 而是被忽略。这里一些代码帮助你理解:
复制代码 代码如下:
foo(); // TypeError "foo is not a function"
bar(); // valid
baz(); // TypeError "baz is not a function"
spam(); // ReferenceError "spam is not defined"
var foo = function () {}; // foo指向匿名函数
function bar() {}; // 函数声明
var baz = function spam() {}; // 命名函数,只有baz被提升,spam不会被提升。
foo(); // valid
bar(); // valid
baz(); // valid
spam(); // ReferenceError "spam is not defined"
怎么写代码
现在你理解了作用域和变量提升,那么这对于javascript编码意味着什么?最重要的一点是,总是用var定义你的变量。而且我强烈推荐,对于一个名称,在一个作用域里面永远只有一次var声明。如果你这么做,你就不会遇到作用域和变量提升问题。
语言规范怎么说
我发现ECMAScript参考文档总是很有用。下面是我找到的关于作用域和变量提升的部分:
如果变量在函数体类声明,则它是函数作用域。否则,它是全局作用域(作为global的属性)。变量将会在执行进入作用域的时候被创建。块不会定义新的作用域,只有函数声明和程序(译者以为,就是全局性质的代码执行)才会创造新的作用域。变量在创建的时候会被初始化为undefined。如果变量声明语句里面带有赋值操作,则赋值操作只有被执行到的时候才会发生,而不是创建的时候。
我期待这篇文章会对那些对javascript比较迷惑的程序员带来一丝光明。我自己也尽最大的可能去避免带来更多的迷惑。如果我说错了什么,或者忽略了什么,请告知。
译者补充
有位朋友提醒了我发现了IE下全局作用域下命名函数的提升问题:
我翻译文章的时候是这么测试的:
复制代码 代码如下:
<script>
functiont(){
spam();
var baz = function spam() {alert('this is spam')};
}
t();
</script>
这种写法, 即非全局作用域下的命名函数的提升,在ie和ff下表现是一致的. 我改成:
复制代码 代码如下:
<script>
spam();
var baz = function spam() {alert('this is spam')};
</script>
则ie下是可以执行spam的,ff下不可以. 说明不同浏览器在处理这个细节上是有差别的.
这个问题还引导我思考了另2个问题,1:对于全局作用于范围的变量,var与不var是有区别的. 没有var的写法,其变量不会被提升。比如下面两个程序,第二个会报错:
复制代码 代码如下:
<script>
alert(a);
var a=1;
</script>
复制代码 代码如下:
<script>
alert(a);
a=1;
</script>
2: eval中创建的局部变量是不会被提升的(它也没办法做到).
复制代码 代码如下:
<script>
var a = 1;
function t(){
alert(a);
eval('var a = 2');
alert(a);
}
t();
alert(a);
</script>
您可能感兴趣的文章: