一文读懂var、let、const三者之间的区别

208 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

区别1:是否存在块级作用域

var 声明的变量不存在块级作用域,let 声明的变量 和 const 声明的常量存在块级作用域。

一个 {} 表示一个块级作用域。

// ----------------------------------- var --------------------------------
if(true) {
  var str = 'Oct13_JJP';
  console.log(str); // Oct13_JJP
}
console.log(str); // Oct13_JJP

// ----------------------------------- let --------------------------------
if(true) {
  let str = 'Oct13_JJP';
  console.log(str); // Oct13_JJP
}
console.log(str); // Uncaught ReferenceError: str is not defined

// ----------------------------------- const --------------------------------
if(true) {
  const str = 'Oct13_JJP';
  console.log(str); // Oct13_JJP
}
console.log(str); // Uncaught ReferenceError: str is not defined

区别2:是否存在变量提升

var 声明的变量存在变量提升,let 和 const 不存在变量提升(变量只能在声明之后使用,否则会报错)。

变量提升:js 代码自上而下执行之前,浏览器首先会把带有 “var”、“function” 关键字的进行提前 “声明” 或 “定义”,这种预先处理的机制就是变量提升。

也就是说:变量可以先使用再声明。

// ----------------------------------- var --------------------------------
// JavaScript 只会提升声明,不会提升其初始化。如果一个变量先被使用再被声明和赋值的话,使用时的值是 undefined。
console.log(str); // undefined
var str = 'Oct13_JJP';

// 如果你先赋值、再使用、最后声明该变量,使用时能获取到所赋的值。
str = 'Oct13_JJP';
console.log(str); // Oct13_JJP
var str;

getName("Oct13_JJP"); // 我的名字叫:Oct13_JJP
function getName(name) {
  console.log("我的名字叫:" + name)
}

// ----------------------------------- let --------------------------------
console.log(str); // Uncaught ReferenceError: Cannot access 'str' before initialization
let str = 'Oct13_JJP';

// ----------------------------------- const --------------------------------
console.log(str); // Uncaught ReferenceError: Cannot access 'str' before initialization
const str = 'Oct13_JJP';

区别3:是否存在暂时性死区

var 声明的变量不存在暂时性死区,let 声明的变量 和 const 声明的常量存在暂时性死区。

ES6 对暂时性死区的解释为:当程序的控制流程在新的作用域(module function 或 block 作用域)进行实例化时,在此作用域中用 let / const 声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,所以是不能被访问的,如果访问就会抛出错误。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时性死区。

简单的说,在 let 和 const 声明变量和常量之前访问对应的变量与常量,会抛出 ReferenceError 错误;但在 var 声明变量之前就访问对应的变量,则会得到 undefined。

// ----------------------------------- var --------------------------------
(function fn() {
  // 函数作用域开始
  console.log(str)  // undefined
  // 先声明
  var str 
  console.log(str)  // undefined
  // 再赋值
  str = "Oct13_JJP"
  console.log(str)  // Oct13_JJP
  // 函数作用域结束
})()
// 在函数作用域外访问
console.log(str)  // Uncaught ReferenceError: str is not defined

// ----------------------------------- let --------------------------------
{
  // 块级作用域开始
  console.log(str)  // Uncaught ReferenceError: Cannot access 'str' before initialization
  // 声明 和 赋值
  let str = "Oct13_JJP"
  console.log(str) // Oct13_JJP
}
// 在块级作用域外访问
console.log(str) // Uncaught ReferenceError: str is not defined

// ----------------------------------- const --------------------------------
{
  // 块级作用域开始
  console.log(str)  // Uncaught ReferenceError: Cannot access 'str' before initialization
  // 声明 和 赋值
  const str = "Oct13_JJP"
  console.log(str) // Oct13_JJP
}
// 在块级作用域外访问
console.log(str) // Uncaught ReferenceError: str is not defined

区别4:是否可以重复声明变量

var 可以重复声明变量,后声明的同名变量会覆盖之前声明的变量,let 和 const 不允许重复声明变量。

// ----------------------------------- var --------------------------------
var name = "Oct13_JJP";
var name = "JJP";
console.log(name); // JJP

// ----------------------------------- let --------------------------------
let name = "Oct13_JJP";
let name = "JJP"; // Uncaught SyntaxError: Identifier 'name' has already been declared(报错:标识符 'name' Y已经被声明过了)
  
// ----------------------------------- const --------------------------------
const name = "Oct13_JJP";
const name = "JJP"; // Uncaught SyntaxError: Identifier 'name' has already been declared(报错:标识符 'name' Y已经被声明过了)

区别5:是否必须设置初始值

var 和 let 声明的变量可以不用设置初始值;const 声明的常量必须设置初始值,并且定义的值不可以修改,修改 const 声明的常量会导致运行时报错。

// ----------------------------------- var --------------------------------
var str;
console.log(str); // undefined

// ----------------------------------- let --------------------------------
let str;
console.log(str); // undefined

// ----------------------------------- const --------------------------------
const PI; // 报错

区别6:是否可以重新赋值

var 和 let 声明的变量可以重新赋值,const 声明的常量不允许重新赋值。

这里 const 声明的常量不允许重新赋值,指的是:如果 const 声明的常量数据类型是基本数据类型,此时就不能重新赋值,但如果 const 声明的常量数据类型是引用数据类型,此时是可以改变值的。

// ----------------------------------- var --------------------------------
var str = "Oct13_JJP";
console.log(str); // Oct13_JJP
str = "abc";
console.log(str); // abc
  
// ----------------------------------- let --------------------------------
let str = "Oct13_JJP";
console.log(str); // Oct13_JJP
str = "abc";
console.log(str); // abc
  
// ----------------------------------- const --------------------------------
// const 声明的 str 是基本数据类型 String,重新赋值会报错 
const str = "Oct13_JJP";
console.log(str); // Oct13_JJP
str = "abc";
console.log(str); // Uncaught TypeError: Assignment to constant variable

// const 声明的 str 是引用数据类型 Object,可以修改值,但不能重新赋值
const p = {
  name: "Oct13_JJP"
}
console.log(p.name); // Oct13_JJP
p.name = "JJP"
console.log(p.name); // JJP

区别7:是否添加全局属性

var 声明的变量为全局变量,并且会将该变量挂载为全局对象的属性,可以通过 window 访问;let 和 const 声明的变量和常量不会添加全局属性。

// ----------------------------------- var --------------------------------
var name = "Oct13_JJP";
console.log(window.name); // Oct13_JJP

// ----------------------------------- let --------------------------------
let age = 24;
console.log(window.age); // undefined

// ----------------------------------- const --------------------------------
const sex = "男";
console.log(window.sex); // undefined

这里整理出一个表格,方便大家记忆!

区别varletconst
是否存在块级作用域×
是否存在变量提升××
是否存在暂时性死区×
是否可以重复声明变量××
是否必须设置初始值××
是否可以重新赋值×
是否添加全局属性××