JavaScript 实用代码片段
# JavaScript 实用代码片段集合
本文档收集了 JavaScript/TypeScript 开发中常用的代码片段和技巧,帮助你写出更简洁、更优雅的代码。
# 1. 解构赋值技巧
ES6 解构语法可以让代码更简洁优雅,以下是几种常见的实用场景。
# 1.1 对象解构
# 精简对象赋值
场景:从嵌套对象中提取多个属性。
// ❌ 原始写法(冗余)
const model = {
template_id: prompt?.ext?.template_id,
biz_id: prompt?.ext?.biz_id,
cell_text: prompt?.ext?.cell_text,
friend_avatars: prompt?.ext?.friend_avatars,
elements: prompt?.ext?.elements,
cell_type: prompt?.ext?.cell_type,
ext: prompt?.ext,
};
// ✅ 优化写法一:使用扩展运算符
const model = {
...(prompt?.ext || {}),
ext: prompt?.ext,
};
// ✅ 优化写法二:先解构再赋值
const { template_id, biz_id, cell_text, friend_avatars, elements, cell_type } =
prompt?.ext || {};
const model = {
template_id,
biz_id,
cell_text,
friend_avatars,
elements,
cell_type,
ext: prompt?.ext,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| 方式 | 优点 | 缺点 |
|---|---|---|
| 写法一 | 代码最简洁 | 可能引入不需要的属性 |
| 写法二 | 明确指定需要的属性 | 代码略长但更清晰 |
# 设置默认值
场景:为可能不存在的属性设置默认值。
// 解构时设置默认值
const {
type = 'default_type',
sub_type = 'default_sub_type',
author_uin,
} = data;
// ✅ 正确:先展开 data,再设置需要覆盖的属性
const result = {
...data, // 先展开
type, // 再覆盖(使用解构后的默认值)
sub_type,
author_uin,
launch: 1,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
注意
- 默认值只在属性为
undefined时生效,对null不生效 - 对象展开的顺序很重要:后面的属性会覆盖前面的
// 默认值的陷阱
const obj = { value: null };
const { value = 'default' } = obj;
console.log(value); // null(而不是 'default')
// 展开顺序
const config = {
...defaults, // 先展开
...custom, // 后展开,会覆盖 defaults 中的同名属性
};
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 提取剩余属性
场景:在不修改原对象的情况下"删除"某些属性。
// 删除单个属性
const { password, ...publicUser } = user;
// 删除多个属性
const { password, email, createdAt, ...safeUser } = user;
// 在函数参数中使用
function updateUser({ id, ...updates }) {
return database.update(id, updates);
}
// 更多示例
const mainData = getMainData();
const { firstPageData, ...rest } = mainData;
showPopup({
...rest,
type: 1,
sub_type: 2,
}, 0);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1.2 数组解构
// 基本解构
const [first, second, third] = [1, 2, 3, 4, 5];
console.log(first, second, third); // 1 2 3
// 跳过元素
const [a, , c] = [1, 2, 3];
console.log(a, c); // 1 3
// Rest 元素
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]
// 默认值
const [x = 0, y = 0] = [1];
console.log(x, y); // 1 0
// 交换变量
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1.3 函数参数解构
// React 组件风格的函数参数
function UserCard({ name, age, email }: {
name: string;
age: number;
email?: string;
}) {
return `${name}, ${age}岁`;
}
// 带默认值的参数解构
function createUser({
name,
age = 18,
role = 'user'
}: {
name: string;
age?: number;
role?: string;
}) {
return { name, age, role };
}
// 调用
createUser({ name: 'Alice' }); // { name: 'Alice', age: 18, role: 'user' }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 2. 扩展运算符应用
# 2.1 对象合并
// 合并多个对象
const user = { name: 'Alice', age: 25 };
const address = { city: 'Beijing', country: 'China' };
const merged = { ...user, ...address };
// { name: 'Alice', age: 25, city: 'Beijing', country: 'China' }
// 覆盖属性
const defaults = { theme: 'light', lang: 'en' };
const custom = { lang: 'zh' };
const config = { ...defaults, ...custom };
// { theme: 'light', lang: 'zh' }
// 深度合并(只合并一层)
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { b: { d: 3 } };
const merged = { ...obj1, ...obj2 };
// { a: 1, b: { d: 3 } } - 注意:b对象被完全替换,c丢失了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2.2 数组合并
// 合并数组
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const merged = [...arr1, ...arr2];
// [1, 2, 3, 4, 5, 6]
// 在数组中间插入元素
const original = [1, 2, 5, 6];
const inserted = [1, 2, 3, 4, 5, 6];
// 使用扩展运算符
const result = [...original.slice(0, 2), 3, 4, ...original.slice(2)];
// 数组去重
const numbers = [1, 2, 2, 3, 3, 4];
const unique = [...new Set(numbers)];
// [1, 2, 3, 4]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2.3 条件属性添加
// 只在值合法时添加属性
const { page_from, page_sn, page_id } = data;
const trackParams = {
...(page_from && { page_from }),
...(page_id && { page_id }),
page_sn, // 必填字段
};
// 条件添加多个属性
const apiParams = {
...baseParams,
...(isAdmin && {
admin_token: token,
permissions: ['all']
}),
...(includeMetadata && {
created_at: Date.now(),
version: '1.0'
}),
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 3. 对象和数组拷贝
# 3.1 浅拷贝
# 对象浅拷贝
const original = { a: 1, b: { c: 2 } };
// 方法1:扩展运算符(推荐)
const copy1 = { ...original };
// 方法2:Object.assign()
const copy2 = Object.assign({}, original);
1
2
3
4
5
6
7
2
3
4
5
6
7
⚠️ 浅拷贝的陷阱:
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { ...obj1 };
obj2.a = 10; // ✅ 不影响 obj1.a
obj2.b.c = 30; // ❌ 会影响 obj1.b.c(因为 b 是引用)
console.log(obj1); // { a: 1, b: { c: 30 } }
1
2
3
4
5
6
7
2
3
4
5
6
7
# 数组浅拷贝
const arr = [1, 2, 3, 4, 5];
// 方法1:扩展运算符(推荐)⭐
const copy1 = [...arr];
// 方法2:slice()
const copy2 = arr.slice();
// 方法3:Array.from()
const copy3 = Array.from(arr);
// 方法4:concat()
const copy4 = [].concat(arr);
// 方法5:Object.assign()(不推荐)
const copy5 = Object.assign([], arr);
// ❌ 错误:会创建嵌套数组
const wrong = [Array.from(arr)]; // [[1, 2, 3, 4, 5]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
性能对比:
| 方法 | 性能 | 可读性 | 推荐度 |
|---|---|---|---|
[...arr] | ⚡⚡⚡ | ★★★★★ | ⭐⭐⭐⭐⭐ |
arr.slice() | ⚡⚡⚡ | ★★★★☆ | ⭐⭐⭐⭐ |
Array.from() | ⚡⚡ | ★★★★☆ | ⭐⭐⭐ |
concat() | ⚡⚡ | ★★★☆☆ | ⭐⭐⭐ |
Object.assign() | ⚡ | ★★☆☆☆ | ⭐⭐ |
# 3.2 深拷贝
# 方法1:JSON 序列化(适用简单场景)
const obj = { a: 1, b: { c: 2 }, arr: [1, 2, 3] };
const deepCopy = JSON.parse(JSON.stringify(obj));
deepCopy.b.c = 3;
console.log(obj.b.c); // 2(不受影响)✅
1
2
3
4
5
2
3
4
5
⚠️ JSON 方法的限制:
- ❌ 无法复制函数
- ❌ 无法复制
undefined - ❌ 无法处理循环引用
- ❌ 无法复制
Date、RegExp、Map、Set等 - ❌ 会丢失原型链
# 方法2:结构化克隆(现代浏览器)
// 浏览器环境(Chrome 98+)
const deepCopy = structuredClone(original);
// 支持更多类型
const obj = {
date: new Date(),
map: new Map([['a', 1]]),
set: new Set([1, 2, 3]),
arr: [1, 2, { nested: true }]
};
const copy = structuredClone(obj);
// ✅ 完美复制所有特殊类型
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 方法3:自定义递归(完整版)
function deepCopy(obj, hash = new WeakMap()) {
// 基本类型和 null
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 处理 Date
if (obj instanceof Date) {
return new Date(obj);
}
// 处理 RegExp
if (obj instanceof RegExp) {
return new RegExp(obj.source, obj.flags);
}
// 处理 Map
if (obj instanceof Map) {
const mapCopy = new Map();
obj.forEach((value, key) => {
mapCopy.set(key, deepCopy(value, hash));
});
return mapCopy;
}
// 处理 Set
if (obj instanceof Set) {
const setCopy = new Set();
obj.forEach(value => {
setCopy.add(deepCopy(value, hash));
});
return setCopy;
}
// 处理循环引用
if (hash.has(obj)) {
return hash.get(obj);
}
// 创建新对象或数组
const newObj = Array.isArray(obj) ? [] : {};
hash.set(obj, newObj);
// 递归复制属性
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key], hash);
}
}
return newObj;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 方法4:第三方库
// Lodash(推荐)
import _ from 'lodash';
const deepCopy = _.cloneDeep(original);
// Ramda
import R from 'ramda';
const deepCopy = R.clone(original);
1
2
3
4
5
6
7
2
3
4
5
6
7
# 3.3 选择建议
| 场景 | 推荐方法 | 理由 |
|---|---|---|
| 简单对象/数组 | {...obj} / [...arr] | 性能最好,代码简洁 |
| 纯数据对象 | structuredClone() 或 JSON | 现代浏览器首选 |
| 复杂对象 | _.cloneDeep() | 功能完整,久经考验 |
| 自定义需求 | 自定义递归函数 | 可控性强 |
# 4. 数组操作技巧
# 4.1 函数式方法
const numbers = [1, 2, 3, 4, 5];
// map:转换每个元素
const doubled = numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]
// filter:过滤元素
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4]
// find:查找第一个匹配的元素
const firstEven = numbers.find(n => n % 2 === 0);
// 2
// findIndex:查找索引
const index = numbers.findIndex(n => n > 3);
// 3
// some:是否存在满足条件的元素
const hasEven = numbers.some(n => n % 2 === 0);
// true
// every:是否所有元素都满足条件
const allPositive = numbers.every(n => n > 0);
// true
// reduce:累计计算
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15
// flatMap:映射并展平
const nested = [[1, 2], [3, 4], [5]];
const flattened = nested.flatMap(arr => arr);
// [1, 2, 3, 4, 5]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 4.2 实用技巧
// ❌ 传统写法:收集满足条件的元素
const result = [];
for (let i = 0; i < arr.length; i++) {
if (condition(arr[i])) {
result.push(arr[i]);
}
}
// ✅ 优化写法
const result = arr.filter(condition);
// 链式调用
const result = numbers
.filter(n => n > 0)
.map(n => n * 2)
.filter(n => n < 10);
// 数组去重
const unique = [...new Set(arr)];
// 数组扁平化
const nested = [1, [2, [3, [4]]]];
const flat = nested.flat(Infinity);
// [1, 2, 3, 4]
// 数组分组(对象版)
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 25 }
];
const grouped = people.reduce((acc, person) => {
const key = person.age;
if (!acc[key]) acc[key] = [];
acc[key].push(person);
return acc;
}, {});
// { 25: [{...}, {...}], 30: [{...}] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 4.3 循环控制
# 提前终止
const numbers = [1, 2, 3, 4, 5];
// for/for...of:可以使用 break
for (const num of numbers) {
if (num === 3) break;
console.log(num); // 1, 2
}
// find:找到第一个就停止
const found = numbers.find(n => n > 2); // 3
// some:找到第一个满足条件的就停止
const exists = numbers.some(n => n > 2); // true
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 跳过迭代
// continue:跳过当前迭代
for (const num of numbers) {
if (num === 3) continue;
console.log(num); // 1, 2, 4, 5
}
// filter:过滤掉不需要的
const filtered = numbers.filter(n => n !== 3);
// [1, 2, 4, 5]
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 方法对比
| 方法 | break | continue | 返回值 | 适用场景 |
|---|---|---|---|---|
for / for...of | ✅ | ✅ | - | 需要精确控制 |
forEach | ❌ | ❌ | undefined | 遍历所有元素 |
map | ❌ | - | 新数组 | 转换所有元素 |
filter | ❌ | - | 新数组 | 过滤元素 |
find | 自动 | - | 元素/undefined | 查找单个元素 |
some | 自动 | - | boolean | 检查是否存在 |
every | 自动 | - | boolean | 检查是否全满足 |
reduce | ❌ | - | 累计值 | 累计计算 |
注意
forEach 中使用 return 只会跳过当前回调,不会终止循环!如需终止,请使用 for...of。
# 5. TypeScript 类型技巧
# 5.1 keyof 类型约束
场景:确保参数只能是对象的已有属性名。
// 基本用法
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = {
name: 'Alice',
age: 25,
email: '[email protected]'
};
const name = getProperty(user, 'name'); // ✅ 'Alice'
const age = getProperty(user, 'age'); // ✅ 25
// const wrong = getProperty(user, 'salary'); // ❌ 编译错误
// 实用示例:类型安全的属性访问
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const result = {} as Pick<T, K>;
keys.forEach(key => {
result[key] = obj[key];
});
return result;
}
const picked = pick(user, ['name', 'age']);
// { name: 'Alice', age: 25 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 5.2 泛型约束
// 约束泛型必须有特定属性
interface HasId {
id: number;
}
function findById<T extends HasId>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id);
}
// 使用
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
const user = findById(users, 1); // ✅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 5.3 索引签名(Index Signature)
// 定义键值对类型
interface StringMap {
[key: string]: string;
}
const map: StringMap = {
name: 'Alice',
email: '[email protected]'
};
// 数字索引
interface RSSCache {
[tabId: number]: {
pageRSSHub: string[];
websiteRSSHub: string[];
pageRSS: string[];
};
}
const cache: RSSCache = {
1: { pageRSSHub: [], websiteRSSHub: [], pageRSS: [] },
2: { pageRSSHub: [], websiteRSSHub: [], pageRSS: [] }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 5.4 函数参数类型定义
// 方式1:直接解构参数
function UserCard({ name, age, email }: {
name: string;
age: number;
email?: string;
}) {
// 实现...
}
// 方式2:先定义类型(推荐)
interface UserCardProps {
name: string;
age: number;
email?: string;
}
function UserCard({ name, age, email }: UserCardProps) {
// 实现...
}
// 方式3:类型别名
type UserCardProps = {
name: string;
age: number;
email?: string;
};
function UserCard(props: UserCardProps) {
const { name, age, email } = props;
// 实现...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
对比:
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 方式1 | 简洁 | 类型不可复用 | 一次性组件 |
| 方式2 | 类型可复用,语义清晰 | 需要额外定义 | 推荐使用 |
| 方式3 | 参数对象完整,便于传递 | 需要手动解构 | 需要传递整个props |
# 6. 现代 JavaScript 特性
# 6.1 可选链(Optional Chaining)
// ❌ 传统写法
const userName = user && user.profile && user.profile.name;
// ✅ 使用可选链
const userName = user?.profile?.name;
// 数组中使用
const firstItem = arr?.[0];
// 函数调用
const result = obj.method?.();
// 组合使用
const value = data?.users?.[0]?.name ?? 'Unknown';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 6.2 空值合并(Nullish Coalescing)
// ❌ 使用 || 的问题
const count = input || 10; // 如果 input 是 0,结果也是 10
// ✅ 使用 ?? 只处理 null 和 undefined
const count = input ?? 10; // input 为 0 时,结果是 0
// 对比
console.log(0 || 10); // 10
console.log(0 ?? 10); // 0
console.log('' || 'default'); // 'default'
console.log('' ?? 'default'); // ''
console.log(false || true); // true
console.log(false ?? true); // false
console.log(null ?? 'default'); // 'default'
console.log(undefined ?? 'default'); // 'default'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 6.3 逻辑赋值运算符
// &&= : 仅当左侧为真值时赋值
let obj = { value: 10 };
obj.value &&= 20; // obj.value = 20
let obj2 = { value: 0 };
obj2.value &&= 20; // obj.value 仍然是 0
// ||= : 仅当左侧为假值时赋值
let config = {};
config.timeout ||= 5000; // config.timeout = 5000
// ??= : 仅当左侧为 null 或 undefined 时赋值
let settings = { retry: 0 };
settings.retry ??= 3; // settings.retry 仍然是 0(因为0不是null/undefined)
let settings2 = {};
settings2.timeout ??= 3000; // settings2.timeout = 3000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 7. 实用工具函数
# 7.1 URL 参数处理
// 定义返回类型
interface LinkParams {
author_uin?: string;
rank_type?: number;
rank_sub_type?: number;
}
// 辅助函数:安全转换为数字
function parseNumber(value: string | null): number | undefined {
if (value === null) return undefined;
const num = Number(value);
return isNaN(num) ? undefined : num;
}
// 提取URL参数
function extractLinkParam(params: URLSearchParams): LinkParams {
return {
author_uin: params.get('author_uin') || undefined,
rank_type: parseNumber(params.get('rank_type')),
rank_sub_type: parseNumber(params.get('rank_sub_type')),
};
}
// 使用示例
const url = new URL('https://example.com?author_uin=123&rank_type=1&rank_sub_type=abc');
const params = extractLinkParam(url.searchParams);
console.log(params); // { author_uin: '123', rank_type: 1 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
通用版本:
// 通用的URL参数解析器
function parseUrlParams<T extends Record<string, any>>(
params: URLSearchParams,
schema: {
[K in keyof T]: 'string' | 'number' | 'boolean';
}
): Partial<T> {
const result: Partial<T> = {};
for (const [key, type] of Object.entries(schema)) {
const value = params.get(key);
if (value === null) continue;
switch (type) {
case 'number':
const num = Number(value);
if (!isNaN(num)) result[key as keyof T] = num as T[keyof T];
break;
case 'boolean':
result[key as keyof T] = (value === 'true') as T[keyof T];
break;
default:
result[key as keyof T] = value as T[keyof T];
}
}
return result;
}
// 使用
interface QueryParams {
page: number;
size: number;
keyword: string;
active: boolean;
}
const parsed = parseUrlParams<QueryParams>(searchParams, {
page: 'number',
size: 'number',
keyword: 'string',
active: 'boolean'
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 7.2 数值处理
# 数值校准(边界吸附)
/**
* 数值校准函数
* @param value - 当前值
* @param low - 下限
* @param high - 上限
* @param delta - 吸附阈值
*/
function calibrateValue(value: number, low: number, high: number, delta: number): number {
if (value > high || Math.abs(value - high) < delta) return high;
if (value < low || Math.abs(value - low) < delta) return low;
return value;
}
// 使用示例
calibrateValue(50, 0, 100, 5); // 50
calibrateValue(98, 0, 100, 5); // 100(吸附到上限)
calibrateValue(3, 0, 100, 5); // 0(吸附到下限)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
应用场景:
- 滑块(Slider)边界处理
- 拖拽元素位置限制
- 音量/亮度控制
- 游戏角色移动边界
# 数值范围限制
// 简单的限制函数
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
clamp(50, 0, 100); // 50
clamp(-10, 0, 100); // 0
clamp(150, 0, 100); // 100
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 7.3 颜色处理
// 根据索引生成颜色
export function getColorByIndex(index: number): string {
const baseRed = 255;
const baseGreen = 255;
const baseBlue = 255;
const red = (baseRed + index * 15) % 256;
const green = (baseGreen + index * 10) % 256;
const blue = (baseBlue + index * 20) % 256;
const toHex = (n: number) => n.toString(16).padStart(2, '0');
return `#${toHex(red)}${toHex(green)}${toHex(blue)}`;
}
// 随机颜色生成
function randomColor(): string {
return `#${Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0')}`;
}
// RGB 转 HEX
function rgbToHex(r: number, g: number, b: number): string {
return `#${[r, g, b].map(x => x.toString(16).padStart(2, '0')).join('')}`;
}
// HEX 转 RGB
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 7.4 时间格式化
// 基本格式化
function formatTimestamp(date: Date = new Date()): string {
const pad = (n: number, len = 2) => String(n).padStart(len, '0');
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}`;
}
// 更灵活的格式化
function formatDate(date: Date, format: string): string {
const map: Record<string, string> = {
'YYYY': String(date.getFullYear()),
'MM': String(date.getMonth() + 1).padStart(2, '0'),
'DD': String(date.getDate()).padStart(2, '0'),
'HH': String(date.getHours()).padStart(2, '0'),
'mm': String(date.getMinutes()).padStart(2, '0'),
'ss': String(date.getSeconds()).padStart(2, '0'),
'SSS': String(date.getMilliseconds()).padStart(3, '0')
};
return format.replace(/YYYY|MM|DD|HH|mm|ss|SSS/g, matched => map[matched]);
}
// 使用
formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss'); // '2024-01-15 10:30:45'
formatDate(new Date(), 'YYYY年MM月DD日'); // '2024年01月15日'
formatDate(new Date(), 'HH:mm:ss.SSS'); // '10:30:45.123'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 7.5 防抖和节流
// 防抖(Debounce):延迟执行,如果在等待期间再次触发,重新计时
function debounce<T extends (...args: any[]) => any>(
func: T,
delay: number
): (...args: Parameters<T>) => void {
let timeoutId: NodeJS.Timeout;
return function(...args: Parameters<T>) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
// 节流(Throttle):限制执行频率
function throttle<T extends (...args: any[]) => any>(
func: T,
limit: number
): (...args: Parameters<T>) => void {
let inThrottle: boolean;
return function(...args: Parameters<T>) {
if (!inThrottle) {
func(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 使用示例
const debouncedSearch = debounce((query: string) => {
console.log('搜索:', query);
}, 300);
const throttledScroll = throttle(() => {
console.log('滚动事件');
}, 100);
// 监听事件
input.addEventListener('input', (e) => debouncedSearch(e.target.value));
window.addEventListener('scroll', throttledScroll);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 8. 环境和配置
# 8.1 环境变量
// Node.js 环境变量
process.env.NODE_ENV;
process.env.API_URL;
process.env.PORT;
// TypeScript 类型声明
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
API_URL: string;
PORT: string;
DATABASE_URL: string;
}
}
}
// 使用示例
const isDev = process.env.NODE_ENV === 'development';
const apiUrl = process.env.API_URL || 'http://localhost:3000';
const port = parseInt(process.env.PORT || '3000', 10);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 8.2 配置管理
// 配置合并
const defaultConfig = {
timeout: 5000,
retries: 3,
headers: {
'Content-Type': 'application/json'
}
};
const userConfig = {
timeout: 10000,
headers: {
'Authorization': 'Bearer token'
}
};
// 浅合并(headers 会被完全替换)
const config1 = { ...defaultConfig, ...userConfig };
// 深度合并(推荐)
function deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>): T {
const result = { ...target };
for (const key in source) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
result[key] = deepMerge(result[key] || {}, source[key]);
} else {
result[key] = source[key];
}
}
return result;
}
const config2 = deepMerge(defaultConfig, userConfig);
// headers 会被合并而不是替换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 9. 常见模式和最佳实践
# 9.1 对象属性简写
const name = 'Alice';
const age = 25;
// ❌ 传统写法
const user = {
name: name,
age: age
};
// ✅ 属性简写
const user = { name, age };
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 9.2 动态属性名
const key = 'dynamicKey';
const value = 'dynamicValue';
// 计算属性名
const obj = {
[key]: value,
[`${key}_suffix`]: 'another value'
};
// { dynamicKey: 'dynamicValue', dynamicKey_suffix: 'another value' }
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 9.3 参数默认值的最佳实践
// ❌ 不好:使用 || 可能导致问题
function createUser(name, age) {
age = age || 18; // 如果 age 是 0,也会被替换为 18
}
// ✅ 好:使用函数参数默认值
function createUser(name, age = 18) {
// age 只在 undefined 时使用默认值
}
// ✅ 好:使用 ?? 运算符
function createUser(name, age) {
age = age ?? 18; // 只在 null 或 undefined 时使用默认值
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 9.4 数组去重高级用法
// 基本去重
const unique = [...new Set(arr)];
// 对象数组去重(按某个属性)
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice' } // 重复
];
// 方法1:使用 Map
const uniqueUsers = Array.from(
new Map(users.map(user => [user.id, user])).values()
);
// 方法2:使用 filter
const uniqueUsers2 = users.filter((user, index, self) =>
self.findIndex(u => u.id === user.id) === index
);
// 方法3:使用 reduce
const uniqueUsers3 = users.reduce((acc, user) => {
if (!acc.find(u => u.id === user.id)) {
acc.push(user);
}
return acc;
}, []);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 9.5 对象属性筛选
// 筛选对象属性
function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
return keys.reduce((result, key) => {
if (key in obj) {
result[key] = obj[key];
}
return result;
}, {} as Pick<T, K>);
}
function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
const result = { ...obj };
keys.forEach(key => delete result[key]);
return result;
}
// 使用
const user = { id: 1, name: 'Alice', password: '123', email: '[email protected]' };
const publicUser = pick(user, ['id', 'name']); // { id: 1, name: 'Alice' }
const safeUser = omit(user, ['password']); // 不包含 password
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 10. 性能优化技巧
# 10.1 避免不必要的计算
// ❌ 在循环中重复计算
for (let i = 0; i < arr.length; i++) {
// arr.length 每次都会被访问
}
// ✅ 缓存长度
const len = arr.length;
for (let i = 0; i < len; i++) {
// 更高效
}
// ✅ 或使用 for...of
for (const item of arr) {
// 不需要索引时的最佳选择
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 10.2 对象属性访问优化
// ❌ 多次访问深层属性
if (user.profile.settings.theme === 'dark') {
console.log(user.profile.settings.theme);
}
// ✅ 解构一次
const { theme } = user.profile.settings;
if (theme === 'dark') {
console.log(theme);
}
// ✅ 使用可选链
const theme = user?.profile?.settings?.theme;
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 10.3 条件判断优化
// ❌ 多个 if 语句
if (status === 'pending') return 'yellow';
if (status === 'success') return 'green';
if (status === 'error') return 'red';
return 'gray';
// ✅ 使用对象映射
const statusColors = {
pending: 'yellow',
success: 'green',
error: 'red'
};
return statusColors[status] || 'gray';
// ✅ 使用 Map(类型更安全)
const statusColorMap = new Map([
['pending', 'yellow'],
['success', 'green'],
['error', 'red']
]);
return statusColorMap.get(status) || 'gray';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 总结
本文档收集了 JavaScript/TypeScript 开发中的实用技巧和代码片段,涵盖:
- 解构赋值 - 对象、数组、函数参数解构技巧
- 扩展运算符 - 对象合并、数组合并、条件属性
- 拷贝技术 - 浅拷贝、深拷贝、选择建议
- 数组操作 - 函数式方法、循环控制、实用技巧
- TypeScript - 泛型、keyof、索引签名、类型定义
- 现代特性 - 可选链、空值合并、逻辑赋值
- 工具函数 - URL处理、数值处理、颜色、时间、防抖节流
- 性能优化 - 避免重复计算、优化属性访问、条件判断
学习建议:
- 🎯 理解每个技巧的适用场景
- 💡 注意代码的可读性和可维护性
- ⚡ 关注性能但不要过度优化
- 🔍 注意常见陷阱(如对象展开顺序、浅拷贝问题)
记住:简洁不等于晦涩,写代码时要平衡简洁性和可读性!
# 参考资源
上次更新: 2025/10/14, 17:25:25