javascript中定义变量方式以及作用域

180 阅读6分钟
1. var、let区别
1.1 在JavaScript中定义变量有两种方式
    ES6之前: var 变量名称;
    ES6开始: let 变量名称;
    注意点:只要出现了let, 在相同的作用域内, 就不能出现同名的变量
        let num = 123;
        var num = 456;  //报错
 
        var num = 123;
        let num = 456;  //报错
        
1.2 var和let的区别:
     1. 是否能够定义同名变量
            通过var定义变量,可以重复定义同名的变量,并且后定义的会覆盖先定义的
                var num = 123;
                var num = 456;
                console.log(num);  //456

            如果通过let定义变量,  "相同作用域内"不可以重复定义同名的变量
                let num = 123;
                let num = 456;  //报错
                
     2. 是否能够先使用后定义       
             通过var定义变量, 可以先使用后定义(预解析)
                  console.log(num);  //123
                  var num = 123;

              通过let定义变量, 不可以先使用再定义(不会预解析)
                  console.log(num); // 报错
                  let num = 123;
                  
     3 是否能被{}限制作用域
              无论是var还是let定义在{}外面都是全局变量
                  var num = 123;
                  let num = 123;

              将var定义的变量放到一个单独的{}里面, 还是一个全局变量
                  {
                      //块级作用域
                      var num = 123;
                  } 
                  console.log(num);  //123

               将let定义的变量放到一个单独的{}里面, 是一个局部变量
                  {
                      //块级作用域
                      let num = 123;
                  }
                  console.log(num);  //报错 
                  
2. 作用域
2.1JavaScript中{}外面的作用域, 我们称之为全局作用域。
         var num = 456;
         console.log(num);  //456
         
2.2JavaScript中函数后面{}中的的作用域, 我们称之为"局部作用域"function say() {
            // 局部作用域
        }
        
2.3ES6中只要{}没有和函数结合在一起, 那么称之为"块级作用域"for(;;){
            // 块级作用域
        }
        
2.4 块级作用域和局部作用域区别:
     1. 在块级作用域中通过var定义的变量是全局变量
     2. 在局部作用域中通过var定义的变量是局部变量
     
2.5 无论是在块级作用域还是在局部作用域, 省略变量前面的let或者var就会变成一个全局变量
        function test() {
            var num1 = 123;  //局部变量
            let num2 = 123;  //局部变量
            num3 = 123;      //全局变量
        }
        test();
        console.log(num1);  //报错
        console.log(num2);  //报错
        console.log(num3);  //123
        
        {
            var num1 = 123;  //全部变量
            let num2 = 123;  //局部变量
            num3 = 123;      //全局变量
        }
        console.log(num1);  //123
        console.log(num2);  //报错
        console.log(num3);  //123
        
2.6 注意点: 在不同的作用域范围内, 是可以出现同名的变量的
       {
           let num = 123;
            {
                let num = 456; //不会报错
            }
        }       
        
3. 作用域链
3.1 注意点: 初学者在研究"作用域链"的时候最好将ES6之前和ES6分开研究。

3.2 ES6之前需要明确:
     1. ES6之前定义变量通过var
     2. ES6之前没有块级作用域, 只有全局作用域和局部作用域
     3. ES6之前函数大括号外的都是全局作用域
     4. ES6之前函数大括号中的都是局部作用域
     
3.3 ES6之前作用域链
     1. 全局作用域我们又称之为0级作用域
     2. 定义函数开启的作用域就是1级/2级/3级/...作用域
     3. JavaScript会将这些作用域链接在一起形成一个链条, 这个链条就是作用域链
         0  --->  1 ---->  2  ---->  3 ----> 4
     4.0级作用域以外, 当前作用域级别等于上一级+1 
     
3.4 变量在作用域链查找规则:
     1. 先在当前找, 找到就使用当前作用域找到的
     2. 如果当前作用域中没有找到, 就去上一级作用域中查找
     3. 以此类推直到0级为止, 如果0级作用域还没找到, 就报错     
     
3.5 
    //全局作用域 又称 0级作用域
    var num = 123;
    function demo() {
        // 1级作用域
        var num = 456;
        function test() {
            // 2级作用域
            var num = 789;
            console.log(num);  //789
        }
        test();
    }
    demo();
3.6 ES6需要明确:
     1. ES6定义变量通过let
     2. ES6除了全局作用域、局部作用域以外, 还新增了块级作用域
     3. ES6虽然新增了块级作用域, 但是通过let定义变量并无差异(都是局部变量)
     
3.7 ES6作用域链
     1. 全局作用域我们又称之为0级作用域
     2. 定义函数或者代码块都会开启的作用域就是1级/2级/3级/...作用域
     3. JavaScript会将这些作用域链接在一起形成一个链条, 这个链条就是作用域链
         0  --->  1 ---->  2  ---->  3 ----> 4
     4.0级作用域以外, 当前作用域级别等于上一级+1
     
3.8 变量在作用域链查找规则
     1. 先在当前找, 找到就使用当前作用域找到的
     2. 如果当前作用域中没有找到, 就去上一级作用域中查找
     3. 以此类推直到0级为止, 如果0级作用域还没找到, 就报错
     
3.9
    //全局作用域 又称 0级作用域
    let num = 123;
    {
        // 1级作用域
        let num = 456;
        function test() {
            // 2级作用域
            let num = 789;
            console.log(num);  //789
        }
        test();
    }
4. 预解析
4.1 什么是预解析?
    浏览器在执行JS代码的时候会分成两部分操作:
     预解析以及逐行执行代码也就是说浏览器不会直接执行代码, 而是加工处理之后再执行,
     这个加工处理的过程, 我们就称之为预解析。
     
4.2 预解析规则:
     1. 将变量声明和函数声明提升到当前作用域最前面
     2. 将剩余代码按照书写顺序依次放到后面
     
4.3 注意点:
     通过let定义的变量不会被提升(不会被预解析)
     
4.4 函数预解析
 注意点: 1. 如果变量名称和函数名称同名, 那么函数的优先级高于变量,
            一定要记住, 在企业开发中千万不要让变量名称和函数名称重名。
         2. 在高级浏览器中, 不会对{}中定义的函数进行提升
         3. 只有在低级浏览器中, 才会按照正常的方式解析
     1. es6之前函数:
            //函数是可以预解析的
            demo()
            function demo(){
                console.log(123)
            }
            
            //如果将函数赋值给一个var定义的变量, 那么函数不会被预解析, 只有变量会被预解析
            say()
            var say = function(){
                console.log(456)
            }
     3. es6函数
            //如果将函数赋值给一个var定义的变量, 那么函数不会被预解析, 只有变量会被预解析
            test() 
            let test = () =>{
                console.log(789)
            }
例子:1.
        if(true){
            function demo() {
                console.log("hello demo1111111111");
            }
        }else{
            function demo() {
                console.log("hello demo2222222222");
            }
        }
        demo();
      预解析后:
        function demo() {
            console.log("hello demo1111111111");
        }
        function demo() {
            console.log("hello demo2222222222");
        }
        if(true){}else{}
        demo();
    2.
      console.log(value); // 会输出函数的定义
      var value = 123;
      function value() {
        console.log("fn value");
      }
      console.log(value); // 会输出123
    预解析后:
      function value() {
        console.log("fn value");
      }
      console.log(value);
      var value;
      value = 123;
      console.log(value);