let const var 的区别

530 阅读4分钟

ES6 之前,我们一直使用 var 定义变量, 而且可以反反复复的修改var定义的内容,比如从基础类型改为引用类型,引用类型改为基础类型

例如:

示例-1

// 基本类型
var a = 1

// 直接修改为引用类型
a = {
	name: "张三"
}
console.log(a) // {name: "张三"}

示例1,直接将基本类型修改为引用类型, 也不会报错,也不会有任何 warning

示例-2

// 从写法上,意图是定义一个常量
var pi = 3.14

// 修改为一个 函数
pi = function() {
	return true
}

pi() // true

示例2,直接将 我们认知的一个常量,修改为 一个function , 也是不会报错的

所以, 我们知道 javascript在灵活方面,确实比强类型语言灵活,但灵活的同时, 缺乏约束, 在大型协同的过程中,导致各种变量值污染, 及其难排查

所以, ES6 推出let 和const 在语法层面上, 对代码进行强约束,具体的区别如下


一、作用域不同

在ES5的时候,js只有函数级作用域和全局作用域,没有块级作用域,所以,会看到以下的情况

for(var i=0; i<5; i++) {
	// do something
}

console.log(i) //  5

为什么是 5呢,其实 for这个括号里的var i 和下面的是等价的

var i; // undefined
for(i=0; i<5; i++) {

}

console.log(i)

因为var 定义的 根本不是块级作用域, i通过变量提升,提升到了最外层作用域, 所以,var定义的 i 跟外面的作用域 是一样的, 所以可以访问

let 和 const

let和const 都是块级作用域, 和面向对象的 java 等强类型语言,是一致的

  1. 未定义的时候, 不能提前访问,否则报 Cannot access 'a' before initialization
console.log(a)
let a = 1

意思很明确,不能在定义之前去访问 a变量

  1. 有了块级作用域
{
    let a = 10;
    var b = 7;
}

console.log(b) // 7
console.log(a) // Uncaught ReferenceError: a is not defined

这里就对比了 let 和 var在作用域的区别, b没有块级作用域的概念,所以,是和 下面的console是同级的, 而let 定义的就被限定在 块级作用域内, 外部无法访问,所以报错

for (let x = 0; x < 5; x++) {
}
console.log(x) // x is not defined

这个常见的 for循环 也是 let 和 var的一个区别,其实质就是 2个 (var 没有块级作用域的概念, let有; var会存在变量提升,let不会)

示例

var arr = [];
for(var i=0; i<5; i++) {
    arr[i] = function() {
        console.log(i)
    }
}

arr[3]() // 5
arr[4]() // 5

上面的i 仍旧是全局的, 所以,每次在循环迭代的时候,是将全局的 i 更新了 然后再执行方法的时候, 由于方法内部没有找到i的定义,就往上层找, 然后找到了全局, 而此时全局的 i 已经是 5, 所以 执行 arr[3]() 和 arr[4]() 都是 5

二、var 定义的变量,会挂载到window对象上, let和const不会

我们定义一个 var 变量, 会将值默认挂在到 window对象上, 当然,会有2种情况会自动挂载

  1. 全局定义的变量
var a = 1;

// 等价于 
window.a = a
  1. 函数内使用未定义的变量, 等价于在全局声明 window.XXX属性
function f() {
  b = 12
}
        
// 执行 f, 下面的 b就是 12
// f()

console.log(window.b)

其实,js是存在变量提升的问题,导致 上面的 window.b js在未执行的时候,会先分析,哪些变量,需要进行提升, 然后如果在当前的作用域中没有找到,就继续往上层找(追溯), 直到到 全局对象上为止(浏览器的顶层对象是 window, nodejs中是 global)

当发现 b 有被使用,但是在 f函数这个作用域中没有找到定义的b的语句, 就继续找,往上一层,就找到了全局 window

所以,就会先静默定义一个 window.b = undefined

三、变量提升

let 和 const 不存在变量提升

var存在变量提升

let 和 const区别

共同点:

  1. 都有块级作用域
  2. let和const都不存在变量提升,一定是先定义,后使用
  3. 都有暂时性死区(TDZ) 在没有定义之前,不能使用
  4. 都不允许重复声明 let a = 1; let a = 2;

不同点:

  1. const 只是常量, 赋予const 类型是基本类型,就不能再改变值
  2. const 对于基本类型来说, 只能赋值一次,所以,第一次初始化,就要赋予一个值; 引用类型,就可以反复修改内容
const a = 1;
const a = 2; // 报错  Identifier 'a' has already been declared

告诉你 a 已经被定义

const a;

a = 1  // Missing initializer in const declaration

没有初始化

const o = {}

o.name = '张三'
o.age = 12

console.log(o)

这种是可以的, 因为 o 是一个对象, 是引用类型,变量名 o 不直接指向数据, 而是指向数据所在的一块 内存地址; 只要我们不要将 指向的地址修改掉, 就可以任意修改这个地址所指向的值

const o = {}

// 这里就是将引用地址修改了,就会报错
o = {}  // Assignment to constant variable.对常量变量赋值。