ECMAScriptECMAScript6-let与const命令详解

前言

《ECMAScript入门》是同按部就班开源的JavaScript语言教程,全面介绍ECMAScript6初引入的语法特性。
let和const命令,是首先回开介绍,也是于基础的学问。我于习下,把其总记录下来,以便自己下复习查看。
以下代码,于Chrome57 DevTools运行
node为6.3版本

先总结

先总结区别,再分别阐述

let 与 const 相同点和区分

1:let指令用来声称变量,用法类似于var,但是所声明的变量。
const声称一个才读的常量,一旦声明,常量的价值就是不能够改。
const保证的是内存地址不得变更。常量(内存地址保存的凡价值),复合类型的多寡(内存地址保存的凡指针)
2:两者都不在变量提升。
3:两者都起临时性死区(TDZ)。
4:两者都非容许再次声明。
5:两者为JavaScript新增了块级作用域。两者作用域,只在声明所当的块级作用域有效。

中心用法

let命令介绍

let令,用来声称变量。它的用法类似于var,
但是所声明的变量,只在let令所在的代码块内立竿见影。
浏览器中的结果

方代码在代码块之中,分别用letvar声明了简单单变量。然后在代码块之外调用这有限独变量,结果let扬言的变量报错,var声称的变量返回了不利的值。这表明,let扬言的变量只于她所于的代码块有效。上面代码在代码块之中,分别就此letvar声明了点儿独变量。然后以代码块之外调用这片只变量,结果let宣示的变量报错,var声明的变量返回了对的值。这表明,let宣示的变量只以它们所于的代码块有效。

let实用举例

for循环的计数器

for (let i =0; i < 10; i++) {}
console.log(i) // ReferenceError: i is not defined

浏览器中之结果

面代码中,计数器i只在for循环体内立竿见影,在循环体外引用就见面报错。

下面的代码如果用var, 最后输出的是10

var  a  =   [];
for  (var  i  =  0;  i  <  10;  i++)  {
  a[i]  =   function ()  {
    console.log(i);
  };
}
a[6](); // 10

浏览器被的结果

面代码中,变量ivar指令声明的,在全局范围外都有效,所以全局只发生一个变量i。每一样糟糕巡回,变量i的价值都见面有改变,而循环外让授予给数组a的函数内部的console.log(i),里面的i对的哪怕是全局的i。也就是说,所有数组a的分子内部的i,指向的还是跟一个i,导致运行时输出的是最后一轮子的i的值,也就是10

比方下let,
声明的变量仅以块级作用域外中,最后输出的凡6

var  a  =   [];
for  (var  i  =  0;  i  <  10;  i++)  {
  a[i]  =   function ()  {
    console.log(i);
  };
}
a[6](); // 6

浏览器结果

方代码中,变量ilet声明的,当前的i止于本轮循环中,所以各一样不成巡回的i事实上还是一个新的变量,所以最终输出的凡6。你也许会见问,如果各一样轮子循环的变量i还是再声明的,那她怎么知道上同一轮循环的价,从而计算出本轮循环的价值?这是因
JavaScript
引擎内部会铭记上亦然轱辘循环的价值,初始化本轮的变量i常常,就当达到一样轱辘循环的根基及开展计算。

另外,for循环还有一个特别之处,就是装循环变量的那么部分凡是一个父作用域,而循环体内部是一个单身的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

浏览器结果

上面代码不易运行,输出了3不行abc。这表明函数内部的变量i以及循环变量i勿以与一个作用域,有独家独立的作用域。

切莫存变量提升

var命令会发生变量提升气象,即变量可以以声明前以,值吗undefined。这种气象大多小少是来几奇怪之,按照一般的逻辑,变量应该于宣称语句之后才方可用。

为纠正这种气象,let命改变了语法行为,它所声明的变量一定要于声明后使用,否则报错。

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

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

浏览器结果

方代码中,变量foovar一声令下声明,会发变量提升,即脚本开始运行时,变量foo既存在了,但是尚未价值,所以会见输出undefined。变量barlet命令声明,不见面生变量提升。这象征于声明其之前,变量bar举凡免存在的,这时如就此到她,就见面丢弃来一个谬误。

临时死区(TDZ)

要块级作用域内存在let指令,它所声明的变量就“绑定”(binding)这个区域,不再让外部的震慑。

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

浏览器中之结果

方代码中,存在全局变量tmp,但是块级作用域内let并且声称了一个有变量tmp,导致后者绑定这个块级作用域,所以于let扬言变量前,对tmp赋值会报错。

ES6明确规定,如果区块中在letconst令,这个节对这些命令声明的变量,从平开始就形成了封闭作用域。凡是在声明前就是使这些变量,就见面报错。

一言以蔽之,在代码块内,使用let令声明变量之前,该变量都是免可用之。这当语法上,称为“暂时性死区”(temporal
dead zone,简称 TDZ)。

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

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

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

浏览器结果

点代码中,在let命令声明变量tmp之前,都属变量tmp的“死区”。

“暂时性死区”也代表typeof不再是一个周安之操作。

typeof x; // ReferenceError
let x;

浏览器结果

地方代码中,变量x使用let令声明,所以当声明前,都属于x的“死区”,只要用到拖欠变量就会见报错。因此,typeof运作时虽见面丢弃来一个ReferenceError

当比较,如果一个变量根本未曾受声称,使用typeof反而不见面报错。

typeof undeclared_variable // "undefined"

浏览器结果

地方代码中,undeclared_variable大凡一个未设有的变量名,结果回到“undefined”。所以,在无let之前,typeof运算符是全体有惊无险之,永远不见面报错。现在随即等同沾不建了。这样的统筹是为了吃大家养成良好的编程习惯,变量一定要于声明后以,否则就报错。

稍微“死区”比较隐蔽,不绝好察觉。

function bar(x = y, y = 2) {
  return [x, y];
}

bar(); // 报错

浏览器结果

方代码中,调用bar函数之所以报错(某些实现可能未报错),是为参数x默认值等于另一个参数y,而此时y尚从来不声明,属于”死区“。如果y的默认值是x,就不见面报错,因为这时候x曾经宣示了。

function bar(x = 2, y = x) {
  return [x, y];
}
bar(); // [2, 2]

浏览器结果

此外,下面的代码也会见报错,与var的行为不同。

// 不报错
var x = x;

// 报错
let x = x;
// ReferenceError: x is not defined

浏览器结果

上面代码报错,也是以少死区。使用let声明变量时,只要变量在还尚未声明完成前以,就会见报错。上面就行就属是情形,在变量x的扬言语句还未曾执行好前,就夺赢得x的值,导致报错”x
未定义“。

ES6
规定临时死区和letconst晓句子不出新变量提升,主要是为减少运作时不当,防止在变量声明前即动这变量,从而致使预期之外的作为。这样的荒谬在
ES5 是很常见的,现在发生了这种规定,避免此类错误就那个容易了。

总的说来,暂时性死区的面目就是是,只要同进入时作用域,所设下的变量就都是了,但是不可得,只有当交声明变量的那么一行代码出现,才得博得与使用该变量。

勿容许再声明

let免允以相同作用域内,重复声明与一个变量。
浏览器结果

用,不能够在函数内部还声明参数。

function func(arg) {
  let arg; //在chrome浏览器下,不报错。使用node执行,会报错。
}

function func(arg) {
  {
    let arg;//在chrome浏览器下,不报错。使用node执行,也不报错。
  }
}

浏览器环境和node执行情况

块级作用域

块级作用域,没有回去值。

何以用块级作用域?

ES5 只有全局作用域和函数作用域,没有块级作用域,这带来许多无成立之景象。

率先栽现象,内层变量可能会见蒙外层变量。

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined

浏览器结果

点代码的本心是,if代码块的标使用外层的tmp变量,内部用内层的tmp变量。但是,函数f施行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外围的tmp变量。

第二种植状况,用来计数的循环变量泄露也全局变量。

var s = 'hello';

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

console.log(i); // 5

浏览器结果

上面代码中,变量i独自所以来控制循环,但是循环结束晚,它并从未没有,泄露成了全局变量。

ES6的块级作用域

let实在为 JavaScript 新增了块级作用域。

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

浏览器结果

点的函数有星星点点只代表码块,都宣称了变量n,运行后输出5。这代表外层代码块不让内层代码块的熏陶。如果简单次等都下var概念变量n,最后输出的价才是10。

浏览器结果

ES6 允许块级作用域的妄动嵌套。

{{{{{let insane = 'Hello World'}}}}};

上面代码用了一个五层的块级作用域。外层作用域无法读取内层作用域的变量。

{{{{
  {let insane = 'Hello World'}
  console.log(insane); // 报错
}}}};

浏览器结果

内层作用域可以定义外层作用域的同名变量。

{{{{
  let insane = 'Hello World';
  {let insane = 'Hello World'}
}}}};

块级作用域的出现,实际上使得获得广泛应用的这实施函数表达式(IIFE)不再必要了。

// IIFE 写法
(function () {
  var tmp = ...;
  ...
}());

// 块级作用域写法
{
  let tmp = ...;
  ...
}

块级作用域与函数声明

  • 允许在块级作用域内声明函数。
  • 函数声明类似于var,即会提升至全局作用域或函数作用域的头。
  • 还要,函数声明还会见升级至所于的块级作用域的头部。

const命令

核心用法

const啊是为此来声称变量,但是声明的凡一个单纯念之常量。一旦声明,常量的价就是未可知改变。

const PI = 3.1415;
PI // 3.1415

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

浏览器结果

面代码表明反常量的值会报错。

const宣示的变量不得转移价值,这意味,const假设声明变量,就必须就初始化,不可知留下至后来赋值。

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

浏览器结果

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

const的作用域与let命相同:只以宣称所于的块级作用域内有效。

if (true) {
  const MAX = 5;
}

MAX // Uncaught ReferenceError: MAX is not defined

浏览器结果

const命令声明的常量也是未升级,同样在小死区,只能以宣称的职后使用。

if (true) {
  console.log(MAX); // ReferenceError
  const MAX = 5;
}

浏览器结果

点代码在常量MAX宣示前便调用,结果报错。

const声称的常量,也与let平等不可再声明。

var message = "Hello!";
let age = 25;

// 以下两行都会报错
const message = "Goodbye!";
const age = 30;

浏览器结果

于复合类型的变量,变量名不指向数,而是指于数所在的地址。
const命令只是保证变量誉为对的地方不转换,并无保险该地址之数码未更换,所以,将一个目标声明也常量必须充分小心。

const foo = {};

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

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError:  Assignment to constant variable.

浏览器结果

面代码中,常量foo囤的凡一个地址,这个地方指向一个目标。不可变的唯有是其一地点,即不克将foo对任何一个地方,但目标自我是可变的,所以依然可以啊该丰富新属性。

超过模块常量

面说了,const声明的常量只于眼前代码块有效。如果想装过模块的常量,可以利用下面的写法。

// constants.js 模块
export const A = 1;
export const B = 3;
export const C = 4;

//test1.js 模块
import * as constans from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3

// test2.js 模块
import {A, B} from './constants';
console.log(A); //1
console.log(B); //3

这里其实采取了Module(模块)的导入导出功能。以后会摆到。

大局对象的特性

大局对象是最好顶层的对象,在浏览器环境因的是window对象,在Node指的是global目标。ES5中,顶层对象的习性和全局变量是相当价格的。

window.a = 1;
a //1

a = 2;
window.a //2

浏览器结果

面代码中,全局对象的习性赋值与全局变量的赋值,是平宗事。(对于Node.js来说,这同长仅对REPL环境适用,模块环境中,全局变量必须出示声明成global对象的性质。)这种规定为市委JavaScript语言的等同不行题材。因为好易不知不觉就创办了全局变量。
ES6以转移及时一点,一方面规定,为了保兼容性,var命令和function一声令下声明的全局变量,依旧是全局对象的特性;另一方面规定,let命令、const命令和class令声明的全局变量,不属全局对象的性质。也就是说,从ES6上马,全局变量将渐渐与顶层对象的属性脱钩。

var a = 1;
// 如果在Node的REPL环境,可以写成global.a
// 或者采用通用方法,写成this.a
window.a // 1

let b = 1;
window.b // undefined

浏览器结果

上面代码中,全局变量avar指令声明,所以她是大局对象的特性;全局变量blet一声令下声明,所以它不是大局对象的性,返回undefined

参考

let 和 const 命令