Jacky's blog
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)

Jack Yang

编程; 随笔
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)
  • web
  • web concept
  • javascript

    • javascript 入门指南
    • JavaScript 实用代码片段
      • 1. 解构赋值技巧
        • 1.1 对象解构
        • 精简对象赋值
        • 设置默认值
        • 提取剩余属性
        • 1.2 数组解构
        • 1.3 函数参数解构
      • 2. 扩展运算符应用
        • 2.1 对象合并
        • 2.2 数组合并
        • 2.3 条件属性添加
      • 3. 对象和数组拷贝
        • 3.1 浅拷贝
        • 对象浅拷贝
        • 数组浅拷贝
        • 3.2 深拷贝
        • 方法1:JSON 序列化(适用简单场景)
        • 方法2:结构化克隆(现代浏览器)
        • 方法3:自定义递归(完整版)
        • 方法4:第三方库
        • 3.3 选择建议
      • 4. 数组操作技巧
        • 4.1 函数式方法
        • 4.2 实用技巧
        • 4.3 循环控制
        • 提前终止
        • 跳过迭代
        • 方法对比
      • 5. TypeScript 类型技巧
        • 5.1 keyof 类型约束
        • 5.2 泛型约束
        • 5.3 索引签名(Index Signature)
        • 5.4 函数参数类型定义
      • 6. 现代 JavaScript 特性
        • 6.1 可选链(Optional Chaining)
        • 6.2 空值合并(Nullish Coalescing)
        • 6.3 逻辑赋值运算符
      • 7. 实用工具函数
        • 7.1 URL 参数处理
        • 7.2 数值处理
        • 数值校准(边界吸附)
        • 数值范围限制
        • 7.3 颜色处理
        • 7.4 时间格式化
        • 7.5 防抖和节流
      • 8. 环境和配置
        • 8.1 环境变量
        • 8.2 配置管理
      • 9. 常见模式和最佳实践
        • 9.1 对象属性简写
        • 9.2 动态属性名
        • 9.3 参数默认值的最佳实践
        • 9.4 数组去重高级用法
        • 9.5 对象属性筛选
      • 10. 性能优化技巧
        • 10.1 避免不必要的计算
        • 10.2 对象属性访问优化
        • 10.3 条件判断优化
      • 总结
      • 参考资源
  • css

  • vue

  • react

  • nextjs

  • module

  • web faq
  • web3

  • more

  • 《web》
  • javascript
Jacky
2023-08-25
目录

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
方式 优点 缺点
写法一 代码最简洁 可能引入不需要的属性
写法二 明确指定需要的属性 代码略长但更清晰

# 设置默认值

场景:为可能不存在的属性设置默认值。

// 解构时设置默认值
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

注意

  1. 默认值只在属性为 undefined 时生效,对 null 不生效
  2. 对象展开的顺序很重要:后面的属性会覆盖前面的
// 默认值的陷阱
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

# 提取剩余属性

场景:在不修改原对象的情况下"删除"某些属性。

// 删除单个属性
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

# 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

# 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. 扩展运算符应用

# 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.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 条件属性添加

// 只在值合法时添加属性
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

# 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

⚠️ 浅拷贝的陷阱:

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

# 数组浅拷贝

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

性能对比:

方法 性能 可读性 推荐度
[...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

⚠️ 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

# 方法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

# 方法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

# 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

# 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

# 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

# 跳过迭代

// 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

# 方法对比

方法 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

# 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

# 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

# 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

对比:

方式 优点 缺点 适用场景
方式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

# 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

# 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

# 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

通用版本:

// 通用的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

# 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

应用场景:

  • 滑块(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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 总结

本文档收集了 JavaScript/TypeScript 开发中的实用技巧和代码片段,涵盖:

  1. 解构赋值 - 对象、数组、函数参数解构技巧
  2. 扩展运算符 - 对象合并、数组合并、条件属性
  3. 拷贝技术 - 浅拷贝、深拷贝、选择建议
  4. 数组操作 - 函数式方法、循环控制、实用技巧
  5. TypeScript - 泛型、keyof、索引签名、类型定义
  6. 现代特性 - 可选链、空值合并、逻辑赋值
  7. 工具函数 - URL处理、数值处理、颜色、时间、防抖节流
  8. 性能优化 - 避免重复计算、优化属性访问、条件判断

学习建议:

  • 🎯 理解每个技巧的适用场景
  • 💡 注意代码的可读性和可维护性
  • ⚡ 关注性能但不要过度优化
  • 🔍 注意常见陷阱(如对象展开顺序、浅拷贝问题)

记住:简洁不等于晦涩,写代码时要平衡简洁性和可读性!

# 参考资源

  • MDN Web Docs (opens new window)
  • TypeScript 官方文档 (opens new window)
  • JavaScript.info (opens new window)
  • 代码仓库 (opens new window)
#JavaScript#TypeScript#snippet#代码技巧
上次更新: 2025/10/14, 17:25:25
javascript 入门指南
css 入门指南

← javascript 入门指南 css 入门指南→

最近更新
01
npx 使用指南
10-12
02
cursor
09-28
03
inspect
07-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 Jacky | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式