JS的作用域

有如下代码:

 1 <body>
 2         <script type="text/javascript">
 3             alert(a);
 4             alert(b);
 5             alert(c);
 6             alert(d);
 7 
 8             var a = 5;
 9             if (false) {
10                 var b = 6;
11             }else{
12                 c = 7;
13             }
14             function f() {
15                 var d = 8;
16             }
17         </script>
18  </body>

这段代码4个alert(),根据前面的预处理知识,首先a是undefined,d是f的局部变量,c不会被预处理,所以报错,那b呢?结果是b也弹出undefined,说明在预处理的时候,if else判断语句被忽略了。

首先知道,作用域有4类,分别是:

  • 块作用域;
  • 函数作用域;
  • 动态作用域;
  • 词法作用域(也称为静态作用域或闭包);

然后一一分析

1.作用域的定义

一个变量、函数、成员在整个程序里面可以被访问的范围就叫作用域。

2.块作用域

正如大部分的编程语言一样,以一对大括号括起来的一段代码就叫一个块,所谓块作用域,就是只在这个块中生效。

有如下代码:

1 for (var i = 0; i < 3; i++) {
2     
3 }
4 console.log(i);

该循环结束后,控制台打印为3,说明var i 是不具备块作用域效果的,但是在ES6标准中,var 被 let 取代,凡是以 let声明的变量是块作用域的,如下:

1 for (let i = 0; i < 3; i++) {
2     
3 }
4 console.log(i);

var 改为 let后该段代码打印会报错 i is not defined;所以官方推荐使用let而不是var来声明变量。

3.函数作用域

即函数里面声明的变量,相当于Java的局部变量,只在一个函数里面生效。

4.动态作用域

有如下代码:

 1 function f() {
 2     console.log(x);
 3 }
 4 
 5 function f1() {
 6     var x = 5;
 7     f();
 8 }
 9 
10 function f2() {
11     var x = 6;
12     f();
13 }
14 
15 f1();

所谓动态作用域,就是如上所示,同一变量在不同的作用域中值应该不同,但是上面代码执行会报错,说明JS并不是动态作用域,而是静态作用域。

5.词法作用域

 静态作用域,又叫词法作用域(LexicalEnviroment)或闭包,所以闭包和作用域是紧密联系的,严格意义上来说,JS的作用域规则便是词法作用域。

 在JS代码被解释执行的时候,

  1. 某个函数 f 被解释创建,然后会给 f 函数本身添加一个成员(不可见的成员) [[scope]], scope等于创建函数 f 时的全局词法环境,[[scope]] == LexicalEnviroment == window, 也就是说,函数内部有一个隐形对象指向window;
  2. 当函数f真正执行的时候,会创建函数自己的词法环境。f 的词法环境会跟 f 的作用域关联起来,Lx -> f.[[scope]] ;
1 function f() { //scope == window
2     //f.le -> f.[[scope]]    
3     var x = 100;
4     function g() { //g.[[scope]] == f.le
5         //g.le -> g.[[scope]]        
6     }
7     g();
8 }
9 //g.le -> g.[[scope]] -> f.le -> f.[[scope]] == windos 这便是整个作用域的链条关系

 

一般的作用域都遵循此规则,但是以new Function(){}方式创建的函数其作用域却永远指向全局,而不是它的父函数。

回顾函数的创建方式:

  1. 直接声明,function 函数名(){}
  2. 函数表达式,var 函数名 = 匿名函数或有名函数/自调用匿名函数
  3. new Function("函数名","函数体")形式
 1 <body>
 2     <script type="text/javascript">
 3         var x = 12;
 4 
 5         function f() {
 6             var x = 100;
 7             //g.[[scope]] == window
 8             var g = new Function("", "alert(x)");
 9             g();
10         }
11         f();
12     </script>
13 </body>

 

这段代码弹出的是12,而不是100,证明了这个规则,还有,这种写法在单独运行js文件时会报 x is not defined,因为没有全局window对象。

6.作用域的本质

所以:作用域的而本质就对某一个需要的变量,从本身词法环境开始,逐级向上查找,直至全局对象的过程。

作用域的意义在于信息隐藏,比如避免太多的全局变量引起的冲突,如下代码:

 1 <body>
 2     <script type="text/javascript">
 3         (function () {
 4             var a = 5;
 5             var b = 6;
 6 
 7             function f() {
 8                 alert(a);
 9             }
10             window.f = f;
11         })();
12 
13         f();
14     </script>
15 </body>

在这段代码中,自调函数定义了变量,将其访问方式挂载到window对象上,局部变量a还可以被外面访问到,并没有被销毁,这便涉及到JS的特性---闭包。  

posted @ 2019-03-09 12:48  jixhua  阅读(253)  评论(0编辑  收藏  举报