let和const
let命令
-
声明的变量,只在声明的代码块中有效
-
适用于for循环
for(let i=0;i<10;i++){
//声明的i只在for循环体中有效,在外面不能访问
}-
如下,
i
递增时,已定义的数组项中的i
值,不会改变,因为无法访问到循环体中的i
-
但若
i
是通过var
定义的,则全局都可以访问,i
递增时,数组项中的i
也会改变
for (let i = 0; i < 10; i++) {
a[i]=function () {
console.log(i)
}
}
a[6](); -
-
for循环中的作用域
-
for
循环中,设置循环变量的部分和循环体中是两个独立的作用域 -
若在此两个作用域中使用
let
定义同名变量,互不影响。但若是var
定义同名变量,互相会影响for (let i = 0; i < 3; i++) {
let i='看看';
console.log(i)
}
//i等于0时进入循环,重新赋值为'看看',输出,继续判断i是否小于3,无法比较,停止循环
for (var i = 0; i < 3; i++) {
var i='看看';
console.log(i)
}
不存在变量提升
-
通过
var
声明的变量,在声明之前,会进行预解析。同样可以调用,返回undefined
-
但通过
let
声明的变量,声明之前不会预解析,不能调用console.log(a);//undefined
var a = 10;
console.log(b);//报错
let b = 20;
暂时性死区
本质:只要以进入当前作用域,所要使用的变量就已经存在了,但是还没有声明,不可获取。只有执行到了声明该变量那一行之后,才可以获取和使用该变量。
-
只要某个块级作用域内,使用了
let
声明变量,则这个变量就绑定了此块级作用域,不会被外界影响 -
此时即使是通过
var
在外部声明的变量,只要通过let
在块级作用域内也声明了。在块级作用域内、声明前调用,也会报错var a = 3;
if (true) {
a = 10;
let a;
} -
此时
typeof
不再百分百安全typeof tmp;//报错
let tmp=10; -
以下行为也会报错
-
赋值时,
x
的声明并没有完成,依然会当做没有声明处理
let x= x;
-
不允许重复声明
-
let
不允许在同一个作用域内重复声明同一个变量function func() {//报错
let arg=20;
var arg=10;
}
function func() {//报错
let arg=20;
let arg=10;
}
function func(arg) {//报错
let arg;
}
func()
function func(arg) {//不报错,不同作用域
{
let arg
}
}
func()
块级作用域
-
let
为函数新增了块级作用域
块级作用域与函数声明
-
ES5规定,函数只能在顶级作用域和函数作用域中声明,不能在块级作用域中声明
-
浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数
-
但在ES6浏览器中,浏览器的实现只需遵守:
-
允许在块级作用域内声明函数。
-
函数声明类似于
var
,即会提升到全局作用域或函数作用域的头部。 -
同时,函数声明还会提升到所在的块级作用域的头部。
-
-
考虑到环境问题,在块级作用域内声明函数时最好使用表达式
-
ES6的块级作用域必须有
{}
const
-
const
声明一个只读的常量,一旦声明常量的值就不能更改 -
const
一旦声明变量,就必须立即初始化 -
同样只在声明的块级作用域中有效
-
const
命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用
const的本质
-
实际上是保证声明的变量所指向的内存地址中所保存的值不能改变。
-
对应简单的数据类型:数字、字符串等等,值就保存在变量指向的那个内存地址,等同于常量
-
而数组、对象,因为其变量指向的地址中保存的依然是指向实际数据的指针,所以只能保证保存的指针不改变,而指针指向的实际数据可以改变,所以
const a = [];
a.push(1);//不报错
console.log(a);
a = [];//报错
冻结对象
-
如果真的想将对象冻结,应该使用
const foo = Object.freeze({});
foo.prop=123;//严格模式下报错