【JavaScript-Day 4】`var` 完全指南:掌握变量声明、作用域及提升

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

01-【JavaScript-Day 1】从零开始:全面了解 JavaScript 是什么、为什么学以及它与 Java 的区别
02-【JavaScript-Day 2】开启 JS 之旅:从浏览器控制台到 <script> 标签的 Hello World 实践
03-【JavaScript-Day 3】掌握JS语法规则:语句、分号、注释与大小写敏感详解
04-【JavaScript-Day 4】var 完全指南:掌握变量声明、作用域及提升



前言

在上一篇文章中,我们了解了 JavaScript 的基本语法规则,包括语句、分号和注释。今天,我们将深入探讨编程中最核心的概念之一:变量(Variables)。想象一下,程序需要处理各种各样的数据,比如用户的名字、商品的价格、计算的结果等等。变量就像是我们在程序中创建的“容器”或“标签”,用来存储和引用这些数据。本篇将聚焦于 JavaScript 早期也是最经典的变量声明方式——var关键字,带你理解如何使用它来创建、命名以及它的一些重要特性,为后续学习更现代的 letconst 打下坚实基础。

一、初识变量:数据的“代号”

1.1 变量的定义与作用

(1) 什么是变量?

在编程中,变量可以理解为一个用于存储数据值的命名占位符。你可以把它想象成一个贴有标签的盒子:

  • 盒子:是计算机内存中的一块空间,用来存放数据。
  • 标签:就是我们给这块空间起的名字,即变量名
  • 盒子里放的东西:就是我们要存储的数据值,即变量值

通过这个“标签”(变量名),我们就可以方便地找到、读取或修改“盒子”里的数据(变量值)。

(2) 变量的作用

变量的主要作用在于:

  1. 存储数据:将程序运行过程中需要用到的数据临时或长期保存起来。
  2. 引用数据:通过变量名方便地访问和操作存储的数据。
  3. 提高可读性与可维护性:使用有意义的变量名可以使代码更易理解和修改。例如,用 userName 存储用户名比直接使用一串字符要清晰得多。
  4. 复用数据:同一个数据可以在程序的不同地方通过变量名被多次使用,避免重复书写。

1.2 为什么需要变量?

假设我们要计算一个圆的周长和面积,半径是 5。

  • 没有变量:

    • 周长 = 2 ∗ 3.14159 ∗ 5 2 * 3.14159 * 5 23.141595
    • 面积 = 3.14159 ∗ 5 ∗ 5 3.14159 * 5 * 5 3.1415955
    • 如果半径变成了 10,你需要修改所有用到半径 5 的地方。
  • 使用变量:

    var radius = 5; // 声明一个变量 radius 并赋值为 5
    var pi = 3.14159; // 声明一个变量 pi
    var circumference = 2 * pi * radius; // 使用变量计算周长
    var area = pi * radius * radius; // 使用变量计算面积
    
    console.log("半径为 " + radius + " 的圆:");
    console.log("周长是: " + circumference);
    console.log("面积是: " + area);
    
    // 如果半径改变,只需要修改一处
    radius = 10;
    circumference = 2 * pi * radius;
    area = pi * radius * radius;
    console.log("半径更新为 " + radius + " 的圆:");
    console.log("周长是: " + circumference);
    console.log("面积是: " + area);
    

通过使用变量 radiuspi,代码不仅更清晰,而且当数据需要改变时,我们只需修改变量赋值的地方,大大提高了代码的灵活性和可维护性。

二、使用 var 声明变量

在 ES6(ECMAScript 2015)出现之前,var 是 JavaScript 中声明变量的唯一方式。虽然现在推荐使用 letconst,但理解 var 对于阅读旧代码和深入理解 JS 特性仍然非常重要。

2.1 基本语法

使用 var 关键字声明变量的基本语法有两种形式:

  1. 先声明,后赋值:

    var variableName; // 声明一个变量,此时它的默认值是 undefined
    variableName = value; // 给变量赋值
    
  2. 声明的同时进行初始化赋值:

    var variableName = value; // 声明变量并直接赋予初始值
    

这里的 variableName 是你为变量起的名字,value 是要存储的数据。

2.2 声明与初始化

(1) 先声明后赋值
var message; // 声明一个名为 message 的变量
console.log(message); // 输出:undefined (因为尚未赋值)

message = "Hello, CSDN!"; // 给 message 变量赋值
console.log(message); // 输出:Hello, CSDN!
(2) 声明时同时赋值

这是更常见的做法,可以在声明变量时就给它一个明确的初始值。

var userName = "TechMaster"; // 声明 userName 并赋值
var userAge = 25; // 声明 userAge 并赋值
var isVIP = true; // 声明 isVIP 并赋值

console.log(userName); // 输出:TechMaster
console.log(userAge); // 输出:25
console.log(isVIP); // 输出:true
(3) 一次声明多个变量

可以使用一个 var 关键字,通过逗号分隔来同时声明多个变量。

var name = "Alice", age = 30, city = "Beijing";

console.log(name); // 输出:Alice
console.log(age); // 输出:30
console.log(city); // 输出:Beijing

// 也可以混合声明和初始化
var x, y = 10, z;
console.log(x); // 输出:undefined
console.log(y); // 输出:10
console.log(z); // 输出:undefined
(4) 重复声明

使用 var 声明的变量,可以在同一个作用域内被重复声明,后面的声明会覆盖前面的声明(如果赋值了的话),或者仅仅是声明而不起实际作用(如果未赋值)。

var greeting = "Hello";
console.log(greeting); // 输出:Hello

var greeting = "Hi"; // 重复声明并赋值
console.log(greeting); // 输出:Hi

var counter = 100;
var counter; // 重复声明,但未赋值,counter 的值不变
console.log(counter); // 输出:100

注意var 允许重复声明是其一个特点,但在现代 JavaScript 开发中,这通常被认为是一个缺点,因为它可能意外地覆盖已有变量,导致难以追踪的 bug。这也是 ES6 引入 letconst 的原因之一(它们不允许在同一作用域重复声明)。

2.3 变量的命名规范与建议

给变量起一个好名字至关重要,它能极大提高代码的可读性和可维护性。

(1) 强制规则
  • 变量名必须以字母(a-z, A-Z)、**下划线(_美元符号($)**开头。
  • 变量名的其余部分可以包含字母、数字(0-9)、下划线或美元符号
  • 区分大小写myVariablemyvariable 是两个不同的变量。
  • 不能使用 JavaScript 的关键字(如 var, if, else, for, function 等)和保留字(如 enum, implements, package 等)作为变量名。
// 合法的变量名
var name;
var _age;
var $price;
var user1_profile;
var currentStatus;

// 非法的变量名
// var 1name;   // 不能以数字开头
// var my-name; // 不能包含连字符
// var var;     // 不能使用关键字
(2) 推荐约定(驼峰命名法)

在 JavaScript 社区,最广泛接受的变量命名约定是小驼峰命名法(Lower Camel Case)

  • 第一个单词全小写。
  • 从第二个单词开始,每个单词的首字母大写。
var firstName = "Guang"; // 推荐
var lastName = "Dong"; // 推荐
var userLoginCount = 10; // 推荐
var isUserLoggedIn = true; // 推荐

var firstname; // 合法,但不推荐(全小写不易区分单词)
var UserLoginCount; // 合法(大驼峰通常用于类名),但不推荐用于变量
(3) 命名建议
  • 见名知意:变量名应清晰地反映其存储数据的含义。
    • userName 而不是 unx 来存储用户名。
    • shoppingCartTotal 而不是 scttotal 来存储购物车总价。
  • 避免使用模糊不清的缩写
  • 保持一致性:在整个项目中遵循相同的命名风格。

三、var 的“个性”:作用域与提升

var 除了声明变量的基本功能外,还有两个非常重要的特性:函数作用域(Function Scope)变量提升(Hoisting)。理解这两点是掌握 var 的关键,也是理解为什么后来需要 letconst 的重要原因。

3.1 函数作用域(Function Scope)

(1) 什么是函数作用域?

作用域(Scope)是指变量和函数在程序中可访问的范围。使用 var 声明的变量,其作用域是函数作用域。这意味着:

  • 函数内部使用 var 声明的变量,只在该函数内部及其嵌套函数中可访问(局部变量)。
  • 函数外部(全局作用域)使用 var 声明的变量,在整个程序中都可访问(全局变量)。
(2) 全局变量与局部变量
var globalVar = "I am global"; // 全局变量

function myFunction() {
  var localVar = "I am local"; // 局部变量
  console.log(globalVar); // 可以访问全局变量
  console.log(localVar); // 可以访问局部变量

  function nestedFunction() {
    var nestedVar = "I am nested";
    console.log(globalVar); // 可以访问全局变量
    console.log(localVar); // 可以访问外部函数的局部变量
    console.log(nestedVar); // 可以访问自己的局部变量
  }
  nestedFunction();
  // console.log(nestedVar); // 错误!无法访问嵌套函数的变量
}

myFunction();

console.log(globalVar); // 可以访问全局变量
// console.log(localVar); // 错误!无法在函数外部访问局部变量
(3) 块级作用域的“陷阱”

与其他很多编程语言(如 C++, Java)不同,var 不具有块级作用域。所谓的“块”通常指由花括号 {} 包裹的代码段,例如 if 语句、for 循环、while 循环等。

这意味着,即使在 iffor 循环内部使用 var 声明变量,该变量实际上也会成为包含它的函数的作用域的一部分(或者成为全局变量,如果它不在任何函数内)。

示例 1:if 语句块

function testScope(condition) {
  if (condition) {
    var message = "Inside if"; // 这个 message 实际上属于 testScope 函数作用域
    console.log(message);
  } else {
    console.log("Inside else");
    // console.log(message); // 如果 condition 为 false,这里访问 message 会得到 undefined (因为 hoisting,后面会讲)
  }
  console.log(message); // 在 if 块外部仍然可以访问 message (如果 if 被执行过)
                       // 如果 condition 为 false,这里访问 message 也会得到 undefined
}

testScope(true);
// 输出:
// Inside if
// Inside if

testScope(false);
// 输出:
// Inside else
// undefined

示例 2:for 循环块

function loopScope() {
  for (var i = 0; i < 3; i++) {
    console.log("Inside loop:", i); // 输出 0, 1, 2
  }
  console.log("Outside loop:", i); // 输出 3 (循环结束后 i 的值是 3,并且在函数作用域内可访问)
}

loopScope();

// console.log(i); // 错误!i 是 loopScope 函数的局部变量,外部无法访问

这个特性(缺少块级作用域)经常导致意想不到的行为,特别是在循环和异步编程中。

3.2 变量提升(Hoisting)

(1) 什么是变量提升?

变量提升是 JavaScript 中一个独特的机制。当 JavaScript 引擎解释代码时,它会将所有使用 var 声明的变量的“声明”部分,“提升”到其所在作用域(函数作用域或全局作用域)的顶部,而赋值操作则留在原地

可以理解为,代码在执行前会经过一个预处理阶段,将类似 var myVar = 10; 的代码看作两部分:

  1. var myVar; (声明) -> 这部分被提升到作用域顶部
  2. myVar = 10; (赋值) -> 这部分留在原来的位置
(2) 提升的表现

正因为变量声明被提升了,我们可以在声明语句之前访问 var 声明的变量,但得到的值是 undefined,因为此时只有声明被提升了,赋值操作还未执行。

console.log(myVar); // 输出:undefined (因为声明被提升了,但赋值还没执行)

var myVar = "Hello"; // 声明并赋值

console.log(myVar); // 输出:Hello

如果一个变量未经声明就直接使用(非赋值操作),则会抛出 ReferenceErrorundefinedReferenceError 是不同的。

// console.log(anotherVar); // 直接报错:ReferenceError: anotherVar is not defined
(3) 提升带来的潜在问题

变量提升虽然是 JS 的一个特性,但它可能会导致代码逻辑不清晰,尤其对于初学者而言。在代码块的中间声明变量,其行为可能与直觉不符。

var x = 1;

function testHoisting() {
  console.log(x); // 输出:undefined (函数内部的 x 声明被提升,覆盖了全局的 x)
  var x = 2; // 声明并赋值函数内部的 x
  console.log(x); // 输出:2
}

testHoisting();
console.log(x); // 输出:1 (全局的 x 未受影响)

在这个例子中,函数内部的 console.log(x) 输出 undefined 而不是全局的 1,就是因为函数内部的 var x 声明被提升到了函数顶部,此时它尚未被赋值为 2

(4) Mermaid 图示(概念演示)

我们可以用 Mermaid 流程图来形象地理解代码的“原始顺序”和“提升后的等效顺序”。

原始代码:

function example() {
  console.log(a);
  var a = 10;
  console.log(a);
}
example();

提升后的等效逻辑:

graph TD
    subgraph example function scope
        direction TB
        A[var a;] -- 声明提升到顶部 --> B(console.log(a));
        B -- 执行时 a 为 undefined --> C(a = 10;);
        C -- 赋值操作留在原地 --> D(console.log(a));
        D -- 执行时 a 为 10 --> E((End));
    end

这个图示说明了声明 var a; 被无形中移到了函数体的最开始,所以在第一个 console.log(a) 执行时,a 已经被声明了(值为 undefined),而赋值 a = 10; 在之后才执行。

四、var 的应用场景与注意事项

4.1 历史背景与现代视角

var 是 JavaScript 最初就有的变量声明方式,在 ES6(2015年)发布之前是唯一的选择。然而,由于其函数作用域和变量提升的特性,有时会带来一些维护上的困难和潜在的错误。

随着 ES6 的普及,letconst 被引入,它们提供了块级作用域,并且 letconst 声明的变量虽然也有提升(存在暂时性死区 TDZ),但行为更符合预期(在声明前访问会报错),因此在现代 JavaScript 开发中,推荐优先使用 let(用于可能改变的变量)和 const(用于不应改变的常量)来代替 var

4.2 何时仍可能见到 var

  1. 维护旧项目:你可能会在一些年代较早的或者没有进行现代化升级的 JavaScript 代码库中看到大量 var 的使用。
  2. 特定需求(罕见):在极少数情况下,开发者可能有意利用 var 的函数作用域或提升特性,但这通常不被推荐,因为代码可读性会降低。

4.3 使用 var 的主要“坑点”回顾

  1. 重复声明不报错:容易在不知情的情况下覆盖变量。
  2. 只有函数作用域:在 if, for 等块内的 var 变量会“泄漏”到外部函数作用域。
  3. 变量提升:在声明前访问变量不会报错,而是得到 undefined,可能隐藏逻辑错误。

理解这些“坑点”有助于我们在阅读或调试包含 var 的代码时更加小心,并促使我们在新代码中拥抱 letconst

五、总结

本篇文章我们深入探讨了 JavaScript 中使用 var 关键字声明变量的相关知识,主要涵盖了以下核心内容:

  1. 变量的基本概念:变量是存储数据的命名容器,通过变量名可以方便地存取数据。
  2. var 的声明语法:学习了如何使用 var 关键字进行变量的声明、初始化以及一次性声明多个变量。
  3. 变量命名规范:掌握了强制性的命名规则(开头、包含字符、大小写敏感、避开关键字)和推荐的驼峰命名法约定。
  4. var 的函数作用域:理解了 var 声明的变量只存在于函数作用域或全局作用域中,不具备块级作用域。
  5. var 的变量提升(Hoisting):了解了 var 声明会被提升到其作用域顶部,而赋值操作留在原地的机制及其表现(声明前访问为 undefined)。
  6. var 的局限性与现代实践:认识到 var 的一些特性(如重复声明、非块级作用域、提升行为)可能导致的问题,以及为何现代开发更推荐使用 letconst

虽然 var 在现代 JavaScript 中已不推荐作为首选,但掌握它的特性对于理解 JavaScript 的演进、阅读遗留代码以及深化对作用域和变量生命周期的理解至关重要。在下一篇文章中,我们将学习 ES6 带来的更强大的变量声明方式:letconst。敬请期待!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴师兄大模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值