旷世的忧伤

Huoty's Blog

JavaScript 的全局变量与局部变量

首先说一下编程语言中全局变量与局部变量的区别。全部变量存储在静态区中,程序运行开始时为其分配内存,程序运行结束后该内存才被释放。局部变量存储在栈中,随程序调用被申请和释放。

在 Javascript 当中,变量的作用域是根据方法块来划分的,即以 function 的一对大括号 { } 来划分。而 for、while、if 块并不是作用域的划分标准。全局变量与局部变量在程序中的区别就在于作用域的不同。

一般情况,如果在任何函数定义之外声明了一个变量,则该变量为全局变量,且该变量的值在整个持续范围内都可以访问和修改;如果在函数定义内声明了一个变量,则该变量为局部变量,每次执行该函数时都会创建和破坏该变量,且它不能被该函数外的任何事物访问。

变量定义

变量都应该先申明再使用,JavaScript 用关键字 var 声明变量:

var varname;

只声明不赋值的变量则是空的,JavaScript 认为它是未定义的(underfined)。也就是说未使用值来声明的变量,其值实际上是 undefined。对一个未申明的变量进行引用则会导致脚本出错而停止运行,即出现ReferenceError: XXX is not defined

当然,可以在声明变量的同时对变量进行赋值:

var name=”Huoty”;

变量申明也可以不用 var,而是直接采用赋值的方式:

a = 10;

这样申明的变量默认是全局变量,但不建议这样做。虽然它是全局变量,但也受作用的限制,这似乎很绕,我们来看一个例子:

<body>
    <button type="button" onclick="getclick()">Click Me!</button>
</body>
<script>
function getclick()
{
    a = 10;
    alert(a);
}
console.log(a);
</script>

这段代码会有 ReferenceError: a is not defined 的错误,原因是在全局作用域中 a 未申明,但 a 确实为全局变量。原因是函数 getclick 只在按钮被单击时调用,而 a 也在此时被定义,console.log(a) 则是在脚本被加载时就调用,a 未来得及定义。而从表面上看这段代码是没有问题的,所以建议统一使用 var 来定义变量,需要全局变量时只需在函数外定义即可。个人认为这是 JavaScript 在设计上的一个缺点,容易让人混淆。如果 getclick 不是由点击事件触发,则这段代码就不会除错,如下所示:

function getclick()
{
    a = 10;
    alert(a);
}
getclick();
console.log(a);

我们再来看另外一个例子:

 var a = 1;
 document.write(a + '<br>');

 function demo()
 {
 	document.write(a + '<br>');

 	var a = 3;
 }

 demo();

程序输出 1 和 undefined,是不是很奇怪,应该输出 1 和 1 才对。这又涉及到另外一个问题,当全局变量跟局部变量重名时,局部变量会覆盖掉全局变量。同时,Javascript 在执行前会对整个脚本文件的声明部分做完整分析(包括局部变量),从而确定实变量的作用域。在这段代码中,函数 demo 中有一句 var a = 3;,Javascript 在做分析时,认为 a 在函数 demo 中是局部变量,而在执行语句 document.write(a + '<br>'); 时,变量还未来得及定义就被引用,所以是 undefined。

如果在局部作用域中要引用全局作用域的值,则用 window.globalVariableName 方式来访问,window 是一个全局对象。例如:

 var a = 1;

 function demo()
 {
 	var a = 3;
 	document.write(a + '<br>');
 	document.write(window.a + '<br>');
 }

 demo();
 document.write(a);

输出结果是: 3 1 1

显式声明与隐式声明

显式声明与隐式声明是针对全局变量而言的。之前有提到变量的申明可以用 var 关键字,也可以不用。使用 var(关键字) + 变量名(标识符)的方式在 function 外部声明,则为全局变量的显示申明;没有使用var,直接给标识符赋值,这样会隐式的声明一个全局变量,即使该语句是在一个 function 内,当该 function 被执行后也会变成全局变量,仅当该 function 被执行时。

全局变量还可以使用 window 全局对象来申明,全局对象的属性对应也是全局变量,例如:

window.a = 5;

全局变量优缺点

优点:

  • 可以减少变量的个数,减少由于实际参数和形式参数的数据传递带来的时间消耗。

缺点:

  • 全局变量保存在静态存贮区,程序开始运行时为其分配内存,程序结束释放该内存。与局部变量的动态分配、动态释放相比,生存期比较长,因此过多的全局变量会占用较多的内存单元。

  • 全局变量破坏了函数的封装性能。函数象一个黑匣子,一般是通过函数参数和返回值进行输入输出,函数内部实现相对独立。但函数中如果使用了全局变量,那么函数体内的语句就可以绕过函数参数和返回值进行存取,这种情况破坏了函数的独立性,使函数对全局变量产生依赖。同时,也降低了该函数的可移植性。

  • 全局变量使函数的代码可读性降低。由于多个函数都可能使用全局变量,函数执行时全局变量的值可能随时发生变化,对于程序的查错和调试都非常不利。

因此,在不是很必要的时候,不建议使用全局变量。

要点总结

  • (1)JavaScript 的变量作用域是根据方法块来划分的(也就是说以function的一对大括号{}来划分)。切记,是function块,而for、while、if块并不是作用域的划分标准。

  • (2)JavaScript 在执行前会对整个脚本文件的声明部分做完整分析(包括局部变量),从而确定变量的作用域。

  • (3)当全局变量跟局部变量重名时,局部变量的作用域会覆盖掉全局变量的作用域。当离开局部变量的作用域后,又重回到全局变量的作用域。而当全局变量遇上局部变量时,用 window.globalVariableName 访问。

Top