ES6语法中let与const命令的区别

let命令

基本用法
类似于var,但是所声明的变量,只在let命令所在的代码块中有效。

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

上面代码中,分别用let,var 声明两个变量,然后再代码块之外调用两个变量,let变量报错,var变量返回正确的值,这表明,let声明的变量只在他的代码块中有效。

for循环的计数器,很适合let命令

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

上面代码中计数器只在循环体中有效,循环体外引用就会报错

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

上述代码中,变量i是var声明的,所以每一轮循环,新的 i 值都会被覆盖,所以最后输出的是最后一轮的 i 的值是10

如果使用let,声明的变量仅在块级作用域中有效,最后输出的是6

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

上面代码中,变量i 是let声明的,当前的 i 只在本轮循环中有效,所以每一次循环i 其实就是新的变量,到最后输出的是6

使用let不存在变量提升

let 不会像 var 那样会发生变量提升的现象,所以,变量变量一定在声明后使用,否则就会报错。

console.log(a)  // undefined
console.log(b) // 报错ReferenceError

var a  = 1;
let b = 2

上面代码中 a 用var 声明会变量提升,这时候提升只是a 提升没赋值,所以是undefined ,变量 b 不存在变量提升,所以报错

暂时性死区(TDZ)

只要块级作用域中存在let命令,那么他所声明的变量就绑定在这个区域,不再受到外部影响

var test = 123;
if(true){
	test = "abc" // ReferenceError
	let test
}

上面代码中,存在全局变量test , 但是在块级作用域中又声明个局部变量test,导致后者绑定块级作用域,所以在 let 声明前使用会导致报错。
“暂时性死区”也意味着typeof 不再是个百分之百安全的操作了.

type x; //ReferenceError
let x 

作为比较,如果一个变量根本没有被声明,使用typeof 反而不会被报错

typeof test  // underfined

上面test是不存在的变量名,结果返回undefined,所以在没有let 之前 ,typeof 是完全安全的。
有些“死区”比较隐蔽,不太容易发现。

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

bar(); // 报错

上面代码中,调用函数之所以报错,是因为 x 默认是另一个函数y,而此时y 还没有声明,属于“死区”,如果y 的默认值是x,就不会报错,因为此时x已经声明了。

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

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

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

不允许重复声明

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

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

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

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

function func(arg) {
  let arg; // 报错
}

function func(arg) {
  {
    let arg; // 不报错
  }
}

ES6规定,块级作用域中,函数声明语句行为类似于let,在块级作用域之外不可引用。

	function f() { console.log('I am outside!'); }
	(function () {
	  if (false) {
	    // 重复声明一次函数f
	    function f() { console.log('I am inside!'); }
	  }
	
	  f();
	}());

上面代码在ES5下运行,会得到“I am outside” ,因为 if 内声明的函数 f 会提升到函数头部,实际运行代码如下

// ES5版本
function f() { console.log('I am outside!'); }
(function () {
  function f() { console.log('I am inside!'); }
  if (false) {
  }
  f();
}());

但是ES6就不一样了

// ES6版本
function f() { console.log('I am outside!'); }
(function () {
  f();
}());

do表达式

本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值

{
  let t = f();
  t = t * t + 1;
}

上面代码中,块级作用域将两个语句封装在一起。但是,在跨级作用域以外不能得到 t 的值,除非 t 是全局变量。
变成do表达式可以获得返回值

let x = do {
  let t = f();
  t * t + 1;
};

上述代码中,变量x 会获得整个作用域中的返回值

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;
}

const 与 let 声明的变量一样不可重复声明

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

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

对于复合型的变量,变量名不指向数据,而是指向数据所在的地址。const 命令指示保证变量名指向的地址不变,并不能保证该地址的数据不变,所以将一个对象声明为常量必须非常小心

const foo = {};
foo.prop = 123;

foo.prop
// 123

foo = {}; // TypeError: "foo" is read-only

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

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

顶层对象的属性

顶层对象,在浏览器环境指的是window对象,在Node指的是global对象。ES5之中,顶层对象的属性与全局变量是等价的。

window.a = 1;
a // 1

a = 2;
window.a // 2

上面代码中,顶层对象的属性赋值与全局变量的赋值,是同一件事。
顶层对象的属性与全局变量挂钩,被认为是JavaScript语言最大的设计败笔之一。这样的设计带来了几个很大的问题,首先是没法在编译时就报出变量未声明的错误,只有运行时才能知道(因为全局变量可能是顶层对象的属性创造的,而属性的创造是动态的);其次,程序员很容易不知不觉地就创建了全局变量(比如打字出错);最后,顶层对象的属性是到处可以读写的,这非常不利于模块化编程。另一方面,window对象有实体含义,指的是浏览器的窗口对象,顶层对象是一个有实体含义的对象,也是不合适的。

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

与诸君共勉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值