let命令
特点
- 不存在变量提升,暂时性时区,在let变量声明之前使用变量会报错
- 块级作用域, {}代码块内的局部变量
- 不允许重复声明,在同一代码块内部(相同作用域)不能重复声明
- 不影响作用域链
基本用法
用来声明变量,类似于
var
,但所声明变量,只在
let
命令所在的代码块内有效
{
let a = 10
var b = 1
}
console.log(a) //报错,a is not defined
console.log(b) //1
可以用let来很好地解决闭包问题
//es5
var a = []
for(var i=0;i<10;i++){
a[i] = function(){
console.log(i)
}
}
a[6]() //10
//es6
var a = []
for(let i=0;i<10;i++){
a[i] = function(){
console.log(i)
}
}
a[6]() //6
因为i是let声明的,当前的i只在本轮循环有效。
另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
for(let i=0;i<3;i++){
let i = 'abc'
console.log(i) //abc
}
不存在变量提升
var
会发生变量提升现象,即变量可在声明之前使用,值为
undefined
.
let
没有变量提升现象,所声明的变量一定要在声明之后使用,否则报错
//var
console.log(foo) //undefined
var foo = 2
//let
console.log(bar) //报错,在用let声明bar之前,它是不存在的
let bar = 3
暂时性死区
只要在块级作用域内存在
let
命令,它所声明的变量就绑定了这个区域,不再受外界影响,在变量被let声明之前都是该变量的死区
var tem = 123
{
temp = 'abc' //报错
let temp
}
不允许重复声明
let
不允许在相同作用域内,重复声明同一个变量
//报错
function func(){
let a = 10
var a = 1
}
//报错
function func2(){
let a = 10
let a = 2
}
块级作用域
为什么需要块级作用域?
ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景
第一种场景:内层变量可能会覆盖外层变量
第二种场景:用来计数的循环变量泄露为全局变量
ES6的块级作用域
ES6的块级作用域必须有大括号,如果没有大括号,JavaScript引擎就认为不存在块级作用域
let
实际上为JavaScript新增了块级作用域,ES6允许块级作用域的任意嵌套
function fn(){
let n = 5
if(true){
let n = 10
}
console.log(n) //5
}
内层作用域可以定义外层作用域的同名变量
{
{
let a = 10
{
let a = 20 //这样不会报错,因为两个a不在同一作用域内
}
}
}
块级作用域与函数声明
ES5规定,函数只能在顶层作用域和函数作用域中声明,不能再块级作用域中声明
ES6引入了块级作用域,明确允许在块级作用域之中声明函数。ES6规定,块级作用域之中,函数声明语句的行为类似于
let
,在块级作用域之外不可引用
function fn(){
console.log('outside')
}
(function(){
if(false){
function fn(){
console.log('inside')
}
}
fn()
}())
在ES5中结果是inside,因为在
if
内声明的函数会被提升到函数头部
在ES6中运行会报错
浏览器的实现可以不遵守上面的规则,有自己的行为方式
- 允许在块级作用域内声明函数
- 函数声明类似于
,即会提升到全局作用域或函数作用域的头部var
- 同时,函数声明还会提升到所在的块级作用域的头部
注意
:上面三条规则只对ES6的浏览器实现有效,其它环境都不用遵守,还是将块级作用域的函数声明当做
let
处理,块级作用域内部的函数声明语句,建议不要使用,优先使用函数表达式
const命令
特点
- 用来声明一个常量
- 声明必须赋初始值,const声明与赋值必须同时执行,也就是说只声明不赋值就会报错
- 块级作用域
- 不存在变量提升
- 存在暂时性死区
- 不允许重复声明
- 标识符一般为大写
- 值不允许修改
声明一个只读常量
const PI = 3.1415
PI = 3 //报错,不能修改const声明基本数据类型
声明与赋值必须同时执行
const foo //报错
块级作用域
{
const a = 10
}
console.log(a) //报错, a is not defined
变量不能提升,暂时性死区,不能重复声明
{
console.log(a) //报错,const声明的变量只能在声明之后使用
const a = 1
const a = 2 //报错
}
本质
const
实际上保证的是变量指向的那个地址所保存的数据不能改变。
对于基本数据类型来说,值保存在变量指向的那个内存地址,等同于常量。
对于引用类型数据来说,变量保存的是指向实际数据的指针,
const
只保证指针固定,变量的数据结构不会固定。
所以在将一个对象声明为常量时必须非常小心。
const foo = {}
//可以为foo添加属性
foo.name = 'zhangsan'
foo.age = 20
//但是将foo指向另一个对象就会报错
foo = {} //报错 TypeError: "foo" is read-only
ES6声明变量的六种方式
在ES5中,只有两种声明变量的方式,
var
和
function
命令。ES6除了添加了
let
和
const
命令,还有另外两种声明变量的方法:
import
和
class
命令。
顶层对象的属性
window.a = 5
console.log(a) //5
b = 2
console.log(window.b) //2
var a = 1
//node中是global.a
console.log(window.a) //1
let b = 2
console.log(window.b) //undefined