一、let 的使用
ES6中新增了一种变量声明方式 let ,let 用来声明变量,用法和 var 相似,但是 let 声明的变量只在代码块内有效。
与 var 相比,let 大概有下面几种特殊之处
1.块级作用域
2.不存在变量提升
3.暂时性死区
4.不允许重复声明
1.1块级作用域
在es5中是没有块级作用域的,es5只有全局作用域和函数作用域,es6新增了块级作用域
{
let a = 6;
var b = 9;
}
console.log(a); // 报错
console.log(b); // 9
上面代码中 var在大括号中声明的变量在括号外面还是可以取到值,let声明的变量不能,let声明的变量会形成块级作用域,防止变量全局污染
1.1.1块级作用域的作用
块级作用域一个很好的用处是 for ,比如
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
上面第一段代码中,变量i
是var
命令声明的,在全局范围内都有效,所以全局只有一个变量i
。每一次循环,变量i
的值都会发生改变,而循环内被赋给数组a
的函数内部的console.log(i)
,里面的i
指向的就是全局的i
。也就是说,所有数组a
的成员里面的i
,指向的都是同一个i
,导致运行时输出的是最后一轮的i
的值,也就是 10。
第二段代码中,变量i
是let
声明的,当前的i
只在本轮循环有效,所以每一次循环的i
其实都是一个新的变量,所以最后输出的是6
。你可能会问,如果每一轮循环的变量i
都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i
时,就在上一轮循环的基础上进行计算。
此外 for 循环还有一个要注意的地方,for 循环里面循环变量设置和循环体是两个作用域
循环变量设置是父作用域 循环体是子作用域
for(let i = 0; i <=10; i++){
let i = 10;
console.log(i);
}
// 输出 11 个 10
1.2不存在变量提升
var声明的变量都可以变量提升,let 声明的变量不可以
例如:
console.log(a); // undefined
var a = 2;
console.log(b); // 报错
let b = 2;
javascript引擎在执行代码是会将 var 声明的变量放到代码最前面,即声明会提升到最前面,变量赋值没有提升,所以变量初始化为 undefined,故第一段代码输出为 undefined
1.3暂时性死区
暂时性死区是在一个块级区域声明了 let 变量,则在这个块级区域中,let声明前的任何区域使用该变量都会报错
{
a = a + 1; //报错
let a = 2;
}
{
a = 3; //报错
let a = 2;
}
1.4不允许重复声明
let
不允许在相同作用域内,重复声明同一个变量。
// 报错
function func() {
let b= 10;
var b = 1;
}
// 报错
function func() {
let b = 10;
let b = 1;
}
二、const常量
1.基本用法
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
1.1声明基本类型
用 const 声明基本数据类型值,声明之后不能再改变
const
声明的变量不得改变值,这意味着,const
一旦声明变量,就必须立即初始化,不能留到以后赋值。
1.2引用类型
const foo = {};
foo.prop = 123;
foo.prop // 123
foo = {}; // TypeError: "foo" is read-only
const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const
只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。