侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 285 篇文章
  • 累计创建 125 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

ES6新特性1:let 和 const 命令

孔子说JAVA
2022-05-05 / 0 评论 / 0 点赞 / 135 阅读 / 3,996 字 / 正在检测是否收录...

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,JavaScript是ECMAScript的拓展语言,ECMAScript只是提供了最基本的语法。2009年ES5首次标准化,2015年6月发布了ES2015版本,简称ES6,该版本提供了很多新特性,是该语言的重要更新。在一些文档中用ES6来泛指ES2015之后所有的新标准,要注意分辨用ES6是特指还是泛指。

根据官网(https://262.ecma-international.org/6.0/)描述,ES6主要特性归纳如下:

  1. 解决了原有语法的一些问题或者缺陷。
  2. 对原有语法进行增强。
  3. 全新的对象、全新的方法、全新的功能。
  4. 全新的数据类型,数据结构。

本文主要介绍ES6新特性:let 和 const 命令。

1、var变量

ES5定义变量常用的方式,存在变量提升现象,即变量可以在声明之前试用,有可能造成不可预测的问题。

  • let 是在代码块内有效,var 是在全局范围内有效
// var 的情况
console.log(a); // 输出undefined
var a = 1;

// let 的情况
console.log(b); // 报错ReferenceError
let b = 1;


for (var i = 0; i < 5; i++) {
    console.log(i); // 输出0,1,2,3,4
}
// 变量i会成为全局变量
console.log("循环外:" + i);  // i 输出为5

2、let变量

2.1 基础用法

ES6 新增了let命令,用来声明变量。它的用法类似于var,不同于var的地方是let变量必须在声明后才能试用,且只在let命令所在的代码块内有效,为块级作用域。适合设置在for循环中的循环变量,let不允许在相同作用域内,重复声明同一个变量。

  • let 声明的变量只在 let 命令所在的代码块内有效。
for (let i = 0; i < 5; i++) {
    console.log(i); // 输出0,1,2,3,4
}
console.log("循环外:" + i); // ReferenceError: i is not defined

for (let i = 0; i < 10; i++) {
    // let i = "foo"; 。// 注释打开,输出10个foo
    console.log(i); // 输出0 - 9
}

function func() {
    let a = 10;
    var a = 1; // SyntaxError: Identifier 'a' has already been declared
}

2.2 代码块内有效

let 是在代码块内有效,var 是在全局范围内有效

{
  let a = 0;
  var b = 1;
}
a  // ReferenceError: a is not defined
b  // 1

2.3 不能重复声明

let 只能声明一次 var 可以声明多次:

let a = 1;
let a = 2;
var b = 3;
var b = 4;
a  // Identifier 'a' has already been declared
b  // 4

2.4 for 循环计数器很适合用 let

for (var i = 0; i < 10; i++) {
  setTimeout(function(){
    console.log(i);
  })
}
// 输出十个 10
for (let j = 0; j < 10; j++) {
  setTimeout(function(){
    console.log(j);
  })
}
// 输出 0123456789
  1. 变量 i 是用 var 声明的,在全局范围内有效,所以全局中只有一个变量 i, 每次循环时,setTimeout 定时器里面的 i 指的是全局变量 i ,而循环里的十个 setTimeout 是在循环结束后才执行,所以此时的 i 都是 10。
  2. 变量 j 是用 let 声明的,当前的 j 只在本轮循环中有效,每次循环的 j 其实都是一个新的变量,所以 setTimeout 定时器里面的 j 其实是不同的变量,即最后输出 12345。(若每次循环的变量 j 都是重新声明的,如何知道前一个循环的值?这是因为 JavaScript 引擎内部会记住前一个循环的值)。

2.5 不存在变量提升

var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

  • let 不存在变量提升,var 会变量提升
// var 的情况
console.log(flag); // 输出undefined
var flag = 2; // 用var命令声明会发生变量提升

// let 的情况
console.log(plus); // 报错ReferenceError
let plus = 2;

变量flag发生了变量提升,即脚本开始运行时就已经存在了,但是没有值,所以前面会输出undefined。变量plus用let命令声明,不会发生变量提升。这表示在声明它之前,变量plus是不存在的,这时如果用到它,就会抛出一个错误。

2.6 块级作用域

ES5 只有全局作用域和函数作用域(var),没有块级作用域。ES6中的 let 为 JavaScript 新增了块级作用域。使用var有可能造成不合理的场景或不可预知的后果,如内外层变量的互相覆盖,局部变量变为全局变量等。

// 情况一:变量提升
var inner = new Date();

function f() {
  console.log(inner);
  if (false) {
    // 内层变量inner会覆盖外层变量inner, 为变量提升的情况
    // 在上一句输出语句中innner作为内部对象还未定义。
    var inner = 'hello world';
  }
}

f(); // undefined

// 情况二:计划作为局部变量的i,变为全局变量

for (var i = 0; i < 5; i++) {
  console.log(i);
}
console.log(i); // 5

// 情况三:块级作用域

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  // 外层代码块不受内层代码块的影响。如果两次都使用var定义变量n,
  // 最后输出的值才是 10。ES6 允许块级作用域的任意嵌套。
  console.log(n); // 5
}

3、const常量

3.1 基础用法

ES6 新增了const命令,用来声明常量,不能被修改,且必须在声明时赋值;类似于Java中的final关键字,为块级作用域。

  • const 声明一个只读的常量,一旦声明,常量的值就不能改变。这意味着,一旦声明必须初始化,否则会报错。
  • const实际上保证的,并不是变量的值不能改动,而是变量指向的那个内存地址所保存的数据不得改动。
  • 对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。
  • 但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const a = 1;
console.log("a = ", a);
//给a重新赋值
a = 2; // TypeError: Assignment to constant variable.
console.log("a = ", a);

3.2 暂时性死区(temporal dead zone,简称 TDZ)

ES6 明确规定,如果块级作用域中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。即它所声明的变量会“绑定”(binding)这个区域,不再受外部的影响。在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

var test = 123;

if (true) {
    test = 'abc'; // ReferenceError: Cannot access 'test' before initialization
    let test;
}
  • “暂时性死区”也意味着typeof不再是一个百分之百安全的操作。
typeof x; // ReferenceError: Cannot access 'x' before initialization
let x;

变量x使用let命令声明,所以在声明之前,都属于x的“死区”,只要用到该变量就会报错。因此,typeof运行时就会抛出一个ReferenceError。作为比较,如果一个变量根本没有被声明,使用typeof反而不会报错。

ES6 规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。

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

4、注意要点

const 如何做到变量在声明初始化之后不允许改变的?

其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动。此时,你可能已经想到,简单类型和复合类型保存值的方式是不同的。是的,对于简单类型(数值 number、字符串 string 、布尔值 boolean),值就保存在变量指向的那个内存地址,因此 const 声明的简单类型变量等同于常量。而复杂类型(对象 object,数组 array,函数 function),变量指向的内存地址其实是保存了一个指向实际数据的指针,所以 const 只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了,所以使用 const 声明复杂类型对象时要慎重。

0

评论区