javascript中的块级作用域【详解】

1. 为什么需要块级作用域?

  • 第一种场景,内层变量可能会覆盖外层变量。

    var tmp = new Date();
    
    function f(){
        console.log(tmp);
        if(false){
            var tmp = 'hello world';
        }
    }
    
    f();//undefined
    
  • 第二种场景,用来计数的循环变量泄露为全局变量。

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

2. ES6的块级作用域

  • let实际上为JavaScript新增了块级作用域。

  • ES6允许块级作用域的任意嵌套。

  • 内层作用域可以定义外层作用域的同名变量。

  • 块级作用域的出现,实际上使得广泛应用的匿名立即执行函数表达式(IIFE)不再必要了。

3. 块级作用域与函数声明

  • ES5规定,函数只能在顶层作用域函数作用域之中声明,不能在块级作用域声明。

    //情况一
    if(true){
        function f(){}
    }
    
    //情况二
    try{
        function f(){}
    }catch(e){
        //...
    }
    
    //上面两种函数声明,根据ES5的规定都是非法的。
    //但是,浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此上面两种情况实际都能运行,不会报错。
    

    ES6引入块级作用域,明确允许在块级作用域之中声明函数。ES6规定,块级作用域之中,函数声明语句类似于let,在块级作用域之外不可引用。

    function f(){ console.log('I am outside!'); }
    
    (function (){
        if(false){
            function f(){ console.log('I am inside!'); }
        }
        f();
    })
    //ES5环境
    //'I am inside!'
    
    //ES6环境
    // Uncaught TypeError: f is not a function
    

    ES6有块级作用域,为什么还会这样呢?

    原来,如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6 在附录 B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。

    但是三条规则只针对ES6环境

    • 允许在块级作用域内声明函数。

    • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。

    • 同时,函数声明还会提升到所在的块级作用域的头部。

    考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句

    // 块级作用域内部的函数声明语句,建议不要使用
    {
      let a = 'secret';
      function f() {
        return a;
      }
    }
    
    // 块级作用域内部,优先使用函数表达式
    {
      let a = 'secret';
      let f = function () {
        return a;
      };
    }
    

    另外,还有一个需要注意的地方。ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。

你可能感兴趣的:(javascript,javascript,前端,开发语言)