JavaScript 内存优化 GC 回收 与 var let 和 const 2/2

接续之前一篇 <JavaScript 内存优化 GC 回收 与 var let 和 const 1/2> ( https://ufqi.com/blog/javascript-gc-var-let-const/ ) , 继续谈 JavaScript 的程序优化内存占用方面的改进。之前说内存使用效率的提升大致有三个方向:
1. 变量声明的关键字的选择 var let 和 const 各个不同;
2. 注意 DOM 对象的引用和显式重置为空释放对象;
3. 注意 定时器 对象的清理,需要手工显式清理掉。

之前一篇已经说了 2, 3 ,这次来说第 1 点,也是最重要的一点,所以单开了一篇来梳理关于 JavaScript 变量声明关键词对程序运行和内存占用的影响。写作过程中这次使用的是 AI 助手 字节跳动 的 豆包AI .

JavaScript 中 constvarlet 这三个变量声明关键字的核心区别分别是它们的作用域、变量提升、可赋值性等这些特性,这是 JavaScript 基础中最关键的知识点之一,它将直接影响代码的可读性和稳定性。

一、核心区别总览

先通过表格快速掌握三者的核心差异,再逐个拆解细节:

特性varletconst
作用域函数作用域 / 全局作用域块级作用域块级作用域
变量提升提升且初始化 undefined提升但未初始化(暂时性死区)提升但未初始化(暂时性死区)
可重复声明✅ 允许❌ 不允许❌ 不允许
可重新赋值✅ 允许✅ 允许❌ 不允许(引用类型内容可改)
声明时必须赋初始值❌ 可选❌ 可选✅ 必须
全局作用域声明挂载到 window 对象不挂载到 window不挂载到 window

二、逐个拆解 + 代码示例

1. var:ES5 关键字,函数作用域(易踩坑)

var 是 ES5 ( ECMAScript 2009 ) 唯一的变量声明方式,核心问题是无块级作用域提升行为不直观,也是 ES6 ( ECMAScript 2015 ) 推出 let/const 的主要原因。

// 1. 函数作用域:if/for 等块无法限制 var 作用域
if (true) {
  var a = 10;
}
console.log(a); // 10(if 块外仍能访问,因为 var 是函数作用域)

// 2. 变量提升:声明提升到作用域顶部,且初始化为 undefined
console.log(b); // undefined(不会报错,提升后默认值)
var b = 20;

// 3. 可重复声明 + 可重新赋值
var c = 30;
var c = 40; // 重复声明不报错
c = 50;     // 重新赋值不报错
console.log(c); // 50

// 4. 全局声明挂载到 window
var d = 100;
console.log(window.d); // 100(全局 var 变量会成为 window 属性)

2. let:ES6 关键字,块级作用域(替代 var 的首选)

let 解决了 var 的作用域和提升坑,是声明 “可变变量” 的首选。

// 1. 块级作用域:if/for 等块可限制作用域
if (true) {
  let a = 10;
}
console.log(a); // ReferenceError: a is not defined(块外无法访问)

// 2. 暂时性死区(TDZ):提升但未初始化,声明前访问报错
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 20;

// 3. 不可重复声明,可重新赋值
let c = 30;
// let c = 40; // SyntaxError: Identifier 'c' has already been declared
c = 40; // 重新赋值正常
console.log(c); // 40

// 4. 全局声明不挂载到 window
let d = 100;
console.log(window.d); // undefined

3. const:ES6 关键字,块级作用域 + 只读(声明常量)

const 核心是声明 “只读引用”,不是 “不可变值”—— 基本类型(数字、字符串、布尔)不可改,引用类型(对象、数组)的内容可改,但不能重新赋值引用。

// 1. 声明时必须赋初始值
// const a; // SyntaxError: Missing initializer in const declaration
const a = 10;

// 2. 不可重新赋值(基本类型)
// a = 20; // TypeError: Assignment to constant variable.

// 3. 引用类型(对象/数组):内容可改,引用不可改
const obj = { name: "张三" };
obj.name = "李四"; // 正常(修改对象内容,引用未变)
console.log(obj); // { name: '李四' }

// obj = { age: 20 }; // 报错(重新赋值引用)

const arr = [1, 2, 3];
arr.push(4); // 正常(修改数组内容)
console.log(arr); // [1,2,3,4]

// 4. 块级作用域 + 暂时性死区(和 let 一致)
if (true) {
  const b = 20;
  console.log(b); // 20
}
// console.log(b); // ReferenceError
  1. 优先用 const:只要变量不需要重新赋值(90% 场景),就用 const,让代码语义更清晰,减少意外赋值。
  2. 其次用 let:只有变量需要重新赋值时(如循环变量、状态变量),才用 let
  3. 彻底抛弃 varvar 的作用域和提升问题容易导致隐蔽 bug,ES6 环境下完全可以用 let/const 替代。

三、最佳实践建议

总结

  1. var 是 ES5 关键字,函数作用域、可重复声明、提升后初始化 undefined,已不推荐使用;
  2. let 是块级作用域,不可重复声明、有暂时性死区,适合声明需要重新赋值的变量;
  3. const 是块级作用域,不可重复声明、声明时必须赋值、不可重新赋值(但引用类型内容可改),适合声明常量 / 只读引用。

记住核心原则:能用 const 就不用 let,能用 let/const 就不用 var,这是现代 JavaScript 开发的通用规范。

四、挂载变量到 window 全局

既然推荐优先使用 let 和 const ,那就立即采用,可是问题也来了。这两个关键词声明的变量默认都是块级作用域,不能曝露到 window 页面全局,如希望能够全局访问到某个 let 或者 const 声明的变量,该如何操作?

const/let 声明的变量默认是块级作用域,且不会自动成为 window 的属性(这也是它们和 var 的关键区别之一)。要实现全局访问,核心是手动给 window 全局对象添加属性,将 const 变量的值赋给这个属性;如果希望 window 上的 “全局常量” 也具备 const 的 “不可重新赋值” 特性,可通过 Object.defineProperty 限制属性的可写性。

具体实现方法

方法 1:基础挂载(简单快捷,window 属性可修改)

先声明 const 变量,再将其值赋给 window 的属性,即可实现全局访问。

// 1. 声明块级作用域的 const 变量
const API_BASE_URL = 'https://api.example.com';
const MAX_SIZE = 100;

// 2. 挂载到 window 全局对象
window.API_BASE_URL = API_BASE_URL;
window.MAX_SIZE = MAX_SIZE;

// 3. 任意作用域均可访问
function test() {
  console.log(window.API_BASE_URL); // 输出:https://api.example.com
  console.log(MAX_SIZE); // 若 test 函数所在作用域无 MAX_SIZE,直接访问会报错,需用 window.MAX_SIZE
}
test();

// 注意:window 上的属性默认可修改(和 const 原变量不同)
window.MAX_SIZE = 200;
console.log(window.MAX_SIZE); // 输出:200(可修改)
API_BASE_URL = 'xxx'; // 报错:const 原变量不可重新赋值

方法 2:严格挂载(模拟 const 特性,window 属性不可修改)

如果希望 window 上的全局 “常量” 也像 const 一样不可重新赋值、不可删除,可使用 Object.defineProperty 配置属性描述符,这是更符合 const 语义的做法。

// 定义全局常量并挂载到 window,且不可修改、不可删除
Object.defineProperty(window, 'PI', {
  value: 3.1415926, // 常量值
  writable: false,   // 核心:设置为 false,禁止重新赋值(模拟 const)
  enumerable: true,  // 可枚举(能被 for...in 遍历到)
  configurable: false // 不可配置(禁止删除属性、修改属性描述符)
});

// 全局访问
console.log(window.PI); // 输出:3.1415926
console.log(PI); // 全局作用域下可直接访问(无需 window.)

// 尝试修改/删除,验证 const 特性
window.PI = 3; // 无报错,但赋值无效(严格模式下会报错)
console.log(window.PI); // 仍输出:3.1415926

delete window.PI; // 无法删除
console.log(window.PI); // 仍输出:3.1415926

// 严格模式下修改会直接报错(推荐开启严格模式)
'use strict';
window.PI = 3; // 报错:TypeError: Cannot assign to read only property 'PI' of object '#<Window>'
  • 全局常量建议用大写字母 + 下划线命名(如 API_BASE_URL),区分普通变量,提高可读性。

总结

  1. 核心方案:通过 window.xxx = const变量Object.defineProperty 将 const 变量挂载到全局对象,实现跨作用域访问;
  2. 如需模拟 const 的 “不可重新赋值” 特性,必须用 Object.defineProperty 设置 writable: false
  3. const 变量的块级作用域特性不影响 window 属性的全局访问,全局访问时建议显式写 window.xxx(避免作用域冲突)。

这种方式既保留了 const 的语义(不可随意修改),又实现了变量的全局共享,是前端项目中管理全局常量的常用最佳实践。

经过前述三项重要修改,在 UfqiFina 有福金融 的项目具体实践中,我们观测到的单页面应用内存占用情况:
峰值 3000MB~5000MB ↓→ 500MB~1500MB 。
运行性能和内存优化大约有 2 倍 至 10 倍 的提升。

此条目发表在-Hanjst/-汉吉斯特, 编程技术, 计算机技术分类目录,贴了, , , , 标签。将固定链接加入收藏夹。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

Captcha Code