JavaScript基础语法实例:变量声明、循环结构与函数定义
全面解析JavaScript三大核心基础语法:变量声明(var/let/const)的作用域规则、循环结构(for/while/for...of)的流程控制,以及函数定义(声明式/表达式/箭头函数)的调用模式与工程实例。
学习 JavaScript 的本质是掌握程序组织与数据流转的控制逻辑。无论是构建前端界面交互还是编写后端 Node.js 服务,变量声明决定了数据的生命周期与内存可见性,循环结构决定了集合计算与流程迭代的高效执行,而函数定义则决定了业务逻辑的解耦粒度与复用方式。本文通过贴合实际编码的实例、详细的行为差异对比以及关键结构图示,逐项解析这三项基础语法的运行机制与最佳实践。
一、变量声明与作用域管理
在 JavaScript 中,声明变量主要有 const、let 和 var 三种关键字。现代 JavaScript(ES6 及以上规范)中,变量声明的核心原则是默认优先使用 const,仅在变量确需重新赋值时使用 let,全面摒弃 var。
1. 块级作用域与暂时性死区(TDZ)
let 和 const 均引入了块级作用域(Block Scope),即变量的作用范围限制在最近的一对大括号 { ... } 之内。同时,它们具备“暂时性死区”特性:变量在作用域顶部被创建,但在执行到达声明语句之前无法被读取或赋值,否则直接抛出 ReferenceError。
// 块级作用域与 TDZ 示例
function scopeExample() {
// console.log(counter); // 报错:ReferenceError: Cannot access 'counter' before initialization (暂时性死区)
let counter = 1;
const maxLimit = 10;
if (counter < maxLimit) {
let innerMessage = "当前未达上限";
console.log(innerMessage); // 正常输出
}
// console.log(innerMessage); // 报错:ReferenceError: innerMessage is not defined (超出块级作用域)
}
scopeExample();
当前未达上限
2. const 的不可变性边界
const 用于声明只读常量。需要明确的是,const 锁定的只是变量标识符与内存地址之间的绑定关系,而不是目标值本身的内部状态。如果 const 声明的是基本数据类型(如数字、字符串),该值确实不可变;但如果声明的是对象或数组,对象的属性或数组的元素是可以被修改的。
const baseConfig = { timeout: 3000, retry: 3 };
baseConfig.timeout = 5000; // 允许修改对象属性
console.log(baseConfig.timeout); // 输出 5000
const list = [1, 2];
list.push(3); // 允许修改数组元素
console.log(list); // 输出 [1, 2, 3]
// baseConfig = {}; // 报错:TypeError: Assignment to constant variable. (禁止重新绑定内存地址)
3. 传统 var 存在的隐患
早期规范中的 var 仅具备函数作用域或全局作用域,不识别代码块(如 if、for)。此外,var 会产生“变量声明提升”,并在提升阶段被隐式初始化为 undefined,容易引发静默逻辑错误。
二、循环结构与执行控制
循环结构用于重复执行指定的代码块,直到预设条件不再满足。根据迭代目标的不同(固定次数、条件驱动、遍历集合),JavaScript 提供了多种循环语法。
1. 标准 for 循环:计次与步进控制
标准 for 循环包含三部分:初始化表达式、条件求值表达式和每次迭代后的递增/递减表达式。适合已知循环次数或需要显式索引指针的场景。
// 标准 for 循环:过滤偶数并累加
const numbers = [12, 7, 23, 44, 18, 9];
let evenSum = 0;
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) {
evenSum += numbers[i];
}
}
console.log("偶数总和:", evenSum);
偶数总和: 74
2. while 与 do...while 循环:条件驱动控制
while 循环在每次执行循环体之前评估条件;如果初始条件为假,循环体一次也不会执行。而 do...while 则先执行一次循环体,再评估条件,确保循环体至少被执行一次。
// while 循环:模拟二分折半查找步数
let remainingLength = 1000;
let attempts = 0;
while (remainingLength > 1) {
remainingLength = Math.floor(remainingLength / 2);
attempts++;
}
console.log("折半次数:", attempts);
折半次数: 9
3. 集合遍历:for...of 与 for...in 的本质区别
很多初学者容易混淆 for...of 和 for...in:
for...of(推荐遍历数组/集合):直接遍历可迭代对象(Array、Set、Map、字符串)的元素值(Value)。for...in(专用于枚举对象属性):遍历对象的可枚举属性键名(Key),并会沿着原型链向上查找,不建议用于常规数组的顺序遍历。
const fruits = ["苹果", "香蕉", "橙子"];
// 1. for...of 遍历数组元素
for (const item of fruits) {
console.log("水果:", item);
}
// 2. for...in 遍历对象键名
const book = { title: "JS核心指南", pages: 320, published: 2024 };
for (const key in book) {
if (Object.hasOwn(book, key)) {
console.log(`${key} => ${book[key]}`);
}
}
4. 循环流程跳转:break 与 continue
break 语句会立即终止当前循环并跳出循环体;continue 语句则跳过当前迭代的剩余代码,直接进入下一次循环条件的判定与步进。
三、函数定义与调用模式
函数是 JavaScript 中封装可复用计算逻辑的一等公民(First-Class Citizen)。这意味着函数可以作为参数传递、作为返回值输出,或赋值给变量。JavaScript 主要有三种定义函数的方式。
1. 函数声明(Function Declaration)
使用 function 关键字后紧跟函数标识符。它的核心特性是函数声明整体提升(Hoisting):在代码执行阶段开始前,函数的定义已经被完整加载至当前作用域中,因此调用代码可以写在声明代码的前面。
// 可以在函数声明之前直接调用
const result = calculateTax(1000, 0.08);
console.log("计算税金:", result); // 输出 80
function calculateTax(amount, rate) {
return amount * rate;
}
2. 函数表达式(Function Expression)
将一个匿名或具名函数赋值给一个变量。此时该函数的提升行为遵循该变量关键字的规则。如果使用 const 声明,在赋值执行之前该函数处于暂时性死区,不可在声明之前调用。
// console.log(formatCurrency(299)); // 报错:ReferenceError
const formatCurrency = function(val) {
return `¥${Number(val).toFixed(2)}`;
};
console.log(formatCurrency(299)); // 正常输出: ¥299.00
3. 箭头函数(Arrow Function)
ES6 引入的简洁语法,使用 => 符号。箭头函数具有以下两项关键区别于普通函数的底层特征:
- 没有独立的
this绑定:箭头函数内部的this静态捕获外层作用域的上下文,不能通过call()、apply()、bind()改变指向。 - 没有
arguments对象,且不能作为构造函数:无法通过new关键字实例化。
// 单参数与单表达式简写
const square = x => x * x;
console.log("平方结果:", square(6)); // 输出 36
// 词法作用域 this 绑定演示
const timerHandler = {
label: "任务计时器",
start() {
// 箭头函数保留 start 方法内的 this 指向
setTimeout(() => {
console.log(`执行上下文: ${this.label}`);
}, 100);
}
};
timerHandler.start();
执行上下文: 任务计时器
四、综合实例:变量、循环与函数的协同应用
在实际业务开发中,变量、循环与函数并不是孤立存在的。通常,我们使用 const 绑定静态数据与函数,使用 let 维护循环迭代过程中的状态变量,通过循环迭代处理集合,并将单一逻辑封装为纯函数(Pure Function)。
综合实战:电商订单明细计算器
以下实例模拟统计用户购物车中的有效订单项:过滤库存充足的商品、计算单项折后价格、累计总金额并生成格式化收据。
// 1. 变量定义:原始商品数据列表(引用不可变)
const cartItems = [
{ id: 101, name: "人体工学椅", price: 899, quantity: 1, inStock: true },
{ id: 102, name: "无线机械键盘", price: 349, quantity: 2, inStock: true },
{ id: 103, name: "4K显示器支架", price: 220, quantity: 1, inStock: false }, // 无货
{ id: 104, name: "Type-C拓展坞", price: 159, quantity: 1, inStock: true }
];
// 2. 函数定义:纯函数计算单项小计(包含满减优惠逻辑)
const calculateSubtotal = (price, quantity) => {
const rawTotal = price * quantity;
// 满 500 减 50 优惠
return rawTotal >= 500 ? rawTotal - 50 : rawTotal;
};
// 3. 函数定义:订单聚合处理函数
function processOrder(items) {
let payableTotal = 0; // let 维护累计总金额
let validItemCount = 0; // let 维护有效商品数量
const receiptLines = []; // const 声明存储结果的数组
// 循环结构:使用 for...of 遍历购物车集合
for (const item of items) {
// 遇到缺货商品,跳过当前项
if (!item.inStock) {
continue;
}
const subtotal = calculateSubtotal(item.price, item.quantity);
payableTotal += subtotal;
validItemCount += item.quantity;
receiptLines.push(`${item.name} × ${item.quantity} = ¥${subtotal}`);
}
return {
lines: receiptLines,
totalCount: validItemCount,
finalAmount: payableTotal
};
}
// 4. 执行调用与输出展示
const orderSummary = processOrder(cartItems);
console.log("=== 订单结算明细 ===");
for (const line of orderSummary.lines) {
console.log(line);
}
console.log(`有效商品总件数: ${orderSummary.totalCount}`);
console.log(`应付总金额: ¥${orderSummary.finalAmount}`);
=== 订单结算明细 ===
人体工学椅 × 1 = ¥849
无线机械键盘 × 2 = ¥648
Type-C拓展坞 × 1 = ¥159
有效商品总件数: 4
应付总金额: ¥1656
避免在异步回调中滥用 var:如果在 for (var i = 0; i < 3; i++) 中设置 setTimeout,由于 var 属于函数作用域而非块作用域,所有的定时器回调最终都会共享同一个已经变为 3 的变量 i。改用 for (let i = 0; ...) 可以利用块级作用域在每次循环迭代时创建独立的作用域绑定,彻底解决该问题。
五、总结与编码自查清单
掌握 JavaScript 的基础语法,不仅在于能够记住关键字的拼写,更在于能够依据运行时的内存与作用域规则写出高内聚、低耦合、防御性强的代码。在日常编写与代码审查中,可以参考以下清单进行快速自查:
- 作用域是否严密:是否已经完全杜绝了
var?能够使用const的地方是否误用了let? - 循环是否具备安全退出边界:在编写
while或带条件的for循环时,迭代步进变量是否必定能在有限次数内触发终止条件? - 集合遍历语法是否正确匹配数据结构:遍历数组元素是否优先使用了
for...of而非for...in? - 函数上下文(
this)是否符合预期:在作为对象方法使用时是否需要动态上下文,而在作为定时器或高阶函数回调时是否采用了箭头函数以避免作用域漂移?
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58








