Appearance
章节2 — JavaScript 核心语法
一、章节概述
JavaScript 是 Web 前端的灵魂语言。本章从变量与类型出发,逐步覆盖运算符、流程控制、函数、数组对象,深入 ES6+ 新特性(解构、展开、模板字符串、Map/Set),并重点讲解 Promise 与 async/await 异步编程模型,为后续框架学习打下坚实基础。
二、学习目标
| 目标分类 | 具体目标 |
|---|---|
| 知识目标 | 理解 var/let/const 区别;掌握 JS 数据类型体系;理解同步与异步执行模型 |
| 技能目标 | 能熟练使用 ES6+ 语法编写现代代码;能用 Promise / async/await 处理异步操作 |
| 素质目标 | 培养严谨的类型意识与异步编程思维 |
三、核心知识点
3.1 变量声明与数据类型
3.1.1 var / let / const
| 声明方式 | 作用域 | 变量提升 | 重复声明 | 暂存死区 | 可否修改 |
|---|---|---|---|---|---|
var | 函数作用域 | ✅ 提升为 undefined | ✅ 允许 | ❌ | ✅ |
let | 块级作用域 {} | ✅ 提升但不初始化 | ❌ 报错 | ✅ 存在 | ✅ |
const | 块级作用域 {} | ✅ 提升但不初始化 | ❌ 报错 | ✅ 存在 | ❌ 基本类型不可变;对象属性可改 |
javascript
// var 的问题
console.log(a); // undefined(提升)
var a = 10;
// let / const 示例
let score = 95;
if (score >= 90) {
let level = "优秀";
console.log(level); // "优秀"
}
// console.log(level); // ❌ ReferenceError
const PI = 3.14159;
// PI = 3; // ❌ 报错
const user = { name: "Alice" };
user.name = "Bob"; // ✅ 允许,对象引用未变最佳实践:默认使用
const,只在需要重新赋值时用let,永远不用var。
3.1.2 数据类型
JavaScript 有 7 种基本类型 + 1 种引用类型:
| 类型 | typeof 返回值 | 示例 |
|---|---|---|
number | "number" | 42, 3.14, NaN, Infinity |
string | "string" | "hello", 'world', `模板` |
boolean | "boolean" | true, false |
undefined | "undefined" | let x; |
null | "object"(语言 bug) | let y = null; |
symbol | "symbol" | Symbol("id") |
bigint | "bigint" | 9007199254740991n |
object | "object" | {}, [], function |
javascript
// 类型检测注意事项
typeof null // "object"(历史遗留 bug)
typeof [1, 2, 3] // "object"
Array.isArray([1, 2, 3]) // true ✅
// 常见类型转换
Number("42") // 42
String(42) // "42"
Boolean(0) // false
Boolean("") // false
Boolean("false") // true ✅ 非空字符串为 true3.2 运算符、流程控制与函数
3.2.1 常用运算符
javascript
// 严格相等(推荐)
1 === "1" // false
1 == "1" // true(隐式转换,不推荐)
// 可选链 ?.
const user = { profile: { name: "Alice" } };
console.log(user?.profile?.name); // "Alice"
console.log(user?.address?.city); // undefined(不报错)
// 空值合并 ??
const count = 0 ?? 10; // 0(?? 只对 null/undefined 生效)
const count2 = 0 || 10; // 10(|| 对所有假值生效)3.2.2 流程控制
javascript
// if / else if / else
const age = 20;
if (age < 18) {
console.log("未成年");
} else if (age < 60) {
console.log("成年");
} else {
console.log("老年");
}
// switch
const day = 3;
let dayName;
switch (day) {
case 1: dayName = "周一"; break;
case 2: dayName = "周二"; break;
case 3: dayName = "周三"; break;
default: dayName = "其他";
}
// 循环
for (let i = 0; i < 5; i++) { /* 传统 for */ }
for (const item of arr) { /* 遍历数组 */ }
for (const key in obj) { /* 遍历对象键 */ }3.2.3 函数定义方式
javascript
// 1. 函数声明(提升)
function add(a, b) {
return a + b;
}
// 2. 函数表达式(不提升)
const subtract = function (a, b) {
return a - b;
};
// 3. 箭头函数(推荐现代写法)
const multiply = (a, b) => a * b;
// 4. 默认参数
const greet = (name = "游客") => `你好,${name}!`;
// 5. 剩余参数
const sum = (...numbers) => numbers.reduce((acc, n) => acc + n, 0);3.3 数组、对象与 ES6+ 新特性
3.3.1 数组核心方法
| 方法 | 用途 | 是否改变原数组 |
|---|---|---|
push(e) / pop() | 尾部增删 | ✅ |
unshift(e) / shift() | 头部增删 | ✅ |
slice(start, end) | 截取片段 | ❌ |
splice(idx, del, ...items) | 任意位置增删改 | ✅ |
forEach(fn) | 遍历 | ❌ |
map(fn) | 映射为新数组 | ❌ |
filter(fn) | 过滤 | ❌ |
reduce(fn, init) | 归约 | ❌ |
find(fn) | 查找第一个匹配元素 | ❌ |
some(fn) / every(fn) | 判断部分/全部满足 | ❌ |
javascript
const nums = [1, 2, 3, 4, 5];
// 链式调用
const result = nums
.filter(n => n > 2) // [3, 4, 5]
.map(n => n * 10) // [30, 40, 50]
.reduce((acc, n) => acc + n, 0); // 1203.3.2 ES6+ 新特性
解构赋值
javascript
// 数组解构
const [a, b, ...rest] = [1, 2, 3, 4];
console.log(a, b, rest); // 1 2 [3, 4]
// 对象解构
const person = { name: "Bob", age: 25, city: "北京" };
const { name, age, ...others } = person;
console.log(name, others); // "Bob" { city: "北京" }
// 嵌套解构 + 重命名
const data = { user: { id: 1, info: { email: "a@b.com" } } };
const { user: { info: { email: mail } } } = data;
console.log(mail); // "a@b.com"展开运算符 ...
javascript
// 数组合并
const arr1 = [1, 2], arr2 = [3, 4];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
// 对象拷贝与合并
const base = { x: 1, y: 2 };
const copy = { ...base, z: 3 }; // { x: 1, y: 2, z: 3 }
// 函数参数展开
const max = Math.max(...[10, 20, 30]); // 30模板字符串
javascript
const name = "Alice", age = 30;
const greeting = `你好,我是 ${name},今年 ${age} 岁。`;
// 支持多行
const html = `
<div class="card">
<h2>${name}</h2>
</div>
`;Map 与 Set
javascript
// Map — 键值对集合(键可以是任意类型)
const map = new Map();
map.set("name", "Alice");
map.set(42, "答案是");
console.log(map.get(42)); // "答案是"
console.log(map.size); // 2
// Set — 唯一值集合
const set = new Set([1, 2, 2, 3, 3, 3]);
console.log([...set]); // [1, 2, 3]
// 实用场景:数组去重
const unique = [...new Set([1, 2, 2, 3, 3, 4])]; // [1, 2, 3, 4]3.4 Promise 异步编程与 async/await
3.4.1 回调与 Promise
javascript
// 传统回调(回调地狱)
setTimeout(() => {
console.log("第1步");
setTimeout(() => {
console.log("第2步");
setTimeout(() => {
console.log("第3步");
}, 1000);
}, 1000);
}, 1000);
// Promise 链式调用
new Promise((resolve) => {
setTimeout(() => { console.log("第1步"); resolve(); }, 1000);
})
.then(() => new Promise((resolve) => {
setTimeout(() => { console.log("第2步"); resolve(); }, 1000);
}))
.then(() => new Promise((resolve) => {
setTimeout(() => { console.log("第3步"); resolve(); }, 1000);
}));3.4.2 Promise 静态方法
javascript
const p1 = fetch("/api/user").then(r => r.json());
const p2 = fetch("/api/posts").then(r => r.json());
// 全部完成
Promise.all([p1, p2]).then(([user, posts]) => {
console.log("用户:", user, "文章:", posts);
});
// 任一完成
Promise.race([p1, p2]).then(first => console.log("先完成:", first));
// 全部完成(不关心失败)
Promise.allSettled([p1, p2]).then(results => {
results.forEach(r => console.log(r.status, r.value || r.reason));
});3.4.3 async / await(语法糖)
javascript
// async 函数返回一个 Promise
async function fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error("请求失败");
const data = await response.json();
return data;
} catch (error) {
console.error("捕获错误:", error);
throw error; // 让调用方继续处理
}
}
// 使用
(async () => {
const user = await fetchUserData(1);
console.log(user);
})();关键原则:
await只能在async函数中使用;async函数始终返回 Promise。
3.4.4 并行请求优化
javascript
// ❌ 顺序请求(慢)
async function sequential() {
const a = await fetch("/api/a").then(r => r.json());
const b = await fetch("/api/b").then(r => r.json()); // 等 a 完成
}
// ✅ 并行请求(快)
async function parallel() {
const [a, b] = await Promise.all([
fetch("/api/a").then(r => r.json()),
fetch("/api/b").then(r => r.json()),
]);
}3.5 箭头函数深入与展开运算符应用
3.5.1 箭头函数 vs 普通函数
| 特性 | 普通函数 | 箭头函数 |
|---|---|---|
this 指向 | 动态,取决于调用方式 | 词法绑定,继承外层 this |
arguments | 有 | ❌ 无,用 ...args 替代 |
new 调用 | 可作为构造函数 | ❌ 不能 |
| 简洁度 | 较长 | 极简 |
javascript
const obj = {
name: "Alice",
sayLater: function () {
// 普通函数中 this 指向 obj
setTimeout(function () {
console.log(this.name); // undefined(this 指向全局)
}, 100);
},
sayLaterArrow: function () {
// 箭头函数继承外层 this
setTimeout(() => {
console.log(this.name); // "Alice" ✅
}, 100);
},
};3.5.2 综合应用场景
javascript
// 数组处理(现代写法)
const students = [
{ name: "张三", score: 85 },
{ name: "李四", score: 92 },
{ name: "王五", score: 78 },
];
// 1. 过滤出成绩 ≥ 80 的学生
const excellent = students
.filter(({ score }) => score >= 80)
.map(({ name, score }) => `${name}:${score}分`);
// 2. 计算平均分
const avg = students
.reduce((sum, { score }) => sum + score, 0) / students.length;
// 3. 对象快速转换
const scoreMap = Object.fromEntries(
students.map(({ name, score }) => [name, score])
);
// { "张三": 85, "李四": 92, "王五": 78 }四、小结
| 知识点 | 核心要点 |
|---|---|
| 变量声明 | 默认 const,需要重赋值用 let,不用 var |
| 数据类型 | 7 基本 + 1 引用;注意 typeof null === "object" |
| 流程控制 | 可选链 ?. 与空值合并 ?? 是现代 JS 标配 |
| 数组方法 | map / filter / reduce 链式调用是核心范式 |
| ES6+ | 解构、展开、模板字符串、Map/Set 极大提升开发效率 |
| 异步编程 | async/await + Promise.all 是主流异步方案 |
| 箭头函数 | 不绑定 this,适合回调;不能作为构造函数 |
五、练习
基础题
- 声明一个
const数组[1, 2, 3, 4, 5],用解构取出前两个值,并用展开运算符将其与[6, 7, 8]合并。 - 编写一个箭头函数
isPalindrome(str),用展开 + 数组方法判断字符串是否为回文(如 "racecar")。 - 用
map+ 模板字符串,将[{name: "小明", age: 18}, {name: "小红", age: 20}]转为"小明18岁" / "小红20岁"格式数组。
进阶题
- 使用
async/await+Promise.all编写一个函数fetchMultiple(urls),传入 URL 数组,并行请求所有资源,返回解析后的 JSON 数组。 - 用
Set实现数组去重,再用Map统计数组中每个元素出现的次数(如[1, 2, 2, 3, 3, 3]→{1: 1, 2: 2, 3: 3})。
挑战题
- 不使用
Promise.all,手写一个myPromiseAll(promises)函数,实现相同功能:所有 promise 成功则返回结果数组,任一失败则立即 reject。