特性 | var | let | const |
---|---|---|---|
作用域 | 函数作用域 / 全局作用域 | 块级作用域 | 块级作用域 |
提升 | 提升并初始化为 undefined | 提升但不初始化 (TDZ) | 提升但不初始化 (TDZ) |
重复声明 | 允许 | 不允许 | 不允许 |
重新赋值 | 允许 | 允许 | 不允许 |
声明时初始化 | 可选 | 可选 | 必须 |
全局对象属性 | 是 (全局作用域) | 否 | 否 |
1. 最基本的区别
var VS let + const
var 能够重复声明变量
js
var name = 'why'
console.log(name) //why
var name = 'wei'
console.log(name)//wei
let 不能够重复声明相同的变量
会报错

const 不能够重复声明相同的变量

var let VS const
var 和 let 能够改变声明变量, const不能改变声明的变量
js
var ageVar = 18
console.log(ageVar) //18
ageVar = 19
console.log(ageVar) //19
let ageLet = 88
console.log(ageLet) //88
ageLet = 99
console.log(ageLet) //99
const不能重复声明
js
const ageConst = 188
console.log(ageConst)
ageConst = 111
console.log(ageConst) //报错
2. 对对象的相同点和不同点
对于var
js
var obj1 = {
name: 'why',
age: 18
}
console.log(obj1)//{ name: 'why', age: 18 }
obj1.name = 'wei'
console.log(obj1)//{ name: 'wei', age: 18 }
obj1 = {aa: 'aa'}
console.log(obj1)//{ aa: 'aa' }
对于let
js
let obj1 = {
name: 'why',
age: 18
}
console.log(obj1)//{ name: 'why', age: 18 }
obj1.name = 'wei'
console.log(obj1)//{ name: 'wei', age: 18 }
obj1 = {aa: 'aa'}
console.log(obj1)//{ aa: 'aa' }
对于const
js
const obj1 = {
name: 'why',
age: 18
}
console.log(obj1)//{ name: 'why', age: 18 }
obj1.name = 'wei'
console.log(obj1)//{ name: 'wei', age: 18 }
//会报错❌
obj1 = {aa: 'aa'}
console.log(obj1)//{ aa: 'aa' }
3. let 和 const 的作用域提升
js
console.log(nameVar)// undefined
var nameVar = 'why'
//error
console.log(nameLet)
let nameLet = 'why'
//error
console.log(nameConst)
const nameConst = 'why'
- 当使用var let const创建变量时,他们的声明的变量都会被创建出来
- 但不同的是。var创建的变量是可以被访问的,而let 和 const 创建的的变量是不能够被访问
- let和const的变量直到赋值时对变量进行绑定,才能够进行访问
我们得出结论
作用域提升:在上面变量的作用域中,如果这变量可以在声明之前被访问,那么我们称为作用域提升
let 和 const:他们和var一样变量名在声明时被创建出来了,但是不能被访问,不能称为作用域的提升
4. 与 window的关系 + 存放地址
Google的V8不能处理JavaScript中window的,window是在浏览器中进行处理的,包括window的一些属性(Date, Number)
-
var 在全局作用域中声明的变量:
- 存储位置: 这些变量被存储为全局对象 (在浏览器环境中是 window 对象,在 Node.js 中是 global 对象)的属性。
- 机制: 当 JavaScript 引擎处理全局作用域时,它会为 var 声明的变量在全局对象的属性集合中创建相应的条目。这就是为什么你可以通过 window.yourVarName (浏览器) 或 global.yourVarName (Node.js) 来访问全局 var 变量。它们与全局对象的生命周期绑定。
-
let 和 const 在全局作用域中声明的变量:
- 存储位置: 这些变量被存储在一个与全局对象不同的、独立的词法环境(Lexical Environment)中。这个环境通常被称为"脚本作用域" (Script Scope) 或顶层词法环境。它们不会成为全局对象的属性。
- 机制: ES6 引入了块级作用域和 let/const,其设计目标之一是减少对全局对象的污染。因此,规范规定全局的 let 和 const 变量存储在脚本自身的顶级作用域记录中,而不是直接附加到 window 或 global 对象上。这个脚本作用域存在于全局作用域链的最顶层,使得这些变量仍然是全局可访问的(在声明之后且脱离 TDZ),但它们与全局对象解耦了。
简单来说:
- 全局 var → 住在 window (或 global) 这栋大楼里,成为大楼的一个房间(属性)。
- 全局 let / const → 也住在这片区域(全局作用域),但它们住在自己独立的房子(脚本作用域/顶层词法环境)里,不属于 window 大楼。
为什么要知道这个区别?
- 避免全局污染: 使用 let 和 const 可以让你的全局变量不直接干扰 window 对象,减少了意外覆盖 window 内置属性或被其他库覆盖的风险。
- 理解访问方式: 你不能通过 window.myLetVariable 来访问全局 let 变量,必须直接使用 myLetVariable。
- 模块化思维: 这更符合现代 JavaScript 的模块化开发模式,每个脚本或模块可以在其顶层作用域中拥有自己的变量,而不会轻易干扰其他模块或全局环境。
js
var globalVar = 'var on window';
let globalLet = 'let in script scope';
const globalConst = 'const in script scope';
console.log(window.globalVar); // 'var on window'
console.log(window.globalLet); // undefined
console.log(window.globalConst); // undefined
console.log(globalVar); // 'var on window'
console.log(globalLet); // 'let in script scope'
console.log(globalConst); // 'const in script scope'
let 和 const 的暂时性死区(TDZ)
在代码中,使用let 和 const 声明变量,在声明变量之前,变量都是不可以访问的, 我们称这种现象为暂时性死区(TDZ)
经常出现在if条件语句
,函数语句
中
在 if 中
js
var foo = 'foo'
if (true) {
console.log(foo) //foo
}
下面的代码会报错,触发了暂时性死区(TDZ)
js
var foo = 'foo'
if (true) {
console.log(foo)
let foo = 'abc'
}
在 function 中
js
var foo = 'foo'
function bar () {
console.log(foo)
}
bar() //foo
下面的代码会报错,触发了暂时性死区(TDZ)
js
var foo = 'foo'
function bar () {
console.log(foo)
let foo = 'abc'
}
bar()
代码块
声明字面量
js
var obj = {
name: 'why'
}
什么时候选择var let const
- var 由于历史的特殊性:作用域提升,window全局对象,没有块级作用域等历史遗留问题 目前不推荐使用var
- 在开发中推荐使用let const ,我们优先使用const,可以保证数据的安全性,不会被随意的的篡改。当我们明确的知道变量后续需要被重新赋值的时候,再使用let
代码块
js
{
var foo = 'foo'
}
ES5中只有两个东西会形成块级作用域
在es5中没有块级作用域
- 全局作用域
- 函数作用域
函数作用域中通过函数链,可以访问外部的变量
当有函数嵌套时,也是同样的道理,内部的函数可以通过函数链访问到外部的变量,而外部的不能够访问内部的变量
ES6 块级作用域
代码块对var不起作用,其他起作用
js
{
var foo = 'foo'
let bar = 'bar'
const bar1 = 'bar1'
class Person {}
}
console.log(foo) //foo
console.log(bar) //error
console.log(bar1) //error
var p = new Person() //error
函数在代码块中的特殊表现
js
{
function demo () {
console.log('demo function')
}
}
demo() //demo function
为什么会这样,不是说代码块中外部无法访问吗❓ 答:不同的浏览器有不同的实行,大部分浏览器为了兼容以前的代码,让function是没有块级作用域的
常见的块级作用域
1. if语句
js
if (true) {
var foo = 'foo'
let bar = 'bar'
const bar1 = 'bar1'
}
console.log(foo) //foo ✔️
console.log(bar) //error
console.log(bar1) //error
2. switch语句
js
var color = 'red'
switch(color) {
case 'red':
var foo = 'foo'
let bar = 'bar'
const bar1 = 'bar1'
}
console.log(foo) //foo
console.log(bar) //error
console.log(bar1) //error
3. for循环语句
var 在for语句中
js
for (var i = 0; i < 1; i++) {
console.log(`hello ${i}`)
}
console.log(i)
//hello 0
//1
let (const)在for语句中
js
for (let i = 0; i < 1; i++) {
console.log(`hello ${i}`)
}
console.log(i) //error
//hello 0