估计大家一直对Js的作用域有点迷糊,今天没事看到JavaScript权威指南对作用域的解释感觉很不错就和大家分享一下。
一:函数作用域
先看一小段代码:
1. var scope="global";
2. function t(){
3. console.log(scope);
4. var scope="local"
5. console.log(scope);
6. }
7. t();
第一句输出的是: "undefined",而不是"global"
第二句输出的是:"local"
你可能会认为第一句会输出:"global",因为代码还没执行var scope="local",所以肯定会输出“global"。
在C/C++中,花括号内中的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的,我们称为块级作用域。而Javascript压根没有块级作用域,而是函数作用域.
所谓函数作用域就是说:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
其实由于函数作用域的特性,局部变量在整个函数体内始终是有定义的,只有在程序执行到var语句的时候局部变量才真正的被赋值,上面的过程据就是我们经常说的“声明提前”,将函数体内的变量声明提前至函数的顶部,同时变量的初始化留在原地。
所以根据函数作用域的意思,可以将上述代码等价于:
1. var scope="global";
2. function t(){
3. var scope;
4. console.log(scope);
5. scope="local"
6. console.log(scope);
7. }
8. t();
我们可以看到,由于函数作用域的特性,局部变量在整个函数体始终是由定义的,我们可以将变量声明”提前“到函数体顶部,同时变量初始化还在原来位置。
1. var name="global";
2. if(true){
3. var name="local";
4. console.log(name)
5. }
6. console.log(name);
都输出是“local",如果有块级作用域,明显if语句将创建局部变量name,并不会修改全局name,可是没有这样,所以Js没有块级作用域。
现在很好理解为什么会得出那样的结果了。scope声明覆盖了全局的scope,但是还没有赋值,所以输出:”undefined“。
所以下面的代码也就很好理解了。
1. function t(flag){
2. if(flag){
3. var s="ifscope";
4. for(var i=0;i<2;i++)
5. ;
6. }
7. console.log(i);
8. console.log(s);
9. }
10. t(true);
输出:2 ”ifscope"
二:作用域链
先来看一段代码:
1. name="lwy";
2. function t(){
3. var name="tlwy";
4. function s(){
5. var name="slwy";
6. console.log(name);
7. }
8. function ss(){
9. console.log(name);
10. }
11. s();
12. ss();
13. }
14. t();
当执行s时,将创建函数s的执行环境(调用对象),并将该对象置于链表开头,然后将函数t的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显
name是"slwy"。
但执行ss()时,作用域链是:ss()->t()->window,所以name是”tlwy"
Javascript权威指南写的啰嗦,我们总结一下就是,在函数作用域内自上而下引用形成的链式结构就叫做作用域链