在Javascript中,一个变量的作用域(scope)是程序源代码中定义这个变量的区域。
一、变量作用域
全局变量拥有全局作用域,在Javascript代码中的任何地方都是有定义的。
然而,在函数内声明的变量只在函数体内有定义,它们是局部变量,作用域是局部性的。
函数参数也是局部变量,它们只在函数体内有定义。
在函数体内,局部变量的优先级高于同名的全局变量。如果在函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量就被局部变量所遮盖。
<script> var scope = "global"; // 声明一个全局变量 function checkscope() { var scope = "local"; // 声明一个同名的局部变量 return scope; // 返回局部变量的值,而不是全局变量的值 } console.log(checkscope()); // local </script>
尽管在全局作用域编写代码时可以不写 var 语句,但声明局部变量时则必须使用 var 语句。
二、函数作用域和声明提前
函数定义时可以嵌套的,由于每个函数都有自己的作用域,因此会出现几个局部作用域嵌套的情况。如:
<script> var scope = "global scope"; // 全局变量 function checkscope() { var scope = "local scope"; // 局部变量 function nested() { var scope = "nested scope"; // 嵌套作用域内的局部变量 return scope; // 返回当前作用域内的值 } return nested(); } console.log(checkscope()); // nested scope </script>
在类似C语言的编程语言中,花括({})内的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的,我们称之为块级作用域(block scope)。而Javascript中没有块级作用域,Javascript使用的是函数作用域(function scope):变量在声明它们的函数体以及整个函数体嵌套的任意函数体内都是有定义的。
如下代码,在函数体内的不同位置定义了变量i,j和k(虽然变量定义在不同的花括号块内),它们都在同一个作用域内----这三个变量在函数体内均是由定义的。
<script> function action(obj) { var i = obj; // i 在整个函数体内都是有定义的 if (typeof obj == "object") { var j = obj; // j在函数体内是有定义的,不仅仅是在整个代码段内 for (var k = 0; k < 10; k++) { // k在函数体内是有定义的,不仅仅在循环内 console.log(k); // 输出0-9 } console.log(k); // k已经定义,输出10 } console.log(j); // j已经定义了,但可能没有初始化(obj不是object对象时) } </script>
Javascript的函数作用域是指在函数内声明的所有变量,在函数体内始终是可见的。这样,意味着变量在声明之前甚至已经可用。Javascript的这个特性被称为声明提前(hoisting),即Javascript函数里声明的所有变量(但不涉及赋值)都被“提前”至函数体的顶部(声明提前是在Javascript引擎“预编译”时进行的,是在代码开始运行之前)。
下面的例子很好的解释了声明提前这一概念:
<script> var scope = "global"; // 全局变量 function fn() { console.log(scope); // 输出undefined(局部变量scope声明提前,覆盖了全局变量scope,但是还没赋值) var scope = "local"; // 变量在这里赋初始值,但变量本身在函数体内任何地方都是有定义的 console.log(scope); // 输出local } fn(); </script>
由于函数作用域的特性,局部变量在整个函数体内始终是有定义的,因此,在函数体内局部变量会覆盖同名的全局变量。尽管如此,只有在程序执行到 var 语句的时候,局部变量才会被真正赋值。因此,上面的代码等价于:将函数体内的变量声明“提前”至函数体顶部,同时变量的初始化留在原来的位置:
function fn() { var scope; console.log(scope); // 输出undefined(局部变量scope声明提前,覆盖了全局变量scope,但是还没赋值) scope = "local"; // 变量在这里赋初始值,但变量本身在函数体内任何地方都是有定义的 console.log(scope); // 输出local }