JS作用域&作用域链

变量

变量分为全局变量和局部变量,全局变量就是指该变量的作用域为当前文档,也就是说全局变量在当前文档的所有所有JavaScript脚本都可以访问到,都是有定义的。换言之就是说全局变量能够被访问的区域就是全局变量作用域。

<script type="text/javascript">
    //定义了一个全局变量。那么这个变量在当前html页面的任何的JS脚本部分都可以访问到。
    var v = 20; 
    alert(v); //弹出:20
</script>
<script type="text/javascript">
    //因为v是全局变量,所以这里仍然可以访问到。
    alert(v);  //弹出:20
1 <script type="text/javascript">
2     alert(a);
3     var a = 20;
4 </script>

这段代码并不会报错,而是输出undefined。

因为这是声明提前引起的。所有的全局变量的声明都会提前到JavaScript的前段声明,而且会早有其他一切代码。但是声明提前了,但是对于全局变量的位置不会提前,赋值的位置仍然是原位置。

<script type="text/javascript">
    var a; //声明提前
    alert(a);
    a = 20; //赋值仍然在原来的位置
</script>

而局部变量指的是在函数内部声明的变量,其中函数的形参变量也是局部变量。局部变量的作用域是该局部变量所在的整个函数内部,出了这个函数就不能访问了。

局部变量的声明也会提前到所在函数的最顶端。

1 <script type="text/javascript">
2     function f(){
3         alert(v);  //   弹出:undefined
4         var v = "abc";  // 声明局部变量。局部变量也会声明提前到函数的最顶端。
5         alert(v);   //  弹出:abc
6     }
7     alert(v);  //报错。因为变量v没有定义。 方法 f 的外部是不能访问方法内部的局部变量 v 的。
8  </script>

分解得

1 <script type="text/javascript">
2     function f(){
3         var v //声明提升
4         alert(v) 
5         v = "abc";  
6         alert(v);   
7     }
8     alert(v);  
9  </script>

全局和局部变量重名

当在同一个作用域内定义了名字相同的变量和方法的话,会根据前置顺序产生覆盖

1 var fn = 3;
2 function fn(){}
3 
4 console.log(fn); // 3

分解相当于

1 var fn
2 function fn() {}//覆盖上面的
3 fn = 3//重新复制
4 console.log(fn)//变量和函数会提升到最前面

函数执行有命名冲突的时候,可以认为在还是内部一开始有隐藏的声明变量这个操作

1 function fn(fn){
2   console.log(fn);//同样给了fn(10)
3 
4   var fn = 3;
5   console.log(fn);//3
6 }
7 
8 fn(10); //10 3

全局变量下可以通过window访问

1 var m = 10;
2     function f(){
3         var m = 20;
4         alert(window.m);  //访问同名的全局变量。其实这个时候相当于在访问window这个对象的属性。
5     }
6     f();  

JavaScript中没有块级作用域,这是与其他强类型的语言不同,JavaScript的作用域是靠函数来形成的,也就是说一个函数内定义的变量函数外不可以访问

 var m = 5;
  if(m == 5){
    var n = 10;
  }
  console.log(n); //10

在web浏览器中,全局执行环境是最外围的一个执行环境,该环境也被认为是window对象,因此全局执行环境的变量对象就是window对象。而对每个函数而言,都有自己的执行环境,也就是该函数的内部。每当执行一个函数,就会进入该函数的执行环境。

而作用域链是与执行环境相关的。在JavaScript中,“一切皆对象”,而函数的这个对象有一个内部属性[[scope]],该内部属性指向了该函数的作用域链,而作用域链中存储了每个执行环境相关的变量对象。

 

1 function sum(num1, num2) {
2     var sum = num1 + num2;
3     function inner(a) {
4 
5     }
6     return sum;
7 }
8 var sum = sum(3, 4);
9 console.log(sum)//7
以上是在创建(或声明)一个函数时会创建一个作用域链,而当函数被调用时,也就是进入到一个新的执行环境的时候,此时这个执行环境也就会有一个新的变量对象被创建,而这个对象就会被存储在该函数的[[scope]]属性所指向的作用域链中,而之前的对象就被压在了新的变量对象的下边,这个可以类比栈,新的变量对象就放在了栈的最顶端,给最顶端的序号为0,向下以此类推。
当以后我们需要查找变量的时候,就总是会沿着这个作用域链的顶端(序号0/栈顶)开始查找,一直到作用域链(栈底)的末端,直到找到为止。也就是说顶端的活动对象可以访问到其后面的参数。
 1 var a = 1
 2 function fn1(){
 3   function fn2(){
 4     console.log(a)//他的上一级函数变量里有 a=2
 5   }
 6   function fn3(){
 7     var a = 4
 8     fn2()
 9   }
10   var a = 2
11   return fn3
12 }
13 var fn = fn1()
14 fn() //输出多少

var a = 1
function fn1(){
  function fn3(){
    var a = 4
    fn2()
  }
  var a = 2
  return fn3
}
function fn2(){
  console.log(a)//他的上一级是全局变量 a=1
}
var fn = fn1()
fn() //输出多少//1

 1 var a = 1
 2 function fn1(){
 3 
 4   function fn3(){
 5     function fn2(){
 6       console.log(a)//fn3 a=4但是对自身没有印象但对内嵌函数有影响。
 7     }
 8     var a
 9 
10     fn2()
11     a = 4
12   }
13   var a = 2
14   return fn3
15 }
16 var fn = fn1()
17 fn() //输出多少
  1. 函数在执行的过程中,先从自己内部找变量
  2. 如果找不到,再从创建当前函数所在的作用域去找, 以此往上
  3. 注意找的是变量的当前的状态

 


 

补充函数表达式不会提前声明 
1   sayName('world');
2     sayAge(10);
3     function sayName(name){
4         console.log('hello ', name);
5     }
6     var sayAge = function(age){
7         console.log(age);
8     };

分解应该是如下

1 sayName('world');
2 sayAge(10);
3 var sayAge;
4 function sayName(name) {
5     console.log('hello ', name);
6 }
7 sayAge = function (age) {
8     console.log(age);
9 };

 

 

posted @ 2018-01-10 23:50  爱好跑步的小张  阅读(166)  评论(0)    收藏  举报