ES6——let和const

let

  ES6新增了let命令,用于声明变量。其用法类似于var,但是所有声明的变量旨在let命令所在的代码块内有效。

{
	let a = 10;
	var a = 10;
}
a; // ReferenceErrorL a is not defined
b; // 1

  上述的代码分别用let和var声明了两个变量,然后再代码块之外打印这两个变量,结果let声明的变量报错,var声明的变量返回了正确值。这表明,let声明的变量只在所在的代码块之内有效。所以let很适合再for循环内使用。

for (let i = 0; i < 5; i++) {
	setTimeout(function () {
		console.log(i)
	}1000)
}

// 每隔一秒打印i: 0 1 2 3 4 
for (var i = 0; i < 5; i++) {
	setTimeout(function () {
		console.log(i)
	}1000)
}
// 每隔一秒打印:5 5 5 5 5
console.log(i); // ReferenceError: i is not defined

  以上的计数器只在for循环体内有效,在for循环外部引用就会报错。而且在let声明的for循环内部的事件不会有闭包问题,因为let会限定代码块的执行时作用域。
  另外,for循环还有一个特别之处,就是设置循环变量的那部分时一个父级作用域,而循环内部是一个单独的子作用域。例如:

for (let i = 0; i < 3; i++) {
	let i = 'abc';
	console.log(i)
}
// 打印 abc abc abc
不存在变量提升

  var命令会发生“变量提升”的现象,即变量可以在声明之前使用,值为undefined。这种现象多少有点奇怪,按照一般的逻辑,变量应该在声明之后才能使用。
  为了纠正这种现象,let命令改变了语法行为,他所声明的变量一定要在声明后才能使用,否则就会报错。

console.log(foo); // undefined
var foo = 2;

console.log(bar); // Uncaught ReferenceError: Cannot access 'bar' before initialization
let bar = 2;

  以上代码中,foo变量用var声明后会发生变量提升,即脚本开始运行时,foo变量已经存在,但是没有值,所以会输出undefined。变量bar用let命令声明则不会发生变量提升。这表明在声明之前,bar变量是不存在的,这时使用它就会报错。

暂时性死区(TDZ)

  只要块级作用域内存在let命令,他所声明的变量就“绑定”了这个作用域,不再受外部的影响。

var temp = 123;
if (true) {
	temp = 'abc'; // ReferenceError
	let temp;
}

  上面的代码在全局定义了temp变量,但是在块级作用域中又let声明了一个局部变量,导致后者绑定了这个块级作用域,所以在let声明变量前,对temp赋值会报错。
  暂时性死区也意味着typeof不再是一个百分百安全的操作。

typeof x; // Error
let x;

在ES5非严格模式时,typeof一个没有声明的变量会返回undefined,但是在ES6会直接报错。
  有些“四区”比较隐蔽,不太容易被发现。

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

  上面的代码中,调用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不允许在相同的作用域内重复声明同一个变量。

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

  上面的代码第二个foo之所以会报错,是因为在严格模式下函数的参数默认是用let声明的,可以理解为以下形式。

function foo (let arg) {
	let arg = 1 // 报错
}

const

const命令除了具有与let相同的特征:
1. 声明的变量不存在提升
2. 不允许重复声明同一变量
3. 暂时性死区
除此之外,const声明的变量不允许改变

const本质

  const实际上保证的并不是变量的值不得改动,而是变量指向的那块内存地址不得改动。
  对于简单类型的数据(数字、字符串、布尔值等)而言,值就保存在变量指向的内存地址中,因此等同于常量.
  但是对于复杂类型的数据(对象和数组),变量指向的内存地址保存的只是一个指针,const只能保证这个指针是固定的,至于他指向的数据结构是不是固定的,这完全不能控制。因此将一个对象声明为常量时必须小心。

const foo = {};
// 为foo添加了一个属性,可以成功
foo.prop = 123;
// 并且可以改变prop属性
foo.prop = 234;
// 将foo指向另一个对象,指针发生改变,就会报错
foo = {}; 

  如果你真想声明一个不可变的对象,应该使用Object.freeze方法。

const foo = Object.freeze({});
// 在严格模式下,下面代码会报错;在非严格模式下,下面代码不起作用
foo.prop = 234;

  上面的代码中,常量foo指向一个被冻结的对象,所以添加属性时不起作用或报错。
除了将对象本身冻结,对象的属性也应该冻结。下面是将一个对象彻底冻结的函数。

var constantize = obj => {
	Object.freeze(obj);
	Object.keys(obj).forEach((key, index) => {
		if (typeof obj[key] === 'object') {
			constantize(obj[key])
		}
	})
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值