一、为什么需要块级作用域?

  ES5 只有 全局作用域 和 函数作用域,没有块级作用域,这导致了很多不合理的场景

  场景1、

    内层变量可能会覆盖外层变量:

var temp =    new Date();
console.info(temp);     //  Thu Jun 30 2022 15:29:49 GMT+0800 (中国标准时间)
function f() {
    console.info(temp);     // undefined
    if (   false ) {     // 替换 false 和 true,查看不同的情况
        var temp =    'hello world' ;
        console.info(temp);
    }
    console.info(temp);     // undefined
}
f();
console.info(temp);     //  Thu Jun 30 2022 15:29:49 GMT+0800 (中国标准时间)


var temp =    new Date();
console.info(temp);     //  Thu Jun 30 2022 15:33:47 GMT+0800 (中国标准时间)
function f() {
    console.info(temp);     // undefined
    if (   true ) {     // 替换 false 和 true,查看不同的情况
        var temp =    'hello world' ;
        console.info(temp);     //  hello world
    }
    console.info(temp);     // hello world
}
f();
console.info(temp);     //  Thu Jun 30 2022 15:33:47 GMT+0800 (中国标准时间)

  上述代码的意思指:

    在 if 代码块 外部时使用外部声明的 temp 变量,在 if 代码块内部时使用内部声明的 temp 变量。

  结果为:

    但是在 f() 函数执行后,if 代码块外部的 temp 变量打印为 undefined;如果进入过 if 代码块的话,会在 if 代码块 外,f() 函数代码块 中 打印出 if 代码块中 赋予的值;

  原因:

    由于内部变量提升,内部 if 代码块中的 temp 变量,覆盖了外部 f() 代码块中的 temp 变量(即 f() 代码块外声明的 temp变量);

  场景2、

    用于 计数 的循环变量,泄露为全局变量:

    var temp = 'hello world';
    console.info(i);  //  undefined
    for (var i = 0; i < temp.length; i++) {
        console.info(temp[i]);
    }
    console.info(i);  // 11

  

 二、ES6 的块级作用域:

  1、let 命令 实际上为 JavaScript 新增了块级作用域;

    var temp = new Date();
    function f() {
        console.info(temp);  //  Thu Jun 30 2022 16:04:47 GMT+0800 (中国标准时间)
        if (true) {
            let temp = 10;
            console.info(temp);  //  10
        }
        console.info(temp);  //  Thu Jun 30 2022 16:04:47 GMT+0800 (中国标准时间)
    }
    f();

  上述的函数有两个代码快,都声明了 temp,在 if 代码块外部 使用的是外部的 temp ,在 if 代码块内部 使用的是内部的 temp 内部的值,表示外层代码块不受内层代码快的影响。如果两次都使用 var 定义变量 temp ,最后的值会是 10;

  注意:实际使用时,尽量别 在代码快内部的变量名称 和代码块外部的变量名称设置为一样,在TypeScript 文件中会报错,同时在看代码时容易搞错逻辑;

  2、ES6 允许块级作用域的任意嵌套:

    {{{
        console.info(temp);  //  ReferenceError: temp is not defined
        {
            console.info(temp);  //  ReferenceError: temp is not defined
            let temp = 10;
            console.info(temp);  //  10
            {
                console.info(temp);  //  ReferenceError: Cannot access 'temp' before initialization
                let temp = 11;
                console.info(temp);  //  11
            }
        }
        console.info(temp);  //  ReferenceError: temp is not defined
    }}}

  上述代码,使用了一个 5层 的块级作用域,每一层都是一个单独的作用域。第三层作用域无法读取到第四层和第五层的内部变量,同理第四层无法读取第五层的内部变量;

  注意:实际使用时,不建议不同层级的作用域,定义相同的 同步变量名称;

  原理:实际上是广泛应用的匿名立即执行函数表达式;

  3、块级作用域 与 函数声明:

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

  ES6 引入了块级作用域,明确允许了会计作用中可以声明函数。

  ES6 规定,块级作用域中,函数声明语句的行为类似于 let,在块级作用域之外不可引用

    function f() {
        console.info('out');
    }
    (
        function() {
            function f() {
                console.info('in');
            }
            f();
        }
    )
    f();  // out

  上述代码在 ES6 中运行,我在 谷歌浏览器中 打印的结果是 out,但是在参考文档中解释为 会出现报错;

  这是由于环境导致的行为差异;

  结论:应该避免在块级作用域内 声明函数;如果确实需要,也应该写成函数表达式,而不是函数声明语句;比如

    //  块级作用域内部的函数声明语句,建议不要使用
    {
        let a = 'secret1';
        function f() {
            return a;
        }
    }
    console.info(f());  //  secret1

    // 块级作用域内部,优先使用函数表达式
    {
        let a = 'secret2';
        let f = function () {
            return a;
        }
        console.info(f());  //  secret2
    }
    console.info(f());  //  secret1

  ES6 块级作用域必须有大括号,如果没有大括号,JavaScrpt 引擎就认为不存在块级作用域;

    //ES6 块级作用域必须有大括号
    // 第一种写法,未添加块级作用的的大括号({}),报错
    if (true) let x = 1;  //  Lexical declaration cannot appear in a single-statement context
    // 第二种写法,不报错
    if (true) {
        let x = 1;
    }

参考文档:https://es6.ruanyifeng.com/#docs/let#%E5%9D%97%E7%BA%A7%E4%BD%9C%E7%94%A8%E5%9F%9F

 

posted on 2022-06-30 17:37  冷漠「」  阅读(47)  评论(0)    收藏  举报