在ES5中,声明变量只有var和function两种形式。但是因为var声明的变量会有一定的缺点(内层变量可能覆盖外层变量的问题以及用来计数的循环变量泄露为全局变量),ES6提出了使用let和const声明变量,更加安全方便的提供我们声明变量。
var 存在全局作用域和函数作用域两种。没有块级作用域的说法,并且存在变量提升。
1.块级作用域{}
{
let a = 2;
var b = 3;
}
for(i = 0 ; i<5 ; i++){
console.log(i)
}
console.log(i) // 5
for(let i = 0 ; i<5 ; i++) {
let i = 'cht';
console.log(i); // cht *5
}
console.log(i); //Error:i is not defined
在作用域之外 i 不能被访问。从上面for循环的中可以看出,设置变量那一部分是一个作用域,循环体内部是一个单独的作用域。
2.let不允许重复声明
{
let name = "cht";
let name = "hw";
// 直接报错:Identifier 'name' has already been declared
}
{
let name = "cht";
var name ="hw";
// 直接报错:Identifier 'name' has already been declared
}
因此,在相同的作用域内,不能重复声明同一个变量
```javascript
function fn( arg){
let arg ;
}
fn() // 报错:Identifier 'name' has already been declared
function fn( arg){
{
let arg;
}
}
fn() // 不报错
为什么会这样呢? 因为函数的形参在栈中会被解析成函数的私有变量出现在其执行上下文中,let不允许重复定义。
3.不存在变量提升
只有用var 声明的变量才会有变量提升,let 和const 都不用考虑
4.脱离顶层作用域
我们知道用 var 声明的变量,可以通过window.变量名的形式使用。但是用let/const声明的变量不会绑定在顶层作用域window或globle中
var name="cht";
// 常用 this.name
window.name
let age = 15
window.age // undefined
5.暂时性死区 TDZ
if(true) {
// TDZ 开始
name = "cht" ;// Error
console.log(name); //Error
let name; // TDZ结束
console.log(name); // undefined
name = "hw";
console.log(name); // hw
}
以上代码if后面{}形成了块级作用域,由于使用let声明了name,则这个变量就绑定了块区域,在声明之前使用,会报错。
隐秘的死区【注意】
function fn (x = y , y = 2) {
return [x,y]
}
fn() // Error:Cannot access 'y' before initialization
//y没有被定义却被x使用了
var a = a;
let b = b;
function fn(arg1 = arg2 , arg2) {
console.log(`${arg1},${arg2}`)
}
fn(null , "arg2") //null,arg2
fn(undefined, "arg2") // 报错
这里涉及到null和undefined区别
ES6 规定暂时性死区和let,const语句不出现变量提升,主要是为了减小运行时的错误,防止变量在声明前就使用这个变量。
let的以上特性,为js新增了块级作用域
以前为了防止变量被污染,我们常使用自执行函数(IIFE)来防止变量被污染 ,当let广泛使用时,IIFE将不在必要
!function fn() {
var a = 5;
}()
console.log(a); // Error:a is not defined 外部访问不到
// 等同于
{
var a = 5
}
注意:
const a; //Error: Missing initializer in const declaration
const a = 5 ; // 5
// const 声明的基本类型不能被修改
const a = 10 ;
a = 12 ; //Error: Assignment to constant variable.
// const 不可重复声明
const a = 5;
const a = 6;
console.log(a)//Error: Identifier 'a' has already been declared
为什么const声明的基本类型变量不能被修改,而复杂类型变量就可以?
因为 const 保存的是指向数组或对象的指针。对于基本类型值,使用const声明的变量是不可以被修改的。但是对于对象,指针依然不能被修改,但是指针指向内容可以修改。
// const 声明的引用类型 指针不能被修改
const obj = {
name : "cht",
age : 15
}
obj = {
job : "Teacher"
}
//Error: Assignment to constant variable.
// const 定义引用类型时,值可以被修改
const obj = {
name: "cht"
}
obj.name = "hw";
console.log(obj.name) // hw