var_let_const见解,新旧交替var已被淘汰

64 阅读6分钟

JavaScript 中的变量声明:varletconst 详解

在 JavaScript 中,变量的声明方式直接影响其作用域、提升行为以及可变性。随着 ES6(ECMAScript 2015)的引入,JavaScript 引入了 letconst 关键字,使得变量声明更加安全和符合直觉。本文将深入探讨 varletconst 的区别,并解释常见的错误类型。


1. 使用 var 声明变量

早期的JS 就用来页面交互,有一些缺失甚至设计不合理的地方

语言精粹,The Good Parts,The Bad Partsa

es5 只有var 申明变量,没有常量

基本语法

var age = 18; // js弱类型,由值决定

特性与问题

变量提升(Hoisting)

var 声明的变量具有“变量提升”特性。这意味着在编译阶段,变量的声明会被提升到其作用域的顶部,但赋值不会。

console.log(a); // 输出: undefined
var a = 1;

虽然代码看起来是先访问 a 再赋值,但由于变量提升,实际执行顺序如下:

var a;           // 声明被提升
console.log(a);  // 此时 a 是 undefined
a = 1;           // 赋值在执行阶段进行
作用域问题

var 声明的变量是函数作用域(function-scoped),而不是块级作用域。这意味着在 iffor 等块中声明的 var 变量,在块外仍然可以访问。

if (true) {
  var x = 10;
}
console.log(x); // 输出: 10

这种行为常常与开发者的直觉不符,容易导致意外的变量污染。


2. 使用 let 声明变量

let 是 ES6 引入的新关键字,用于声明块级作用域的变量。

基本语法

let b = 2;

特性

块级作用域

let 声明的变量只在当前块 {} 内有效。而var的变量全局引用并不严谨

{ // 块级作用域
    var age = 18;//不支持块级作用域
    let height = 188;//支持块级作用域 是大型项目或高级语言的核心
}

console.log(age);
console.log(height);
无变量提升,但有暂时性死区(Temporal Dead Zone, TDZ)

let 变量不会被提升到作用域顶部,如果在声明之前访问,会抛出错误。

console.log(z); // ReferenceError: Cannot access 'z' before initialization
let z = 3;

这个错误提示说明变量 z 处于“暂时性死区”——从块的开始到变量被正式声明之间的区域,访问该变量会报错。


3. 使用 const 声明常量

const 用于声明一个常量,其值在声明后不能被重新赋值。

es5早期常量

var PI = 3.1415926;//变量大写 约定就不应该改变 编程习惯

es6基本语法

const PI = 3.14159;

特性

  • const 也具有块级作用域和暂时性死区。
  • 声明时必须初始化,且不能重新赋值。
const PI = 3.14;
PI = 3.15; // TypeError: Assignment to constant variable.

注意: 简单数据类型的常量不能改变的; 复杂数据类型的常量,不能改变引用的地址,但是可以改变引用地址中的属性值

const obj = { name: "Alice" };
obj.name = "Bob"; // 合法
obj = {}; // TypeError: Assignment to constant variable.

如果对象一定不能变呢?

const wes = Object.freeze(person);// 冻结对象

使用冻结对象那么对象属性值也无法改变


JavaScript 中的函数提升与作用域详解

在 JavaScript 中,函数被视为“一等公民”(first-class citizens),这意味着函数不仅可以被调用,还可以作为参数传递、赋值给变量,甚至作为返回值。与此同时,JavaScript 的函数提升(Function Hoisting)机制也使其在执行前就具备了可访问性。

函数提升 vs 变量提升

在编译阶段,JavaScript 会进行“提升”(Hoisting)操作,将 var 声明的变量和函数声明提升到其作用域顶部。但两者的行为有重要区别:

  • var 提升:仅提升变量声明,不提升赋值。因此在声明前访问会得到 undefined
  • 函数提升:不仅提升函数声明,还会将整个函数定义一起提升,因此可以在声明之前安全调用。

示例对比

// 函数声明:可以提前调用(完全提升)
setWidth(); // 输出: 100

function setWidth() {
    var width = 100;
    console.log(width);
}

上述代码可以正常运行,因为函数声明 setWidth 在编译阶段就被完整提升。

然而,如果使用函数表达式并赋值给 letconst 变量,则行为完全不同:

setWidth(); // ❌ 报错:Cannot access 'setWidth' before initialization

let setWidth = function() {
    var width = 100;
    console.log(width);
};

错误分析

上面代码会抛出:

ReferenceError: Cannot access 'setWidth' before initialization

这是因为:

  1. let 声明不会像 var 那样简单提升为 undefined
  2. 它进入了“暂时性死区”(Temporal Dead Zone, TDZ),在正式声明前无法访问。
  3. 虽然变量名 setWidth 已被绑定到当前作用域,但在赋值完成前不可使用。

作用域解析

我们来详细分析上述代码中的作用域层级:

let setWidth = function() {
    // 函数作用域(局部作用域)
    var width = 100;

    // 块级作用域示例
    // {
    //     let a = 1;
    // }
    // console.log(a); // ReferenceError: a is not defined

    console.log(width); // ✅ 输出: 100
};

// console.log(width); // ❌ ReferenceError: width is not defined

作用域说明:

  • width 是使用 var 在函数内部声明的,因此它属于函数作用域,只能在 setWidth 函数内访问。
  • 外部全局作用域中无法访问 width,尝试访问会抛出 ReferenceError: width is not defined
  • 如果启用块级作用域(如 let a = 1;),其变量 a 仅在 {} 内有效,外部也无法访问。

常见错误类型解析

1. ReferenceError: a is not defined

当你试图访问一个未声明的变量时,JavaScript 会抛出此错误。

console.log(nonExistentVar); // ReferenceError: nonExistentVar is not defined

原因:变量未在任何作用域中声明。


2. TypeError: Assignment to constant variable.

当你尝试给 const 声明的变量重新赋值时,会触发此错误。

const age = 25;
age = 30; // TypeError: Assignment to constant variable.

解决方法:使用 let 声明可变变量,或避免重新赋值。


3. ReferenceError: Cannot access 'PI' before initialization

这是 letconst 特有的错误,发生在你试图在声明之前访问变量。

console.log(PI); // ReferenceError: Cannot access 'PI' before initialization
const PI = 3.14;

原因:变量处于“暂时性死区”。虽然 JavaScript 知道这个变量存在,但在它被初始化之前不能访问。


总结与最佳实践

关键字作用域提升可重新赋值初始化要求
var函数作用域
let块级作用域否(有 TDZ)
const块级作用域否(有 TDZ)

推荐做法:

  • 优先使用 const:如果你不打算改变变量的值,使用 const
  • 其次使用 let:当变量需要重新赋值时使用。
  • 避免使用 var:由于其变量提升和函数作用域带来的不可预测性,现代 JavaScript 开发中应尽量避免。

通过合理使用 letconst,可以写出更清晰、更安全的代码,避免常见的作用域和提升陷阱。