ES6块级作用域

块级作用域

① 全局作用域和函数作用域

在ES6之前只有 全局作用域和函数作用域,由于变量提升带来了一些不便。

1. 第一种场景,内层变量可能会覆盖外层变量。
```
var tmp = new Date();

function f(){
    console.log(tmp); 
    if(false){      
        var tmp = 'hello world';
    }
}

f(); //输出 undefined
```

if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。但是函数f执行后,输出的结果为undefined,
原因:在于变量的提升,导致内层的tmp变量覆盖了外层的tmp变量,在赋值tmp之前调用此变量所以结果为undefined。

变量提升:JavaScript 中,函数及变量的声明都将被提升到函数的最顶部,

注意:虽然函数声明和变量声明都会被提升,但是函数会首先被提升,然后才是变量。
在变量声明过后,它的赋值依然保留在原来的位置。
例:
上面的代码变量提升后
function f(){
var tmp;
console.log(tmp);
if(false){
tmp = "hello world";
}
}
var tmp ;
tmp= new Date();
f();
补充:使用匿名函数的方式不存在函数提升,因为函数名称使用变量表示,只存在变量提升。
例:
var getName=function(){
console.log(2);
}
function getName(){
console.log(1);
}
getName();//结果为2
--------------------------
//变量提升后
//首先函数先提升到顶部
function getName(){
console.log(1);
}
//其次变量提升
var getName;
getName=function(){ //变量赋值保留在原来的位置
console.log(2);
}
getName();//结果:2

JS运行机制(阮一峰)
JS运行机制(线程)

2. 第二种场景,用来计数的循环变量泄露为全局变量。
```
var s ="hello";

for(var i =0;i<s.length;i++){
    console.log(s[i]);
}

console.log(i);//结果为5
--变量提升--------- 
var s;
var i;
s ="hello";

for(i=0;i<s.length;i++){
    console.log(s[i]);
}
console.log(i);//结果为5
```
变量<span style="color:red;">i</span>只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量,<span style="color:red;">i</span>最终一直增值到字符串的长度5。

> ECMAScript参考文档关于作用域和变量提升的部分: 
> &emsp;如果变量在函数体类声明,则它是函数作用域。否则,它是全局作用域(作为global的属性)。变量将会在执行进入作用域的时候被创建。块(比如if(){})不会定义新的作用域,只有函数声明和全局性质的代码(单个JS文件)才会创造新的作用域。变量在创建的时候被初始化为undefined。如果变量声明语句里面带有赋值操作,则赋值操作只会在被执行到的时候才会发生,而不是创建的时候。

② ES6的块级作用域

1. 什么是块级作用域
    ```
           {
                let i =0;    //块级作用域
           }
    ```
    >  注意:使用块级作用域一般要使用let来声明变量

    - ES6允许块级作用域的任意嵌套。外层作用域无法读取内层作用域的变量。
    ```
    {{{{
            {let i=6;}
            console.log(i);//Uncaught ReferenceError: i is not defined
    }}}}
    ```
    - 内层作用域可以定义外层作用域的同名变量,但是外层代码块不受内存代码块的影响。
    ```
    {{{{
            let i =5;
            {let i = 6;}
            console.log(i);//5
    }}}}
    ```
    > 注意如果使用var定义 就没有了块级作用域,受变量提升的影响输出值为6
    > ```
    > var i;
    > var i;
    > {{{{
    >       i=5;
    >       {i=6;}
    >       console.log(i);//6
    > }}}}      
    > ```

   > 补充:块级作用域的出现,使得立即执行函数表达式(IIFE)不再必要了。
   > ```
   > //IIFE写法
   >(function{
   >     var i =5;
   >})();
   >//块级作用域写法
   >{
   >     let i =5;
   >}
   > ```
2. 块级作用域与函数声明
   - ES5规定,函数只能再顶层作用域和函数作用域之中声明,不能在块级作用域中声明。
   ```
    if(true){
        function f(){}        
    }

    try{
        function f(){}
    }catch(e){}
   ```
    上面的两种函数声明根据ES5都是非法的。
    但是,浏览器为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此以上两种情况实际都能运行,不会报错;但是在严格模式下,还是会报错;不过现在的浏览已经适用的ES6,不会报错。
    > ```
    > 'use strict';
    > if(true){
    >   function func(){}//ES5报错/ES6不会
    > }
    > ```

   - ES6引入了块级作用域,明确允许在块级作用域之中声明函数。ES6规定,块级作用域之中,函数声明语句的行为类似let,在块级作用域之外不可引用。
   ```
   //ES5环境
    function f(){console.log('I am outside!');}

    (function{
        if(false){
            function f(){console.log('I am inside!');}
        }  
        f();//I am inside!         
    })(); 
    ------经过变量提升---------
    function f(){console.log('I am outside!');}

    (function{
         function f(){console.log('I am inside!');}
        if(false){               
        } 
        f();//I am inside!          
    })();   
   ```
   ES6浏览器块级作用域的函数声明规则:
   &emsp;允许在块级作用域内声明函数
   &emsp;函数声明类似于var ,即会提升到全局作用域或函数作用域的头部。
   &emsp;同时,函数声明还会提升到所在的块级作用域的头部。  
   ```
   //ES6环境
    function f(){console.log('I am outside!');}

    (function{
        if(false){
            function f(){console.log('I am inside!');}
        }  
        f();//Uncaught TypeError: f is not a function      
    })(); 
    ------经过变量提升---------
    function f(){console.log('I am outside!');}

    (function{
        var f =undefined; 
        if(false){  
            function f(){console.log('I am inside!');}             
        } 
        f();//Uncaught TypeError: f is not a function       
    })();   
    ```
    考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。
    如果确实需要,也应该写成函数表达式,而不是函数声明语句。
    ```
    //函数声明语句
    {
        let a ='secret';
        function f(){}
    }
    //函数表达式
    {

        let a ='secret';
        let f = function(){}
    }
    ```
    > 注意:ES6的块级作用域允许声明函数的规则,只在使用大括号{}的情况下成立,如果没有使用大括号,就会报错。
    > ```
    > //不报错
    > 'use strict';
    > if(true){
    >   function f(){}
    > }
    > //报错
    > 'use strict';
    > if(true)
    >   function f(){}
    > ```

猜你喜欢

转载自blog.csdn.net/YCJ_xiyang/article/details/82635210