ES6基础:let和const

ゝ一世哀愁。 2022-12-01 09:58 312阅读 0赞

ES6基础之let和const

变量声明是我们在学习一门语言时,最先了解的部分之一。不要忽略它,我们一起来看看ES6中新增的两种变量声明命令吧~

在开始ES6系列之初,我们就应该遵循尽量或者完全将它们应用到我们之后写的代码之中,加油!

目录

  • 一、let 命令
  • 二、const 命令
  • 三、块级作用域
  • 四、思考
  • 写在最后

一、let 命令

ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。即不会发生变量提升现象

如果大家对什么是变量提升有疑惑,不妨看看《JavaScript中的变量提升与预编译》

1.1 let拒绝提升
  1. console.log(value); // undefined
  2. var value = '余光';

通过 var 声明的变量,声明的这个操作会被提前,这导致你可以使用它虽然它的值不一样符合你的预期。

  1. {
  2. let a = 10;
  3. var b = 1;
  4. }
  5. console.log(a); // a is not defined
  6. console.log(b); // 1

可以看到变量a只在他所在的 {}中有效,这样可以有效的减少全局变量污染的情况

1.2 经典的问题的for循环问题

还是那个问题,大家看下面的例子:

  1. const arr = [];
  2. for (var i = 0; i < 10; i++) {
  3. arr[i] = () => { console.log(i) }
  4. }
  5. arr[0](); // 10
  6. arr[1](); // 10
  7. arr[2](); // 10

上面代码中,变量ivar命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

利用let的特点,我们改动一下代码:

  1. const arr = [];
  2. for (let i = 0; i < 10; i++) {
  3. arr[i] = () => { console.log(i) }
  4. }
  5. arr[0](); // 0
  6. arr[1](); // 1
  7. arr[2](); // 2

上面代码中,变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,虽然输出的变量都叫i,但他们已经存在于不同的作用域之中了。

1.3 let不能在同一个代码块中重复声明

let不允许在相同作用域内,重复声明同一个变量。

  1. // 报错
  2. function func() {
  3. let a = 10;
  4. var a = 1;
  5. }
  6. // 报错
  7. function func() {
  8. let a = 10;
  9. let a = 1;
  10. }

因此,不能在函数内部重新声明参数。

1.4 暂时性死区

大家不要被这个“高大上”的名字欺骗了

我们来看这段代码:

  1. var tmp = 123;
  2. if (true) {
  3. tmp = 'abc'; // ReferenceError: Cannot access 'tmp' before initialization
  4. let tmp;
  5. }

20200826151951631.png

再看看书中的例子:

  1. if (true) {
  2. // TDZ开始
  3. tmp = 'abc'; // ReferenceError
  4. console.log(tmp); // ReferenceError
  5. let tmp; // TDZ结束
  6. console.log(tmp); // undefined
  7. tmp = 123;
  8. console.log(tmp); // 123
  9. }

总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

小结
  1. let 和 var一样可以声明变量
  2. let 不存在变量提升
  3. let 不允许重复声明
  4. let 声明的变量存在“暂时性死区”

在这里插入图片描述

二、const命令

2.1 和let很像的const

const绝大部分特点和let一样,但是它声明是一个只读的常量。一旦声明,常量的值就不能改变。

2.2 const不可修改
  1. const PI = 3.1415;
  2. PI // 3.1415
  3. PI = 3; // TypeError: Assignment to constant variable.

上面代码表明改变常量的值会报错。

  1. const foo;
  2. // SyntaxError: Missing initializer in const declaration

上面代码表示,对于const来说,只声明不赋值,就会报错。

2.3 对于不可修改的疑惑

const的不可写,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。

但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

  1. const foo = { };
  2. // 为 foo 添加一个属性,可以成功
  3. foo.prop = 123;
  4. foo.prop // 123
  5. // 将 foo 指向另一个对象,就会报错
  6. foo = { }; // TypeError: "foo" is read-only

上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。

下面是另一个例子。

  1. const a = [];
  2. a.push('Hello'); // 可执行
  3. a.length = 0; // 可执行
  4. a = ['Dave']; // 报错

上面代码中,常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。

小结
  1. const 和 let 一样可以声明变量
  2. const 不存在变量提升
  3. const 不允许重复声明
  4. const 声明的变量存在“暂时性死区”
  5. const 声明的变量的存储地址不可写

在这里插入图片描述

三、块级作用域

在let和const声明变量时,我们经常看到“当前代码块”或“函数块”的字样,那么我们如何对这个进行定义呢?

ES6的块级作用域——有大括号( {}或() ),如果没有大括号,JavaScript 引擎就认为不存在块级作用域。

那么在没有块级作用域时,会发生什么问题呢?

3.1 内层变量可能会覆盖外层变量。
  1. var params = { name: '余光' };
  2. function func() {
  3. console.log(params);
  4. var params = {
  5. name: 10
  6. }
  7. }
  8. func(); // undefined

被var声明的变量存在极大的风险

3.2 第二种场景,用来计数的循环变量泄露为全局变量。

又是那个熟悉的场景

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

上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。

四、思考

既然ES6是Js的新标准(现在已经不算是新标准了…),它的向后兼容是什么样的呢?

我们借助babel来看一下文章内提到的部分例子:

4.1 单纯的 let 和 const 声明
  1. let num = 1;
  2. const str = 'str';
  3. // babel
  4. var num = 1;
  5. var str = 'str';

咦~,应该babel转换后变成了var,我们是在无用功吗?

4.2 块级作用域中的 const 和 let
  1. const bool = true;
  2. if (bool) {
  3. let a = 1;
  4. }
  5. console.log(a);
  6. // babel
  7. var bool = true;
  8. if (bool) {
  9. var _a = 1;
  10. }
  11. console.log(a);

你会发现,a变量变成了 _a变量,这样你在作用域外使用它时,当然没有这个变量!

4.3 for 循环中的let
  1. const arr = [];
  2. for (let i = 0; i < 10; i++) {
  3. arr[i] = function() { console.log(i) }
  4. }
  5. // babel
  6. var arr = [];
  7. var _loop = function _loop(i) {
  8. arr[i] = function() {
  9. console.log(i);
  10. };
  11. };
  12. for (var i = 0; i < 10; i++) {
  13. _loop(i);
  14. }

你会发现let声明i,在每次循环中都会有一个独立的作用域,他们互不影响。

到这里关于let和const就总结到这里次

参考

  • ES6系列之let和const
  • ES6入门

写在最后

JavaScript系列:

  1. 《JavaScript内功进阶系列》(已完结)
  2. 《JavaScript专项系列》(持续更新)
  3. 《ES6基础系列》(持续更新)

关于我

  • 花名:余光(沉迷JS,虚心学习中)
  • WX:j565017805

其他沉淀

  • Js版LeetCode题解
  • 前端进阶笔记
  • 我的CSDN博客

如果您看到了最后,对文章有任何建议,都可以在评论区留言

这是文章所在GitHub仓库的传送门,如果真的对您有所帮助,希望可以点个star,这是对我最大的鼓励 ~

20200602155947301.png

发表评论

表情:
评论列表 (有 0 条评论,312人围观)

还没有评论,来说两句吧...

相关阅读

    相关 es6let const

    Let和const 一、Let 1.let用来声明变量,并且会在当前作用域形成代码块;let声明的变量可以改变,(值和类型都可以改变)。 { le

    相关 ES6基础letconst

    ES6基础之let和const > 变量声明是我们在学习一门语言时,最先了解的部分之一。不要忽略它,我们一起来看看ES6中新增的两种变量声明命令吧~ 在开始ES6系列之

    相关 es6 let const 命令

    一.let 1.作用域 在javascript中只有全局作用域和函数作用域,并不存在块级作用域。这样,在使用时就会出现一些问题。 下面我们先来举例说明let块级作用