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关键字,带你理解如何使用它来创建、命名以及它的一些重要特性,为后续学习更现代的 let 和 const 打下坚实基础。
一、初识变量:数据的“代号”
1.1 变量的定义与作用
(1) 什么是变量?
在编程中,变量可以理解为一个用于存储数据值的命名占位符。你可以把它想象成一个贴有标签的盒子:
- 盒子:是计算机内存中的一块空间,用来存放数据。
- 标签:就是我们给这块空间起的名字,即变量名。
- 盒子里放的东西:就是我们要存储的数据值,即变量值。
通过这个“标签”(变量名),我们就可以方便地找到、读取或修改“盒子”里的数据(变量值)。
(2) 变量的作用
变量的主要作用在于:
- 存储数据:将程序运行过程中需要用到的数据临时或长期保存起来。
- 引用数据:通过变量名方便地访问和操作存储的数据。
- 提高可读性与可维护性:使用有意义的变量名可以使代码更易理解和修改。例如,用
userName存储用户名比直接使用一串字符要清晰得多。 - 复用数据:同一个数据可以在程序的不同地方通过变量名被多次使用,避免重复书写。
1.2 为什么需要变量?
假设我们要计算一个圆的周长和面积,半径是 5。
-
没有变量:
- 周长 = 2 ∗ 3.14159 ∗ 5 2 * 3.14159 * 5 2∗3.14159∗5
- 面积 = 3.14159 ∗ 5 ∗ 5 3.14159 * 5 * 5 3.14159∗5∗5
- 如果半径变成了 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);
通过使用变量 radius 和 pi,代码不仅更清晰,而且当数据需要改变时,我们只需修改变量赋值的地方,大大提高了代码的灵活性和可维护性。
二、使用 var 声明变量
在 ES6(ECMAScript 2015)出现之前,var 是 JavaScript 中声明变量的唯一方式。虽然现在推荐使用 let 和 const,但理解 var 对于阅读旧代码和深入理解 JS 特性仍然非常重要。
2.1 基本语法
使用 var 关键字声明变量的基本语法有两种形式:
-
先声明,后赋值:
var variableName; // 声明一个变量,此时它的默认值是 undefined variableName = value; // 给变量赋值 -
声明的同时进行初始化赋值:
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 引入 let 和 const 的原因之一(它们不允许在同一作用域重复声明)。
2.3 变量的命名规范与建议
给变量起一个好名字至关重要,它能极大提高代码的可读性和可维护性。
(1) 强制规则
- 变量名必须以字母(a-z, A-Z)、**下划线(
_)或美元符号($)**开头。 - 变量名的其余部分可以包含字母、数字(0-9)、下划线或美元符号。
- 区分大小写:
myVariable和myvariable是两个不同的变量。 - 不能使用 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而不是un或x来存储用户名。 - 用
shoppingCartTotal而不是sct或total来存储购物车总价。
- 用
- 避免使用模糊不清的缩写。
- 保持一致性:在整个项目中遵循相同的命名风格。
三、var 的“个性”:作用域与提升
var 除了声明变量的基本功能外,还有两个非常重要的特性:函数作用域(Function Scope)和变量提升(Hoisting)。理解这两点是掌握 var 的关键,也是理解为什么后来需要 let 和 const 的重要原因。
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 循环等。
这意味着,即使在 if 或 for 循环内部使用 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; 的代码看作两部分:
var myVar;(声明) -> 这部分被提升到作用域顶部myVar = 10;(赋值) -> 这部分留在原来的位置
(2) 提升的表现
正因为变量声明被提升了,我们可以在声明语句之前访问 var 声明的变量,但得到的值是 undefined,因为此时只有声明被提升了,赋值操作还未执行。
console.log(myVar); // 输出:undefined (因为声明被提升了,但赋值还没执行)
var myVar = "Hello"; // 声明并赋值
console.log(myVar); // 输出:Hello
如果一个变量未经声明就直接使用(非赋值操作),则会抛出 ReferenceError。undefined 和 ReferenceError 是不同的。
// 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 的普及,let 和 const 被引入,它们提供了块级作用域,并且 let 和 const 声明的变量虽然也有提升(存在暂时性死区 TDZ),但行为更符合预期(在声明前访问会报错),因此在现代 JavaScript 开发中,推荐优先使用 let(用于可能改变的变量)和 const(用于不应改变的常量)来代替 var。
4.2 何时仍可能见到 var?
- 维护旧项目:你可能会在一些年代较早的或者没有进行现代化升级的 JavaScript 代码库中看到大量
var的使用。 - 特定需求(罕见):在极少数情况下,开发者可能有意利用
var的函数作用域或提升特性,但这通常不被推荐,因为代码可读性会降低。
4.3 使用 var 的主要“坑点”回顾
- 重复声明不报错:容易在不知情的情况下覆盖变量。
- 只有函数作用域:在
if,for等块内的var变量会“泄漏”到外部函数作用域。 - 变量提升:在声明前访问变量不会报错,而是得到
undefined,可能隐藏逻辑错误。
理解这些“坑点”有助于我们在阅读或调试包含 var 的代码时更加小心,并促使我们在新代码中拥抱 let 和 const。
五、总结
本篇文章我们深入探讨了 JavaScript 中使用 var 关键字声明变量的相关知识,主要涵盖了以下核心内容:
- 变量的基本概念:变量是存储数据的命名容器,通过变量名可以方便地存取数据。
var的声明语法:学习了如何使用var关键字进行变量的声明、初始化以及一次性声明多个变量。- 变量命名规范:掌握了强制性的命名规则(开头、包含字符、大小写敏感、避开关键字)和推荐的驼峰命名法约定。
var的函数作用域:理解了var声明的变量只存在于函数作用域或全局作用域中,不具备块级作用域。var的变量提升(Hoisting):了解了var声明会被提升到其作用域顶部,而赋值操作留在原地的机制及其表现(声明前访问为undefined)。var的局限性与现代实践:认识到var的一些特性(如重复声明、非块级作用域、提升行为)可能导致的问题,以及为何现代开发更推荐使用let和const。
虽然 var 在现代 JavaScript 中已不推荐作为首选,但掌握它的特性对于理解 JavaScript 的演进、阅读遗留代码以及深化对作用域和变量生命周期的理解至关重要。在下一篇文章中,我们将学习 ES6 带来的更强大的变量声明方式:let 和 const。敬请期待!

1234

被折叠的 条评论
为什么被折叠?



