写 React 不需要精通 JS 全部语法,但有一组 ES6+ 特性你每天都会遇到。这篇文档把它们集中讲清楚,按出现频率排序。
一、解构赋值
从对象或数组里”拆”出值,给它们取名字。
1.1 对象解构:按名字取
const user = { name: "yi", age: 25, city: "Shanghai" };
// 按名字取值,顺序随意
const { age, name } = user;
// name → 'yi'
// age → 25
// 改名:把 name 改叫 userName
const { name: userName } = user;
// userName → 'yi'
// 取不存在的 key → undefined
const { email } = user;
// email → undefined
// 给默认值
const { email = "none" } = user;
// email → 'none'(因为 user 里没有 email)
// 为什么会没有?后端 JSON 序列化时,值为 null/空的字段常被省略,
// 前端拿到的对象里压根没那个 key,解构出来就是 undefined
React 里的样子——useQuery 返回对象,用对象解构接:
const { data, isPending, error } = useQuery({ ... })
1.2 数组解构:按位置取
const coords = [120, 31];
// 按位置对应,第几个就是第几个
const [lng, lat] = coords;
// lng → 120
// lat → 31
// 跳过某个位置
const [, lat] = coords;
// lat → 31,跳过第一个
// 给默认值
const [first, second, third = 0] = [1, 2];
// third → 0(数组里没有第三个元素)
React 里的样子——useState 返回数组,用数组解构接:
const [count, setCount] = useState(0);
// ↑ 第一个 ↑ 第二个
1.3 为什么 useState 用数组、useQuery 用对象?
| 返回类型 | 适合场景 | 原因 |
|---|---|---|
数组 [a, b] | 返回值少且固定(2 个) | 简洁,你自由命名 |
对象 { a, b, c } | 返回值多(10+ 个) | 按名字取,不用记位置 |
useState 只返回 2 个东西,用数组短;useQuery 返回十几个字段,用对象才不会乱。
1.4 嵌套解构
两种可以组合使用:
const response = {
data: { name: "yi" },
meta: { page: 1, total: 100 },
};
// 对象里取 data,再从 data 里取 name
const {
data: { name },
} = response;
// name → 'yi'
二、箭头函数
2.1 基本语法
// 普通函数
function add(a, b) {
return a + b;
}
// 箭头函数
const add = (a, b) => {
return a + b;
};
// 函数体只有一行时,省略大括号和 return
const add = (a, b) => a + b;
// 只有一个参数时,省略小括号
const double = n => n * 2;
// 没有参数时,必须写空括号
const sayHi = () => console.log("hi");
2.2 在 React 里为什么用箭头函数?
因为短。
// 箭头函数:一行搞定
onClick={() => setCount(count + 1)}
// 普通函数:啰嗦
onClick={function() { setCount(count + 1) }}
React 里到处都是回调——onClick、map、queryFn——箭头函数省掉 function 和 return,代码更紧凑。
2.3 this 的问题(面试常考,React 里感知不强)
箭头函数没有自己的 this,它从定义时的外层作用域继承 this。
不适合当对象方法:
const obj = {
name: "yi",
// ✅ 普通函数当方法:this 指向调用者 obj
greet() {
console.log(this.name);
}, // 'yi'
// ❌ 箭头函数当方法:this 继承外层作用域(全局/模块),不是 obj
greetArrow: () => {
console.log(this.name);
}, // undefined
};
适合需要保留外层 this 的回调:
const obj = {
name: "yi",
friends: ["Alice", "Bob"],
// ✅ 箭头函数回调:this 继承 showFriends 的 this(即 obj)
showFriends() {
this.friends.forEach(friend => {
console.log(this.name + " knows " + friend); // 正确!this 是 obj
});
},
// ❌ 普通函数回调:this 取决于谁调用 forEach,不再是 obj
showFriendsBroken() {
this.friends.forEach(function (friend) {
console.log(this.name + " knows " + friend); // 报错!this 不是 obj
});
},
};
但注意:React 函数组件里根本不用 this,所以这个特性你几乎感觉不到。真正受益的是 class 组件(现在很少写了)。this 相关的知识了解一下即可,面试会问,日常 React 开发不用操心。
2.4 React 里的样子
// 事件处理
<button onClick={() => setCount(count + 1)}>Click</button>;
// map 回调
{
items.map(item => <li key={item.id}>{item.name}</li>);
}
// queryFn
useQuery({
queryKey: ["user"],
queryFn: () => fetch("/api/user").then(res => res.json()),
});
2.5 返回对象字面量的坑
箭头函数直接返回对象时,大括号会被当成函数体:
// ❌ 返回 undefined(大括号被当成函数体,label: 被当成标签语句)
const getUser = () => {
name: "yi";
};
// ✅ 用括号包裹,告诉 JS 这是一个对象
const getUser = () => ({ name: "yi" });
三、展开运算符 ...
把对象或数组”拆开”铺平。
3.1 复制对象
const user = { name: "yi", age: 25 };
// 复制 + 覆盖某个字段
const updated = { ...user, age: 26 };
// → { name: 'yi', age: 26 }
// 添加新字段
const withEmail = { ...user, email: "yi@test.com" };
// → { name: 'yi', age: 25, email: 'yi@test.com' }
记住:... 放前面是”复制原来的”,后面写的字段会覆盖前面的。
{ ...user, age: 26 } // 复制 user,age 改成 26
{ age: 26, ...user } // age: 26 被 user.age: 25 覆盖,结果还是 25
3.2 复制数组
const items = [1, 2, 3];
// 末尾加
const added = [...items, 4]; // [1, 2, 3, 4]
// 开头加
const prepended = [0, ...items]; // [0, 1, 2, 3]
// 合并
const merged = [...items, ...more]; // 拼接两个数组
3.3 React 里的样子
// 更新 state 对象
setUser({ ...user, age: 26 })
// 更新 state 数组
setItems([...items, newItem]) // 加
setItems(items.filter(i => i.id !== id)) // 删
setItems(items.map(i => i.id === id ? { ...i, done: true } : i)) // 改
// 传递 props
<Component {...defaultProps} extraProp="value" />
3.4 收集剩余参数
... 放在函数参数里,作用反过来——收集成数组:
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
3.5 浅拷贝警告
... 只复制一层,嵌套的对象仍是引用:
const original = { name: "yi", address: { city: "Shanghai" } };
const copy = { ...original };
copy.address.city = "Beijing";
console.log(original.address.city); // 'Beijing' ← 原对象也被改了!
嵌套对象需要逐层展开:{ ...original, address: { ...original.address, city: 'Beijing' } }。
四、可选链 ?.
访问可能为 null 或 undefined 的属性/方法,不会报错。
4.1 问题:直接点属性会崩溃
const user = undefined;
user.name; // ❌ TypeError: Cannot read property 'name' of undefined
4.2 解决:?. 遇到 undefined/ null 就短路
user?.name // undefined(不报错)
user?.address?.city // undefined(链式也安全)
items?.map(...) // undefined(方法调用也安全)
等价于:
user && user.name; // 旧写法,啰嗦
user?.name; // 新写法,简洁
4.3 React 里的样子
// 渲染可能为空的数据
<div>{data?.user?.name}</div>;
// 调用可能不存在的方法
onClose?.();
// 访问数组元素
items?.[0]?.name;
五、空值合并 ??
左边是 null 或 undefined 时,用右边的值。
const name = null ?? "匿名"; // '匿名'
const name = undefined ?? "匿名"; // '匿名'
const name = "yi" ?? "匿名"; // 'yi'
const count = 0 ?? 10; // 0(0 不是 null/undefined)
const text = "" ?? "默认"; // ''(空字符串不是 null/undefined)
5.1 和 || 的区别
|| 对所有 falsy 值(0、''、false、null、undefined)都会用右边:
const count = 0 || 10; // 10(0 是 falsy,用了右边)
const count = 0 ?? 10; // 0(0 不是 null/undefined,保留左边)
const text = "" || "默认"; // '默认'
const text = "" ?? "默认"; // ''(空字符串保留左边)
规律:需要保留 0、''、false → 用 ??;不需要 → 用 ||。
5.2 React 里的样子
<div>{user?.name ?? '未登录'}</div>
<span>{remaining ?? '—'}</span>
六、短路求值 && 和 ||
利用 JS 的逻辑运算特性做条件判断——不是返回 true/false,而是返回决定结果的那个值。
6.1 &&:左边为真 → 返回右边;左边为假 → 返回左边
true && "hello"; // 'hello'
false && "hello"; // false
1 && 2 && 3; // 3(全部为真,返回最后一个)
0 && "hello"; // 0(0 是 falsy,返回 0)
React 条件渲染——最常用的模式:
{
isLoading && <Spinner />;
}
// isLoading=true → 渲染 <Spinner />
// isLoading=false → 渲染 false(React 忽略,什么都不显示)
{
items.length > 0 && <List items={items} />;
}
// 有数据才渲染列表
6.2 ||:左边为真 → 返回左边;左边为假 → 返回右边
"yi" || "默认"; // 'yi'
"" || "默认"; // '默认'
0 || 10; // 10
null || "无"; // '无'
React 默认值:
<div>{userName || "未登录"}</div>
七、三元表达式 ? :
一行写完 if-else,返回值。
const status = age >= 18 ? "成年" : "未成年";
等价于:
let status;
if (age >= 18) {
status = "成年";
} else {
status = "未成年";
}
7.1 React 条件渲染
{
isLoggedIn ? <Dashboard /> : <LoginPage />;
}
{
isPending ? "Loading..." : `共 ${data.length} 条`;
}
7.2 可以嵌套,但别超过两层
// ✅ 两层还行
{
status === "loading" ? (
<Spinner />
) : status === "error" ? (
<Error />
) : (
<Content data={data} />
);
}
// ❌ 再多层就改用 if-else 或抽组件
八、模板字符串
用反引号 ` 包裹,${} 里写表达式。
const name = "yi";
const age = 25;
// 旧写法:拼字符串
"Hello, " +
name +
"! Age: " +
age
// 模板字符串
`Hello, ${name}! Age: ${age}`
// ${} 里可以写任何表达式
`Next year: ${age + 1}``Total: ${items.reduce((sum, i) => sum + i.price, 0)}`;
8.1 多行字符串
// 普通字符串不能换行
const html = "<div>\n <p>Hello</p>\n</div>";
// 模板字符串直接换行
const html = `<div>
<p>Hello</p>
</div>`;
九、async / await
Promise 的同步写法——详见 Promise 详解,这里只讲 React 里的用法。
9.1 基本用法
// async 标记函数为异步
async function fetchUser() {
// await 等待 Promise 完成
const res = await fetch("/api/user");
const data = await res.json();
return data;
}
9.2 React 里的样子
// useQuery 的 queryFn
useQuery({
queryKey: ["user"],
queryFn: async () => {
const res = await fetch("/api/user");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
},
});
// useMutation 的 mutationFn
useMutation({
mutationFn: async newTodo => {
const res = await fetch("/api/todos", {
method: "POST",
body: JSON.stringify(newTodo),
});
return res.json();
},
});
9.3 错误处理
// try/catch
async function fetchUser() {
try {
const res = await fetch("/api/user");
return await res.json();
} catch (err) {
console.error(err);
}
}
十、数组方法
10.1 map —— 变换每个元素,返回新数组
React 列表渲染的核心:
const items = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
]
// JSX 列表渲染(必须加 key)
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
// 纯数据变换
const names = items.map(item => item.name);
// ['Apple', 'Banana']
const withPrice = items.map(item => ({ ...item, price: 10 }));
// [{ id: 1, name: 'Apple', price: 10 }, ...]
10.2 filter —— 按条件筛选,返回新数组
const nums = [1, 2, 3, 4, 5];
const evens = nums.filter(n => n % 2 === 0);
// [2, 4]
// 删除数组中的某项(React state 更新)
setItems(items.filter(i => i.id !== deletedId));
10.3 find —— 找第一个符合条件的元素
const items = [
{ id: 1, name: "A" },
{ id: 2, name: "B" },
];
const item = items.find(i => i.id === 2);
// { id: 2, name: 'B' }
const notFound = items.find(i => i.id === 99);
// undefined
10.4 some / every —— 是否有/全部满足条件
const ages = [18, 25, 30];
ages.some(a => a < 20); // true(至少有一个 < 20)
ages.every(a => a >= 18); // true(全部 >= 18)
10.5 reduce —— 累计计算
const prices = [10, 20, 30];
const total = prices.reduce((sum, price) => sum + price, 0);
// 60
// 第一个参数:累计函数
// 第二个参数:初始值(0)
10.6 它们都不修改原数组
map、filter、find、some、every、reduce 全部返回新值,不改变原数组。这和 React 的不可变性原则天然契合。
速查表
| 语法 | 一句话 | React 里怎么用 |
|---|---|---|
| 解构 { } [ ] | 从对象/数组里取值 | useQuery 返回值 / useState 返回值 |
| 箭头函数 => | 更简洁的函数,没有自己的 this | 事件处理、map 回调、queryFn |
| 展开 … | 复制对象/数组 | 更新 state:展开旧值再覆盖 |
| 可选链 ?. | 安全访问可能为空的属性 | data?.user?.name |
| 空值合并 ?? | 左边是 null/undefined 用右边 | name ?? ‘默认’ |
| 短路 && | 左边为真才看右边 | loading && Spinner |
| 三元 ? : | 一行 if-else | ok ? A组件 : B组件 |
| 模板字符串 | 字符串插值 | Hello, ${name} |
| async/await | Promise 的同步写法 | queryFn、mutationFn |
| map / filter | 遍历/筛选数组 | 列表渲染、state 更新 |