var
var 存在全局作用域和函数作用域两种。没有块级作用域的说法,并且存在变量提升。
let
1.块级作用域{}
-
作用域只在他所在的代码块
-
一个 {}可以看作一个作用域
-
if语句和 for语句里面的{ }也属于块作用域
-
很适合在for循环中使用
for(var i = 0 ; i<5 ; i++){
console.log(i) //0 1 2 3 4
}
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 不允许在相同的作用域内,重复声明同一个变量。
因为函数的形参在栈中会被解析成函数的私有变量出现在其执行上下文中,let不允许重复定义。
- 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
- TDZ又称暂时性死区,意思是变量在作用域内已经存在,必须在 let / const声明后面使用。
- ES6明确规定,如果区块中存在let/const命令,这个区块用这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用的变量,就会报错
- TDZ本质:只要一进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
- 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
}
注意:
- 块级作用域允许相互嵌套
- 外层作用域不能访问内层变量
- 不同层级作用域可以定义同名变量
- es6允许在块级作用域下声明函数,在块级作用域外面不可引用
- 凡是有{}者都有块级作用域
- ES6的块级作用域必须有大括号 {} 如果没有{} js引擎认为不存在块级作用域
const
- const 声明一个只读的常量,一旦声明,常量值就不可以改变。所以const一旦声明就必须立即初始化。
- 只声明,不赋值,会报错(注意:var let 只声明不赋值 : undefined)
- 不能重新赋值const定义的值,但是可以修改const’声明的对象类型。
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
- const 的作用域与let命令相同,只在声明所在的块级作用域内有效
- const声明的变量也不存在变量提升,同样存在暂时性死区,只能在声明位置的后面使用
- const声明的常量和let一样不可重复声明
const和let的最大区别
-
const声明的对象不能重新赋值,只能赋值里面的属性值
-
let声明的变量(对象)可以重新赋值
const声明的常量和let一样不可重复声明