博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ES6系列之let/const及块级作用域
阅读量:5171 次
发布时间:2019-06-13

本文共 4814 字,大约阅读时间需要 16 分钟。

本系列是在平时阅读、学习、实际项目中有关于es6中的新特性、用发的简单总结,目的是记录以备日后温习;本系列预计包含let/const、箭头函数、解构、常用新增方法、Symbol、Set&Map、Proxy、reflect、Class、Module、Iterator、Promise、Generator、async/await

let/const为我们带来了什么?

let

  1. 约束变量提升
    (function foo() {        console.log(a);        let a = 1;    })();// Uncaught ReferenceError: a is not defined

    总结下来就是一句: 在变量使用之前,必须先要声明,变量声明永远在使用之前。

  2. 带来了块级作用域
    // es5    (function(){        if(false) {            var temp = 1;        }        console.log(temp); // undefined    })();    // es6    (function(){        if(false) {            let temp = 1;        }        console.log(temp); // Uncaught ReferenceError: temp is not defined    })();

    从代码中我们可以很清晰看到es6的let的块级作用域,那么块级作用域有什么应用呢?举个例子:

    var fnArr = [];    for(var i = 0; i < 5; i++) {        fnArr.push(function() {            console.log(i);        });    }    fnArr[0](); // 5    fnArr[1](); // 5    fnArr[2](); // 5    fnArr[3](); // 5    fnArr[4](); // 5    console.log(i); // 5

    如果没有仔细分析,执行的结果是不是有些出乎意料呢? 是的,我们本意在for循环内部使用的变量i被泄露成了全局变量,而且在for循环的每一次循环,变量i并没有被重新声明,实际上数组fnArr中保存的每一个函数中引用的都是同一个变量i,所以才导致了现在的结果,那怎么让代码按照我们最初的想法运行呢?来看es5中常用的解法

    var fnArr1 = [];    for(var i = 0; i < 5; i++) {        (function(j) {            fnArr1.push(function() {                console.log(j);            });        })(i)    }    fnArr1[0](); // 0    fnArr1[1](); // 1    fnArr1[2](); // 2    fnArr1[3](); // 3    fnArr1[4](); // 4    console.log(i); // 5

    看起来是解决了,这里实际上用到了闭包的方法,fnArr1中每一项函数引用的j都是当前循环时i的一个副本,这样就解决了前面的问题,但是还有一个问题: 变量i仍然隐式得泄露到了全局

    var fnArr1 = [];    for(let i = 0; i < 5; i++) {        fnArr1.push(function() {            console.log(i);        });    }    fnArr1[0](); // 0    fnArr1[1](); // 1    fnArr1[2](); // 2    fnArr1[3](); // 3    fnArr1[4](); // 4    console.log(i); // Uncaught ReferenceError: i is not defined

    OK,问题解决了,仅仅是将’var‘替换成了let,这就是let带来的便利。

  3. 产生暂时性死区&禁止重复声明
    // 什么是禁止重复声明呢? 先不给书面解释,来看一个es5中经常的写法(function() {        var temp = 1;        var temp = 2;        var temp = function() {            return 1;        };    })();

    上面这段代码执行没有任何问题,最终temp被赋值为一个函数

    (function() {        let temp = 1;        var temp = 2; // Uncaught SyntaxError: Identifier 'temp' has already been declared        var temp = function() {            return 1;        };    })();

    在第三行时就抛出了一个错误:temp已经被声明,是的,let声明过的变量,是不允许再次被声明的,再给几个例子巩固一下:

    (function() {        var temp = 2;        var temp = function() {            return 1;        };        let temp = 1; // Uncaught SyntaxError: Identifier 'temp' has already been declared    })();    (function() {        if(true) {            let temp = 1;            var temp = 2; // Uncaught SyntaxError: Identifier 'temp' has already been declared        }    })();(function() {        if(true) {            let temp = 1;            function temp() { // Uncaught SyntaxError: Identifier 'temp' has already been declared                return 1;            }        }    })();

    看出来了吗?只要是在let声明所在的作用域,就不允许再次声明同名变量(包括函数声明)

    var foo = 1;    if(true) {        foo = 2; // Uncaught ReferenceError: foo is not defined        let foo;    }

    看到let的’霸道‘了吧?只要在let所在的作用域,同名的变量就会被let占有,不允许重复声明,同时也要遵守let的规则

  4. 全局变量不再作为window对象的属性
    var foo = 1;    (function() {        bar = 2;    })();    window.foo; // 1    window.bar; // 2

    是的,es5中,全局变量(包括意外泄露的)都将自动被添加为window对象的属性

    let foo = 1;    window.foo; // undefined

    一切尽在不言中。。。

const

  1. let所拥有的特性,const都有,同时const还有一条:const声明的变量必须进行初始化,并且不能再被重新赋值
    const temp = 1;    temp = 2; // Uncaught TypeError: Assignment to constant variable.

    注意是不能被重新赋值,这样是比较准确的,其实const声明的变量是可以被修改的,当const声明的变量被初始化为复杂数据类型时,const声明的变量就是可变的,至于为什么,自己理解喽(变量标识符中保存的只是复杂数据类型内存地址而已。。。)

    const temp = {};    temp.foo = 'aa'; // 这里没问题    temp = {foo: 'aa'}; // 这里就会抛出异常

     

for循环中的变量声明

前面在记录let块级作用域的时候,我们使用了一个for循环的例子,这里我们不妨试着解析一下for循环的执行过程

var fnArr = [];    for(var i = 0; i < 3; i++) {        fnArr.push(function() {            console.log(i);        });    }    // 伪代码    var fnArr;    fnArr = [];    {        var i;        i = 0;        if(i < 3) {            fnArr.push(function() {                console.log(i);            })        }        i++;        if(i < 3) {            fnArr.push(function() {                console.log(i);            });        }        i++;        ...    }

这里可惜清晰得看到所有的i都是一个i。。。那使用了let以后呢?

var fnArr = [];    for(let i = 0; i < 3; i++) {        fnArr.push(function() {            console.log(i);        });    }    // 伪代码    var fnArr;    fnArr = [];    {        let i;        i = 0;        if(i < 3) {            let i = i;            fnArr.push(function() {                console.log(i);            })        }        i++;        if(i < 3) {            let i = i;            fnArr.push(function() {                console.log(i);            });        }        i++;        ...    }

是不是看出点名堂?其实我们完全可以这样理解,在每一次循环中都重新声明了i,并且被赋值为外层i的当前值。(注意啊,这里只是伪代码,便于理解,实际中let i = i是会抛出异常的)

转载于:https://www.cnblogs.com/innooo/p/10438947.html

你可能感兴趣的文章
软件公司项目经理岗位职责
查看>>
Mac下使用gitHub
查看>>
Python 持久存储
查看>>
python 爬虫 (错误很多)
查看>>
16 数值的整数次方 (第3章 高质量的代码-代码的完整性)
查看>>
C#之玩转反射
查看>>
“adb server is out of date.
查看>>
app dcloud 打包公用证书
查看>>
linq to json
查看>>
继承(初识继承,继承的进阶)
查看>>
JSON定义及应用
查看>>
TFS2018环境搭建一硬件要求
查看>>
GenericFactoryMethod泛型工厂模式实现简单IOC功能
查看>>
spring data jpa方法命名规则
查看>>
Problem Triangle
查看>>
Mysql常用函数
查看>>
Jquery对话框基本配置
查看>>
Delphi之TClientSocket和TServerSocket使用tcp keepalive心跳机制实现“断网”、"断电"检测...
查看>>
Android - 软件自动更新的实现
查看>>
php服务端setcookie()原理
查看>>