)
教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载JavaScript 提供了三种变量声明语句var、let与const。其中let和const是ES6才引入的而var自语言诞生之初便存在三者看似只是声明变量的不同写法实则作用域、提升hoisting、全局对象绑定与重复声明规则上差异显著直接决定了代码的可读性与可维护性。本篇以 30-seconds-of-code 仓库中的 var-let-const 文档为骨架逐一拆解三者的底层行为差异并结合仓库内 variable-scope、variable-hoisting 与 use-strict 等配套文章深化原理讲解读完你将能准确判断任意场景下该用哪种声明方式也能从容应对面试中围绕此话题的常见追问。一、三种声明语句概览JavaScript 的变量声明体系可以概括为一句话const声明常量不可重新赋值的值var与let声明变量。但这只是最表层的区别var在作用域、提升、全局对象属性、重复声明四个维度上都与let、const存在本质差异。[!NOTE]截至本文修订时间2024 年 5 月var的辉煌时代早已过去。如今使用var通常被视为一种代码坏味道code smell应当尽量避免。如果你在维护一个使用var的遗留项目建议考虑将其重构为let或const。在 30-seconds-of-code 仓库中与var、let、const直接相关的三篇文章被统一收录在 JS 面试题集合content/collections/js/interviews.yaml中与equality、this、closures等话题并列足以说明这一知识点是 JavaScript 基础面试的高频考点。该集合的description也指出这些文章旨在用简洁的答案与简单的示例覆盖常见面试问题因此下文每个小节都附带了可直接运行的最小示例代码。二、作用域函数作用域 vs 块级作用域作用域指的是一个或多个实体例如变量对代码特定部分的可见性。JavaScript 的作用域分为全局作用域与局部作用域其中局部作用域又可细分为块级作用域、函数作用域等更具体的类型。var与let/const最核心的差异就在于此使用var声明的变量是函数作用域function scoped的使用let或const声明的变量是块级作用域block scoped的。块级作用域指的是由{}包裹的代码区域如if、for、while语句体或独立的代码块。下面这个来自原文档的示例可以直观地展示两者的差异const scopeExample () { var a var; let b let; console.log(a, b); // var, let { var c var; let d let; console.log(c, d); // var, let } console.log(c); // var console.log(d); // Throws a ReferenceError };代码块内部声明的cvar在离开代码块后依然可以访问因为它绑定到了外层函数scopeExample的作用域上而dlet在块结束后便不可见访问它会抛出ReferenceError。从 variable-scope 一文可以进一步归纳三种声明在不同作用域下的表现全局作用域在函数或代码块之外声明的变量属于全局作用域代码中任何位置都能访问。此时var、let、const在作用域层面行为一致但顶层var会额外绑定到全局对象详见下文第四节。局部作用域块级在代码块内声明的变量只能从该代码块及其嵌套的局部作用域中访问。var在外部作用域中存在但值为undefined而let、const在外部作用域中完全不存在。局部作用域函数级在函数内声明的变量无论用哪种关键字外部作用域都不可访问。variable-scope 中还提供了一张 作用域可视化示意图直观呈现了全局作用域、函数作用域与块级作用域之间的嵌套关系非常适合用来建立直觉。三、提升Hoisting从undefined到ReferenceError在 JavaScript 代码执行之前引擎会先对代码进行解析与编译即时编译JIT。在编译阶段变量与函数声明会被放入内存这一过程被称为提升hoisting。需要特别注意的是只有声明被提升初始化不会被提升。原文档给出了三种声明在提升行为上的关键对比const hoistingExample () { console.log(a); // undefined var a var; console.log(a); // var console.log(b); // ReferenceError let b let; console.log(b); // let };var声明被提升到其所在作用域的顶部但在初始化执行之前值为undefined。也就是说访问提前并不会报错而是拿到undefined——这是一种静默失败。let与const声明同样会被提升引擎知道它们存在但在其定义被求值之前处于未初始化状态。这个阶段访问它们会抛出ReferenceError不会静默返回undefined。这段声明之后、初始化之前的不可访问区间就是常说的暂时性死区Temporal Dead Zone, TDZ。variable-hoisting 一文对提升的各种场景做了更完整的分类函数声明是唯一完全提升的特例——在定义之前调用也能正常工作hello(); // logs Hello world! function hello() { console.log(Hello world!); }var变量提升后值未初始化且var声明的函数表达式同样只会得到undefinedconsole.log(x); // logs undefined f(); // throws Uncaught TypeError: f is not a function var x 1; var f () Hi!; console.log(x); // logs 1 f(); // returns Hi!const与let提升后不初始化为undefined而是直接抛出明确的错误class声明也是如此console.log(y); // throws Uncaught ReferenceError: Cannot access y before initialization g(); // throws Uncaught ReferenceError: Cannot access g before initialization let y 2; const g () Hey!;综合来看const与let在先使用后声明的场景下会给出清晰、易于调试的错误信息而var只会静默地返回undefined为后续的诡异 bug 埋下伏笔——这是实践中优先使用let/const的重要原因。四、全局对象属性顶层var的特殊绑定在顶层top level声明变量时三种关键字的差异会进一步外溢到全局对象上使用var声明的变量会为全局对象浏览器中是window创建一个属性而let、const不会。var a var; let b let; console.log(window.a); // var console.log(window.b); // undefined这意味着顶层var声明的变量会被挂载到全局对象上从而可以被window.a这类方式访问。let声明的b虽然也属于全局作用域但并不会成为window的属性。从代码卫生的角度看var这种隐式的全局对象绑定容易造成命名污染——例如无意中覆盖浏览器内置的全局属性这也是规避var的又一理由。五、重复声明var宽容let/const严格在严格模式strict mode下var允许在同一个作用域内重复声明同名变量而let、const在同一作用域内重复声明是不允许的会直接抛出语法错误use strict; var a var1; var a var2; let b let1; let b let2; // SyntaxError重复声明看似只是语法约束的不同背后反映的是设计哲学的差异var的历史包袱使其保持了宽松的重复声明能力这在大型脚本中极易引发无意的变量覆盖let/const则通过强制唯一性把这类问题在编译阶段就拦截下来。上述示例中的use strict指令本身也值得展开。严格模式可以通过在脚本或函数最前面添加use strict来启用它会强制执行更严格的解析与错误处理规则。从 use-strict 一文可以了解到除了禁止重复声明相关场景外严格模式还会禁止意外的全局变量对未声明变量的赋值会抛出ReferenceError而不是静默地创建全局变量消除静默错误将原本会被静默忽略的赋值失败、删除不可删除属性、函数参数重名、0 前缀八进制字面量、给原始值设置属性等行为全部转为显式错误简化eval防止eval在周边作用域中引入新变量简化argumentsarguments不再与形参别名化arguments.callee、arguments.caller不再受支持禁止this装箱未指定this时函数内的this为undefined而非全局对象其他收紧禁止with语句、implements/interface/let/static等一批保留关键字、限制函数语句的位置等。尽管在重复声明这一小节中var与let/const的差异在严格模式下才完整显现部分浏览器在非严格模式下行为略有差异但统一启用严格模式并配合let/const是写出可预测、可维护代码的稳妥组合。六、实践建议如何选择声明方式综合上述四个维度的差异可以形成一套简单实用的选型准则默认使用const绝大多数情况下变量初始化后不需要重新赋值const既表达意图又防止意外覆盖确需重新赋值时使用let循环计数器、累加器、状态切换等场景避免使用var它带来的函数作用域、undefined静默失败、全局对象属性绑定与重复声明许可在现代 JavaScript 工程中几乎都是负面特性遗留代码中的var应逐步重构为let/const善用严格模式在文件或函数开头加上use strict让引擎替你拦截更多低级错误正如 variable-hoisting 一文的最佳实践清单所建议的那样始终先声明后使用无论使用哪种关键字都避免依赖提升特性提前访问变量variable-hoisting 中特别提醒可借助 ESLint 等工具强制这一规范。在面试场景中围绕var、let、const的追问通常都会落到本文的四个维度上作用域函数 vs 块级、提升undefinedvsReferenceError、全局对象绑定window属性 vs 无与重复声明允许 vsSyntaxError。把 var-let-const 中的四个示例代码亲自运行一遍再结合 variable-scope、variable-hoisting 与 use-strict 的扩展内容融会贯通即可对这一个话题建立起完整而扎实的认知体系。赞分享教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载相关推荐Keymap Editor性能优化与部署实践让编辑器运行更流畅的7个秘诀Keymap Editor性能优化与部署实践让编辑器运行更流畅的7个秘诀 Keymap Editor是一款基于Web的ZMK键盘映射图形编辑器通过直观的界面教程前端30 Seconds of Interviews 深度解析XHTML 与 HTML 的核心差异与实战判断30 Seconds of Interviews 深度解析XHTML 与 HTML 的核心差异与实战判断 XHTML 是将 HTML 4.01 以 XML 语教程前端javascript-questions实战var vs let作用域差异javascript questions实战var vs let作用域差异 你是否曾在JavaScript循环中遇到变量值异常的问题是否困惑为什么同样的代码前端教程文档上一篇30天无限续期JetBrains IDE试用重置终极指南下一篇JetBrains IDE试用期重置终极指南30天无限续杯的简单方法创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考