详解作用域

76 阅读3分钟

1. 定义:

变量可以起作用的范围
作用域分为全局作用域和局部作用域(私有作用域)

2. 全局作用域:

  script 标签内就是全局作用域,在全局中都可以使用。
  生命周期: 从页面打开到页面关闭  
  全局作用域中定义的变量,在全局中都可以使用。
  
  注意:在 JS 中, 全局作用域中有一个 提前给我们准备好的  window对象,创建的全局变量, 会被自动添加到 window 对象中。
  
  <script>
      var num =100;
      function fn() {
        console.log( num );
      }
      fn();
      console.log( num )
   </script>

3. 局部作用域:(以下规则:仅针对于var这个关键字)

   也叫做私有作用域,在js中只有函数的大括号才能确定一个局部作用域(if和for的大括号不行)
   在局部作用域中定义的变量,只能在这个局部作用域中使用,在别的地方不能使用
   <script>
        局部作用域
        function ff() {
          var abc123 = '我是在 fn 函数内部创建的局部变量'
        }
        function ff2() {
          var abc123 = '我是在 fn 函数内部创建的局部变量'
        }
    </script>
    

案例1

 <script>
      var num = 100;
      function fn() {
        console.log( num ); // undefined
        var num = 200;
        console.log( num );// 200
        var username = 'zs';
      }
      fn(); // undefined 200 100 报错 username is not defined
      console.log( num ) // 100
      函数私有作用域定义的变量,在外部补不能使用
      console.log( username) // 报错 username is not defined
  </script>
  
// 执行解析:
// fn函数调用,fn函数内有预解析 变量提升
// fn函数内:
    function fn(){
       var num;
       var username;
       console.log( num );
       num = 200;
       console.log( num );
       username = 'zs';
    }
// 1. 在函数的私有作用域中声明了变量num和username没有赋值
// 2. 在私有作用域中访问变量num,私有作用域中声明了这个变量,但是还没有赋值,所有输出是undefined
// 3. 将数值200赋值给私有作用域中的变量num
// 4. 在私有作用域中访问变量num,私有作用域中声明了这个变量,并且值为200,所以输出200
// 5. 将字符串'zs'赋值给私有作用域中的变量username

4. 作用域链

          会先在当前自己作用域中查找,是否有定义这个变量,如果有则拿来使用
          如果当前作用域中没有这个变量,则去上一父级作用域中查找,找到了则使用
          如果找不到,则再继续去上一父级作用域中查找这个变量,找到了则使用
          如果还是找不到,则再继续去上一父级作用域中查找这个变量,找到了则使用
          .....
          一直往上级作用域中查找,直到全局作用域查找,找到则使用
          如果在全局作用有中还是找到不到,则报错(变量 is not defined)
          我们将这个一层一层向上查找的规律, 叫做作用域链
  
注意: 变量的访问,找不到的时候只会去上一级作用域查找,不会往下的作用域中查找

5. 作用域中变量的赋值规则 和 隐式全局变量

   作用域中变量的赋值规则: 
   当作用域中有 给变量赋值的时候,
   会现在当前作用域中找,是否有声明这个变量,如果有声明则赋值
   如果没有,则去上一级作用域中查找,在上一级作用域中是否有声明这个变量,如果有则赋值
   如果还是没有,则继续往上一级作用域中找,如果还是没有,则继续往上的作用域中找
   如果直到这种作用域找,还是没有找到,则会将这个变量定义为 **全局变量** ,并且赋值
   这种定义的变量 我们称之为 隐式全局变量  
<script>
        function fn1() {
            var num = 999
            function fn2() {
                num = 100
     /**
     *  在当前作用域内查找 num 发现没有, 会去自己的父级作用域内查找, 也就是 fn1 函数内部
     * 
     *  在 fn1 函数内部发现一个变量 num 然后值为 999    我们会对这个变量做一个重新赋值的操作
     * 
     *  也就是将他的值 重新修改为 100
     */
            }
            fn2()
            console.log(num)    // 100
        }
        fn1() // 100  报错num is not defined
        console.log(num)    // 报错num is not defined
</script>
<script>
        var num = 666
        function fn1() {
            var num = 999
            function fn2() {
                num = 100
                /**
                 *  在当前作用域内查找 num 发现没有, 会去自己的父级作用域内查找, 也就是 fn1 函数内部
                 * 
                 *  在 fn1 函数内部发现一个变量 num 然后值为 999    我们会对这个变量做一个重新赋值的操作
                 * 
                 *  也就是将他的值 重新修改为 100
                */
            }
            fn2()
            console.log(num) //100
        }
        fn1() //100
        console.log(num)    // 666
  </script>
<script>
        var num = 666
        function fn1() {
            function fn2() {
                num = 100
                /**
                 *  在当前作用域内查找 num 发现没有, 会去自己的父级作用域内查找, 也就是 fn1 函数内部
                 * 
                 *  在 fn1 函数内部, 发现没有 这个变量, 继续去自己的父级作用域查找, 也就是 全局作用域
                 * 
                 *  在全局作用域发现了一个变量 叫做 num, 他的值是 666, 我们将这个变量重新赋值为 100
                */
            }
            fn2()
        }
        fn1()
        console.log(num)    // 100
</script>