JavaScript基础语法实例:变量、循环与函数用法详解
深入解析JavaScript基础核心语法,系统讲解var、let与const的作用域机制,详细剖析for与while循环执行控制,结合代码实例阐释函数声明、表达式与箭头函数的用法与注意事项。
编写任何 JavaScript 程序的基础都可以拆解为三个最关键的语法构成:如何存储数据(变量)、如何重复执行指定逻辑(循环),以及如何组织并复用业务逻辑(函数)。理解这三者的运行机制、语法边界与常见陷阱,是掌握后续异步编程、DOM 操作及各类现代框架的核心前提。本文结合可运行的实例代码与架构解析图,逐项详解变量声明、循环控制与函数的用法规范及实战要点。
一、变量声明与内存绑定机制
JavaScript 提供了三种用于声明变量的关键字:var、let 与 const。在现代 ECMAScript 规范中,推荐优先使用 const,仅在需要重新赋值时使用 let,并完全避免使用早期的 var。这种规范源于三者在作用域边界与变量生命周期上的本质差异。
1. 作用域划分与暂时性死区(TDZ)
var 遵循函数作用域规则,如果在函数外部声明则为全局变量,并且存在声明提升(Hoisting)现象,允许在声明前访问(此时值为 undefined),还允许对同一个变量名重复声明。这种宽容的特性极易导致变量被静默覆盖或全局污染。
相较之下,let 和 const 具备严格的块级作用域(由一对花括号 {} 限定),并且引入了暂时性死区(Temporal Dead Zone, TDZ)。在代码块执行初始,变量虽已绑定该作用域,但在实际执行到声明语句之前,访问该变量会直接抛出 ReferenceError。
// 实例:var 与 let 的作用域及提升差异对比
function testScope() {
console.log("var提升前读取:", tempVar); // 输出: undefined
// console.log("let提升前读取:", tempLet); // 报错: Cannot access 'tempLet' before initialization (TDZ)
var tempVar = "可提升的数据";
let tempLet = "块作用域数据";
if (true) {
var insideBlockVar = "穿透代码块";
let insideBlockLet = "锁在代码块内部";
}
console.log(insideBlockVar); // 正常输出: 穿透代码块
// console.log(insideBlockLet); // 报错: insideBlockLet is not defined
}
testScope();
穿透代码块
2. const 的不可变性边界
const 声明必须在初始化时赋值,且不能对变量标识符本身进行重新赋值。但需要特别注意:当 const 保存的是引用类型(如对象、数组)时,保证不可变的是内存地址指针,而非对象内部的属性结构。如需锁定对象属性,需要配合 Object.freeze() 实现浅冻结。
// 实例:const 引用类型操作
const config = {
timeout: 5000,
retries: 3
};
// 允许修改对象内部属性
config.timeout = 10000;
console.log("修改后属性:", config.timeout);
// 尝试重新赋值整个对象将引发类型错误
// config = { timeout: 2000 }; // TypeError: Assignment to constant variable.
二、循环结构的控制流程与选择策略
循环语句用于控制程序段重复执行指定的次数或直至满足特定终止条件。JavaScript 提供了常规计数循环(for)、条件分支循环(while、do...while)以及专门用于遍历迭代对象的高级循环结构(for...of、for...in)。根据数据结构与业务目标正确选择循环,可以显著提升代码的鲁棒性与运行效率。
1. 经典 for 循环与条件循环
for 循环将初始化、条件判断和迭代步进整合在头部,结构严密,最适合已知迭代范围的数组或区间计数:
// 实例:标准 for 循环计算累计和与偶数筛选
const scores = [65, 88, 92, 74, 99];
let totalScore = 0;
for (let i = 0; i < scores.length; i++) {
totalScore += scores[i];
}
console.log("全班总分:", totalScore);
// 实例:while 循环实现递减条件控制
let retryCount = 3;
while (retryCount > 0) {
console.log("执行重试任务,剩余次数:", retryCount);
retryCount--;
}
执行重试任务,剩余次数: 3
执行重试任务,剩余次数: 2
执行重试任务,剩余次数: 1
2. for...of 与 for...in 的本质区别
许多开发者在面对复合数据时容易混淆 for...in 与 for...of:
for...in:遍历对象自身及其原型链上的可枚举属性名(Key)。遍历返回的是字符串类型的属性名,不能保证固定的遍历顺序,严禁用于遍历常规数组。for...of:基于 ES6 的迭代器协议(Iterable Protocol),专门用于遍历具备Symbol.iterator属性的集合元素值(Value),如 Array、Set、Map、字符串等。
// 实例:for...in 与 for...of 的数据提取差异
const techStack = ["Node.js", "React", "Vue"];
techStack.customTag = "Frontend"; // 人工追加自定义属性
console.log("--- 使用 for...in 遍历 (获取键/属性名) ---");
for (const key in techStack) {
console.log("键:", key); // 输出: "0", "1", "2", "customTag"
}
console.log("--- 使用 for...of 遍历 (获取元素值) ---");
for (const item of techStack) {
console.log("值:", item); // 输出: "Node.js", "React", "Vue" (仅迭代合法数组元素)
}
键: 0
键: 1
键: 2
键: customTag
--- 使用 for...of 遍历 (获取元素值) ---
值: Node.js
值: React
值: Vue
3. 中断与步进:break 与 continue
break 语句用于彻底跳出当前循环体,直接执行循环之后的语句;而 continue 语句仅用于中断当前周期的后续代码,直接跳至下一个迭代判断。在嵌套循环中,还可通过标签语句(Label)精确指定跳出的外层循环。
三、函数定义形态与执行上下文
函数是 JavaScript 中实现代码抽象与封装的第一公民(First-Class Citizens)。函数可以被当作参数传递、从其他函数返回,并赋予变量。然而,不同函数语法定义方式在提升表现、this 指向及参数绑定上有着核心差异。
1. 函数声明、函数表达式与箭头函数
JavaScript 常见的三种函数定义形态如下:
- 函数声明(Function Declaration):使用
function name() {}语法。函数声明会被整体提升至当前作用域顶部,可以在声明语句之前直接调用。 - 函数表达式(Function Expression):将匿名或具名函数赋值给变量(如
const fn = function() {})。其提升行为取决于所用变量声明(const/let处于 TDZ,不可提前调用)。 - 箭头函数(Arrow Function):ES6 引入的简洁语法
const fn = () => {}。箭头函数不绑定自身的 this,而是直接捕获其定义时所处上下文的this值(词法作用域);此外,箭头函数没有自身的arguments对象,也不能用作构造函数(无法使用new实例化)。
// 实例:传统函数与箭头函数中的 this 指向对比
const userProfile = {
username: "Developer_K",
skills: ["JavaScript", "CSS"],
// 传统函数方法
printSkillsStandard: function() {
console.log("外部 this 指向:", this.username);
// 在传统回调中,this 会丢失或指向全局/undefined
setTimeout(function() {
// 此处的 this 默认指向全局 window / global,导致读取失败
// console.log("传统回调内部:", this.username);
}, 100);
},
// 箭头函数方法结合词法 this
printSkillsModern: function() {
setTimeout(() => {
// 箭头函数继承外层 printSkillsModern 上下文中的 this
console.log("箭头函数继承 this.username:", this.username);
}, 100);
}
};
userProfile.printSkillsModern();
2. 形参默认值与剩余参数(Rest Parameters)
现代 JavaScript 支持在形参列表中直接设定默认值,当调用方未传参或显式传递 undefined 时自动兜底。同时,可以使用 ...args 剩余参数语法收拢不定数量的实参为真实数组,从而完全取代老旧脆弱的 arguments 类数组对象。
// 实例:默认参数与 Rest 参数实战
function calculateBill(basePrice, taxRate = 0.08, ...discounts) {
const totalDiscount = discounts.reduce((sum, d) => sum + d, 0);
const taxedPrice = basePrice * (1 + taxRate);
const finalPrice = Math.max(0, taxedPrice - totalDiscount);
return {
basePrice,
taxAmount: basePrice * taxRate,
totalDiscount,
finalPrice
};
}
const billResult = calculateBill(100, undefined, 5, 10);
console.log("账单结果:", billResult);
四、综合实战:三大支柱语法协同应用
在真实的工程业务中,变量、循环与函数并不是孤立存在的,而是彼此协同运作:函数承接数据输入并提供作用域保护,局部变量维系状态安全,循环负责集合流水线调度。以下通过一个处理电商购物车订单聚合计算的完整实例,展示三者的综合应用。
// 综合实例:订单列表多规则结算处理系统
const cartItems = [
{ id: 101, name: "机械键盘", price: 399, count: 2, category: "digital" },
{ id: 102, name: "显示器支架", price: 159, count: 1, category: "furniture" },
{ id: 103, name: "鼠标垫", price: 49, count: 3, category: "digital" }
];
/**
* 计算结算单据
* @param {Array} items 购物车条目
* @param {number} threshold 满减门槛
* @param {number} rebate 优惠立减金额
*/
function processCheckout(items, threshold = 500, rebate = 50) {
// 1. 初始化隔离状态变量
let rawSubtotal = 0;
const processedItems = [];
// 2. 循环遍历各明细条目,进行校验与小计累加
for (const item of items) {
// 块级作用域变量保障各条目计算无副作用
const itemTotal = item.price * item.count;
rawSubtotal += itemTotal;
processedItems.push({
id: item.id,
name: item.name,
itemTotal
});
}
// 3. 优惠逻辑计算
let actualRebate = 0;
if (rawSubtotal >= threshold) {
actualRebate = rebate;
}
const finalPayable = Math.max(0, rawSubtotal - actualRebate);
return {
itemCount: processedItems.length,
rawSubtotal,
actualRebate,
finalPayable,
details: processedItems
};
}
// 执行结算
const orderSummary = processCheckout(cartItems, 600, 80);
console.log("最终订单汇总:", orderSummary);
itemCount: 3,
rawSubtotal: 1104,
actualRebate: 80,
finalPayable: 1024,
details: [
{ id: 101, name: '机械键盘', itemTotal: 798 },
{ id: 102, name: '显示器支架', itemTotal: 159 },
{ id: 103, name: '鼠标垫', itemTotal: 147 }
]
}
在使用早期 var 声明循环变量时,由于变量提升和函数作用域,在循环内部启动的定时器或异步回调捕获的都是同一个内存地址,导致所有回调执行时读取的都是递增结束后的最终值。改用 let 之后,JavaScript 引擎在每次循环迭代时都会为代码块创建独立的词法环境(Lexical Environment),从根本上避免了闭包数据错乱。
五、核心实践总结
掌握 JavaScript 基础语法并写出健壮代码的关键,在于时刻明确代码所处的执行上下文:
- 坚守块级作用域准则:彻底弃用
var,将const作为第一选择,让变量的生命周期与可见范围降至最小。 - 依据数据类型挑选循环:纯值遍历数组优先选用
for...of;对象键名读取使用Object.keys()或for...in(配合自有属性检查);复杂的序列算法使用标准for循环精细控制步长。 - 清晰规划函数职责与上下文:回调函数与流式数据处理充分利用箭头函数的词法
this优势;模块核心功能与公共导出接口采用规范的函数声明,并利用参数默认值和 Rest 参数提升接口容错度。
D3.js 比例尺与坐标轴的配置详解
2026-11-05 16:54:46
JavaScript怎么遍历对象的Symbol键和不可枚举属性
2026-11-05 13:13:39
封装一个健壮的JS字符串判空工具函数
2026-11-05 12:16:14
JavaScript合并数组的三种常用方法:concat、扩展运算符与push
2026-11-05 11:22:29
JavaScript立即执行函数(IIFE)是什么及基本语法
2026-11-05 10:51:19








