ES6基础系列之let和const(系列一)

时间:2020-8-27 作者:admin


ES6基础之let和const

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

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

目录

一、let 命令

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

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

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

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

{
  let a = 10;
  var b = 1;
}
console.log(a); // a is not defined
console.log(b); // 1

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

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

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

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

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

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

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

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

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

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

// 报错
function func() {
  let a = 10;
  var a = 1;
}

// 报错
function func() {
  let a = 10;
  let a = 1;
}

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

1.4 暂时性死区

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

我们来看这段代码:

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError: Cannot access 'tmp' before initialization
  let tmp;
}

ES6基础系列之let和const(系列一)

再看看书中的例子:

if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

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

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

ES6基础系列之let和const(系列一)

二、const命令

2.1 和let很像的const

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

2.2 const不可修改
const PI = 3.1415;
PI // 3.1415

PI = 3; // TypeError: Assignment to constant variable.

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

const foo;
// SyntaxError: Missing initializer in const declaration

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

2.3 对于不可修改的疑惑

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

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

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

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

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

下面是另一个例子。

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

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

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

ES6基础系列之let和const(系列一)

三、块级作用域

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

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

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

3.1 内层变量可能会覆盖外层变量。
var params = { name: '余光' };

function func() {
    console.log(params);
    var params = {
        name: 10
    }
}

func(); // undefined

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

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

又是那个熟悉的场景

var s = 'hello';

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

console.log(i); // 5

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

四、思考

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

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

4.1 单纯的 let 和 const 声明
let num = 1;
const str = 'str';

// babel

var num = 1;
var str = 'str';

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

4.2 块级作用域中的 const 和 let
const bool = true;
if (bool) {
	let a = 1;
}
console.log(a);

// babel
var bool = true;

if (bool) {
  var _a = 1;
}

console.log(a);

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

4.3 for 循环中的let
const arr = [];
for (let i = 0; i < 10; i++) {
    arr[i] = function() { console.log(i) }
}

// babel
var arr = [];

var _loop = function _loop(i) {
    arr[i] = function() {
        console.log(i);
    };
};

for (var i = 0; i < 10; i++) {
    _loop(i);
}

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

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

参考

写在最后

JavaScript系列:

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

关于我

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

其他沉淀

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

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

ES6基础系列之let和const(系列一)

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。