javascript变量的作用域一直都是js程序员最困扰的难点之一。先通过概要来过一遍涉及到的知识。
一、全局变量
全局变量包含:1.在函数体外定义的变量
2.在函数体内部定义的无var变量(很多人很容易的误认为在函数体内定义的所有变量都是局部变量,这里需特变注意!)
全局变量可以在任何位置被调用。
二、局部变量
局部变量包含:1.在函数内部使用Var声明的变量
2.函数的参数变量
局部变量只能在当前函数体内部被调用。
三、优先级(符合就近原则)
1.局部变量高于同名全局变量
2.参数变量高于全局变量
3.局部变量高于同名参数变量
四、特性
1.忽略块级作用域
2.全局变量是全局对象的属性
3.局部变量是调用对象的属性
4.作用域链:1)内层函数可访问外层函数局部变量
2)外层函数不能访问内层函数局部变量
5.生命周期:1)全局变量:除非被显示删除,否则一直存在
2)局部变量:自声明起至函数运行完毕或被显示删除
3)回收机制:标志清楚+引用计数
接下来我们直接上代码,通过图示分析代码切身体会全局变量和局部变量的作用域。
<script type="text/javascript">
var a = 8;
var b = true;
function test()
{
alert(a);
alert(b);
b=false;
alert(b);
var a = 20;
alert(a/2);
alert(++Math.PI);
alert(Math.PI++);
}
test();
</script>
问题:a、b都是全局变量,但是第一个alert的结果却是defined,第二个的结果是true?
换句话说,b可以找到外面定义的全局变量,为什么a不能?
前面的概要中提到,局部变量的优先级大于全局变量。当js引擎在当前作用域(这个例子指test()函数体内)找不到此变量时,它就会往外但包含当前作用域的作用域找。
在这里我简单阐述一下关于js的作用域链的基本原理:
javascript需要查询一个变量时,首先会查找作用域链的第一个对象,如果第一个对象没有定义,则继续查找第二个对象……依次类推。
存在一个问题:当前的作用域中这个变量存在吗?
javascript是一种解释型语言,基本分为两个阶段:编译器与运行期。
在编译阶段,它是用函数来划分作用域,然后逐层为其以var声明的变量和函数的定义开辟内存空间,然后再对var变量进行特殊处理,统一赋初始值为undefined。
下面通过图片来看看以上例子编译后各个变量与作用域的关系:分析:由上图我们知道当前网页拥有两个a,一个b,一个test函数。如果在运行期用到除此以外的东西,如X函数或y变量啦,都会报未定义错误(用eval等非正常手段生成变量与函数的情况除外),此外,它们最多出现未赋值警告。
javascript的运行期是在为var变量与函数定义分配空间后立即执行,并且是逐行往下执行的。
- 第1行它为外围作用域的a赋值为100
- 第2行它为外围作用域的b赋值为true
- 第3行进行test的作用域
- 第4行就立即调用test作用域的a,这时它还没有来得及赋值呢!不过它已经声明过了,因此默认为其赋值为undefined(在预编译阶段,见图),于是alert为undefined
- 第5行就调用b时,JS引擎发现test的作用域内没有b,往test作用域外找,发现b了,而b在第二行就赋值为true,于是alert为true。
- 第6行为一个赋值操作,把外围的b变量改赋为false。于是到第7行时,alert为false。剩下的代码同理。
以上例子的代码的第6行
我们再来看看代码修改后编译出来的各个变量与作用域的关系:
这下恍然大悟了吧!在编译阶段,js引擎并不是和运行时一样按代码由第一行往下编译的,而是按var变量与函数定义分配空间,然后按顺序执行。在alert(a)、alert(b)时并未赋值,但是在编译时,在test()函数中已经声明过了,故结果为undefined。
掌握编译期为var变量与函数定义分配空间这一原理后,许多问题就会迎刃而解。
最后小结一下:
1.JavaScript的变量作用域是基于其特有的作用域链的。2.函数体内部,局部变量的优先级比同名的全局变量高。
3.javascript没有块级作用域(在javascript中,函数不仅在块中有定义,在整个函数都有定义。)
4.函数中声明的变量在整个函数中都有定义。
(如果函数内部有定义变量,即使在定义之前输出也会先执行后面定义语句,然后判断输出结果.声明的变量在整个函数中都是起作用的。)
5.未使用var关键字定义的变量都是全局变量。
6.全局变量都是window对象的属性。