ES6入门 (一)
let 和 const 命令
- 定义:let 和 const 都是ES6 当中声明变量的
关键字
let
定义
let 用法与var 相似
注意事项
块级作用域
我们使用let 会创建一个块级作用域,这个变量只能在这个块级作用域当中使用。那么问题来了,什么是块级作用域呢?在我们学习js的时候相信大家都知道什么是 局部作用域
什么是 全局作用域
。下面我们例子说明一下
<script>
let a = 5; //当我们在全局范围内声明一个变量的时候,这个变量的块级作用域就是全局作用域
function fn1(){
let b = 10 //当我们在局部范围内声明一个变量,他的块级作用域就是局部作用域
}
</script>
- ES5当中 内存变量可能会覆盖外层变量
var a = 'abc';
function f() {
// 函数内部变量提升 相当于 var a 的声明
console.log(a);
if(false) {
var a = '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
- ES5中 函数只能在顶层的作用域和函数作用域之中声明,不能在块级作用域当中声明。而ES6当中
允许
在块级作用域之中声明函数。但是函数声明语句的行为类似于let
,在块级作用域之外不可引用
不存在变量提升
如果你已经学过 js 并且有一定的js 基础 那么你对 变量提升
一定不陌生。我们都知道如果按正常逻辑,我们必须声明一个变量,然后才应该可以使用他,而变量提升就可以让我们在声明变量之前就使用这个变量(只是不会报错)
变量提升简单来说:就是在js 代码解析的过程当中 我们会把所有的声明的变量提升到这个变量所在作用域的第一行,也就是说我们在给变量赋值之前使用变量的时候不会报错,因为变量提升让变量的声明体现到了代码解析的最前面。
而我们使用let 声明变量的时候,不存在变量提升。也就是说我们在声明这个变量之前不可以使用这个变量否则就会报错
// 变量提升 相当于 var a 提前声明了一个变量 只有经历赋值操作才可以有值
console.log(a) // undefined
var a = 2
console.log(a) // 2
console.log(b) // 报错
let b = 2
console.log(b) // 2
let的特点就是存在暂时性死区
只要块级作用域内存在 let
命令,它所声明的变量就 “绑定” (binding)
这个区域,不再受外部的影响。
var a = 10;
if (true) {
a = 5; // ReferenceError
let a;
}
在这个代码中, 存在一个全变量 a
, 但是在 if
块级作用域内又声明了一个局部变量a,导致局部变量a被绑定在这个块级作用域,所以在let
声明变量前,使用这个局部变量都会报错。
ES6 明确规定,如果区块中存在let
和const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let
命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)
if(true) {
a = 'abc'; // ReferenceError
console.log(a) // ReferenceError
let a ; //遇到了变量的声明 暂时性死区结束
console.log(a) //已经遇到了变量的声明,这个时候打印他 a的值是 undefined
a = 123;
console.log(a) // 结果 123
}
特殊情况的暂时性死区 之 ES6函数存在默认值情况
function fn1(x=y,y=2) {
return [x,y]
}
fn1() //报错
如果你是初次介绍es6 我可以简单给你先介绍一下es6中函数存在默认值的情况。函数的默认值呢,是在我们使用函数并且没有给函数传递实参的时候,这时他的参数的值就等于我们给这个参数设置的默认值
在上述代码中 参数x
默认值等于另一个参数 y
但是在这个时候 y
还没有声明,属于 暂时性死区。
function fn1(x=2,y=5) {
return [x,y]
}
fn1() // //[2,5] 因为我们没有给这个函数传递实参 所以参数的默认值就起了作用
fn1(3) // [3,5]
fn1(3,4) //[3,4]
默认参数的作用域
一旦设置了参数的默认值,函数进行声明初始化的时候,参数会形成一个单独的作用域。等到初始化结束,这个作用域就会消失 。
var x = 1;
function f(x,y=x) {
console.log(y)
}
f(2) // 2
在上述代码当中,参数y
的默认值等于变量x
。调用这个函数的时候,参数形成了一个单独的作用域 ,在这个作用域里面,函数的一个参数指向的是调用这个函数传递的实参2
let x = 2
function f1(y=x){
let x = 3;
console.log(y)
}
f1() // 2
在上述代码中同样涉及到一个高级概念 作用域链
。与上面例子一样,调用这个函数的时候y=x 形成了一个单独的作用域他是局部作用域,因为在这里x的值不存在,所以根据作用域链,他会向上一层作用域寻找x直到找到他 所以这个例子中 参数y的默认值x 是全局作用域当中声明的变量x
不允许重复声明变量
function fn2() {
let a = 10;
let a = 1; // 报错
}
fn2()
function fn3(x) {
let x;
}
fn3() // 报错
function fn4(x) {
{
let x ;
}
}
fn4() // 不报错
const 关键字
定义:
const
声明一个只读的常量。一旦声明,常量的值就不能改变。
注意事项
const
命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
const命令的本质
const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const
只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only