JavaScript入门必学:变量、数据类型与控制流基础详解
系统详解JavaScript入门核心知识:深入剖析let、const与var的区别,全面梳理7大原始类型与引用类型内存机制,掌握条件分支与循环控制流的高效用法与常见陷阱。
编写任何具有实际功能的 JavaScript 脚本,本质上都是在处理三件事:用变量安全存放数据、根据数据类型的特性进行计算转换、依靠控制流决定执行的逻辑分支与循环。理解这三项基本功的运行机制与语法边界,是写出稳定、无缺陷前端或后端程序的首要前提。本文将依次剖析变量声明关键字的选型规范、掌握7大原始类型与引用类型的内存特性,并系统梳理分支与循环的控制流写法。
一、变量声明体系:从 var 到 let 与 const
变量是程序在内存中划定的一块存储单元,变量名则是访问该单元的标识符。在现代 JavaScript(ES6+)中,声明变量有三种关键字:var、let 和 const。虽然历史代码中广泛存在 var,但现代开发规范已经严格限制甚至淘汰了 var。
1. 作用域层级的根本差异
var 仅具备全局作用域或函数级作用域(Function Scope),对由大括号 { } 包裹的代码块(如 if、for、while)完全透明。这会导致在循环或条件语句内声明的 var 变量泄漏至外部作用域,产生意料之外的变量污染。而 let 和 const 具备严格的块级作用域(Block Scope),仅在声明它们的大括号内部有效。
// var 的作用域污染示例
if (true) {
var age = 25;
}
console.log(age); // 输出: 25,外部依然能访问
// let 的块级保护
if (true) {
let score = 98;
}
// console.log(score); // 抛出错误: ReferenceError: score is not defined
2. 提升机制与暂时性死区(TDZ)
使用 var 声明的变量会被“提升”(Hoisting)至作用域顶部,并在执行任何代码之前默认赋值为 undefined。这意味着你在声明语句前访问它不会报错,只会拿到未定义值,极易掩盖拼写错误和逻辑漏洞。相反,let 和 const 虽然也会在语法分析阶段被记录,但在代码执行到具体声明行之前,该变量均处于“暂时性死区”(Temporal Dead Zone, TDZ)。在死区内尝试读取或修改该变量会立即抛出 ReferenceError,从而在开发阶段强制杜绝“先使用后声明”的不良习惯。
在日常编码中,遵循“默认使用 const,仅在需要重新赋值时使用 let,彻底弃用 var”的准则。使用 const 能让变量引用保持恒定,使数据流向一目了然;需要注意的是,如果 const 绑定的是一个对象或数组,其内部属性仍然可以被修改,受保护的仅是该变量指向该内存地址的绑定关系本身。
二、数据类型体系与内存机制详解
JavaScript 是一种弱类型、动态类型的脚本语言。变量本身没有固定类型,值才具备数据类型。根据存储形式和传递机制,数据类型被划分为两大阵营:原始数据类型(Primitive Types)与引用数据类型(Reference Types)。
1. 7 种原始数据类型与特性
- Number:双精度 64 位浮点数,支持整数与小数。包含特殊值
NaN(Not a Number)和Infinity。 - String:不可变的文本字符序列。推荐使用反引号模板字符串
`Hello ${name}`支持插值与多行。 - Boolean:仅包含两个逻辑值:
true与false。 - Undefined:表示“变量已声明但尚未赋值”的默认初始态。
- Null:表示“故意赋予的空对象指针”,常用于手动释放对象引用或表示无值。
- Symbol:ES6 引入的唯一且不可变的原语,常作为对象私有属性名避免命名冲突。
- BigInt:用于处理超过
Number.MAX_SAFE_INTEGER(2^53 - 1)的大整数,字面量末尾添加n(例如9007199254740995n)。
2. 类型检测工具与常见误区
日常开发中最常用的是 typeof 操作符,但在检测复杂数据时需要注意两个著名现象:
console.log(typeof 42); // "number"
console.log(typeof "hello"); // "string"
console.log(typeof undefined); // "undefined"
console.log(typeof true); // "boolean"
// 历史遗留现象:
console.log(typeof null); // "object"(JS早期二进制标识留下的历史遗留包袱)
console.log(typeof [1, 2, 3]); // "object"(数组本质也是对象)
// 精确检测方法:
console.log(Array.isArray([1, 2, 3])); // true
console.log(Object.prototype.toString.call(null)); // "[object Null]"
3. 显式转换与隐式转换
JavaScript 在执行某些算术或比较运算时会自动发生类型转换(隐式转换),这也是初学者产生 Bug 的主要温床。例如加号 + 只要有一侧是字符串,就会将另一侧也转为字符串并执行拼接操作;而减号 - 则会强行将两端转为数值。
// 隐式转换陷阱
console.log("5" + 2); // "52" (数字2被转为字符串)
console.log("5" - 2); // 3 (字符串"5"被转为数字)
console.log([] == false); // true (复杂的隐式抽象比较)
// 推荐做法:显式转换并使用全等符 (===)
const input = "120";
const parsedNumber = Number(input); // 显式转换
console.log(parsedNumber === 120); // true(绝不使用隐式转换的 ==)
三、控制流:条件分支与循环流转
控制流决定了代码执行的先后次序。没有控制流,代码只能从上到下线性执行一次;引入控制流后,程序才能根据不同输入做出判断,或重复处理多项数据。
1. 条件分支结构
JavaScript 提供了三种主要的条件控制方式:
- if...else 链条:适用于范围判断和复合布尔逻辑。
- 三元运算符 (condition ? expr1 : expr2):适用于精简的二选一赋值场景,避免嵌套过深。
- switch 语句:适用于单变量的多常量离散值全等(===)匹配,每个分支务必以
break结尾,防止发生贯穿(fall-through)。
// 1. if...else 结构
const score = 85;
if (score >= 90) {
console.log("等级: A");
} else if (score >= 80) {
console.log("等级: B");
} else {
console.log("等级: C");
}
// 2. 三元运算符简洁赋值
const isMember = true;
const discount = isMember ? 0.8 : 1.0;
// 3. switch 多分支全等判断
const userRole = "editor";
switch (userRole) {
case "admin":
console.log("拥有全部管理权限");
break;
case "editor":
console.log("拥有文章编辑与发布权限");
break;
default:
console.log("只读权限访客");
break;
}
2. 循环迭代与流程控制语句
处理列表数据或重复任务时,根据场景选择合适的循环结构:
- 标准 for 循环:
for (let i = 0; i < n; i++),对迭代计数器和步长拥有完全控制权。 - while 循环:在满足特定前置条件时持续执行,适合循环次数不固定的场景。
- for...of 循环:ES6 推荐方案,专用于遍历可迭代对象(如数组、字符串、Set),直接获取元素本身,语法更简洁自然。
- break 与 continue:
break会立即强制中断并彻底跳出整个循环;continue则跳过当前这一轮剩余代码,直接进入下一次迭代的步进阶段。
// for...of 遍历数组结合控制语句
const cartItems = [
{ name: "键盘", price: 299, inStock: true },
{ name: "鼠标", price: 99, inStock: false },
{ name: "显示器", price: 1299, inStock: true }
];
for (const item of cartItems) {
if (!item.inStock) {
console.log(`[跳过缺货商品]: ${item.name}`);
continue; // 跳过缺货项,继续处理下一件
}
console.log(`[计入结算]: ${item.name} - ¥${item.price}`);
}
四、避坑指南:初学者最常犯的典型错误
在综合运用上述基础知识时,有几个非常高频的技术暗坑需要格外警惕:
1. 循环内部异步执行时的作用域陷阱
如果使用旧式的 var 作为循环计数器,由于它只有函数作用域,异步操作(如 setTimeout 或事件监听)回调被触发时,循环早已跑完,拿到的全是最终值:
// 错误写法 (var 共享同一变量):
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 全部输出: 3, 3, 3
}
// 正确写法 (let 拥有每次循环独立的块级作用域):
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 依次输出: 0, 1, 2
}
2. 浮点数计算精度偏差
JavaScript 的 Number 遵循 IEEE 754 双精度标准,在进行小数运算时存在经典的小数截断误差:
console.log(0.1 + 0.2 === 0.3); // 输出 false! 实际值为 0.30000000000000004
// 解决方案:使用容差比对 (Number.EPSILON) 或转为整数计算
const isEqual = Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON;
console.log(isEqual); // true
掌握 JavaScript 的核心就在于理清“内存怎么存”、“类型怎么转”以及“流程怎么走”。用 const 和 let 固化作用域边界,用全等符号 === 杜绝隐式类型转换,并根据业务逻辑设计条理清晰的 if-else 与 for...of 流程,即可构建出健壮高效的基础程序结构。
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








