JavaScript 核心知识笔记
本文档整合了 Promise 异步、数组/对象核心方法、函数式编程、异步并发控制、不可变数据更新等企业级必备技巧。内容全面,示例丰富,可直接用于开发参考。
一、Promise 与异步基础
1.1 Promise 核心
// 创建异步任务
new Promise((resolve, reject) => { /* 异步操作 */ });
// 处理结果
promise.then(onFulfilled).catch(onRejected);
// 快速创建已决议的 Promise
Promise.resolve(value);
Promise.reject(error);
// 并发控制
Promise.all([p1, p2]) // 全部成功才成功,返回结果数组
Promise.race([p1, p2]) // 第一个完成的决定结果
Promise.allSettled() // 等待全部结束,返回每个的状态
Promise.any() // 第一个成功的决定结果
1.2 async/await 优雅语法
async function fetchData() {
try {
const result = await somePromise();
// 直接拿到结果,不用写 then
} catch (err) {
// 错误处理
}
}
// await 就是等 Promise 干完活,直接拿结果
二、数组核心处理函数(必会)
2.1 every – 全真才为真
// 表单校验:所有字段都通过校验
const formFields = [
{ name: 'username', valid: true },
{ name: 'email', valid: true },
{ name: 'phone', valid: false }
];
const isFormValid = formFields.every(field => field.valid);
// 场景:整体校验、权限全满足、批量数据合规检查
2.2 some – 一真即为真
const tasks = [
{ id: 1, name: '写文档', done: true },
{ id: 2, name: '改Bug', done: false }
];
const hasPendingTask = tasks.some(task => !task.done); // true
// 搜索命中
const keywords = '张三';
const hasMatch = users.some(user => user.name.includes(keywords));
2.3 filter – 筛选过滤(生成新数组)
const strings = ["苹果", "", "香蕉", " ", "橙子", null];
const validStrings = strings.filter(s => s && s.trim());
// 场景:数据筛选、清理无效项、数据分类
2.4 map – 一对一映射
// 字符串转数字
const strNums = ['1', '2', '3'];
const numbers = strNums.map(Number); // [1,2,3]
// 接口字段适配
const apiData = [{ user_name: '张三', user_age: 25 }];
const frontData = apiData.map(item => ({
name: item.user_name,
age: item.user_age
}));
2.5 reduce – 万能累加器
核心:把多个元素汇总成一个结果。必须返回累加器,必须传初始值!
// 求和
const sum = [1,2,3,4].reduce((acc, curr) => acc + curr, 0);
// 分组统计
const orderItems = [
{ category: '食品', price: 20 },
{ category: '服装', price: 100 },
{ category: '食品', price: 30 }
];
const totalByCategory = orderItems.reduce((acc, item) => {
acc[item.category] = (acc[item.category] || 0) + item.price;
return acc;
}, {}); // ← 初始值必须是 {}
// 场景:聚合统计、数据分组、数组转对象、函数式管道
2.6 find / findIndex – 精准查找
const users = [{ id: 1, name: '张三' }, { id: 2, name: '李四' }];
const user = users.find(u => u.id === 2); // 返回元素
const index = users.findIndex(u => u.id === 2); // 返回索引,找不到 -1
// 与 indexOf 区别:支持复杂条件判断
2.7 flat & flatMap – 数组扁平化
const nested = [1, [2, [3, [4]]], 5];
nested.flat(Infinity); // [1,2,3,4,5]
// flatMap = map + flat(1)
const lines = ["Hello world", "JavaScript is good"];
const words = lines.flatMap(line => line.split(' '));
// ["Hello", "world", "JavaScript", "is", "good"]
2.8 includes – 是否包含
const roles = ['admin', 'editor'];
const isAdmin = roles.includes('admin');
2.9 sort – 排序(会修改原数组!)
const nums = [3,1,2];
// 不修改原数组的安全写法
const sorted = [...nums].sort((a,b) => a - b); // 升序
const desc = [...nums].sort((a,b) => b - a); // 降序
2.10 reverse – 反转(会修改原数组)
const arr = [1,2,3];
const reversed = [...arr].reverse(); // [3,2,1]
2.11 slice – 截取(不改变原数组)
const arr = [1,2,3,4];
arr.slice(1); // [2,3,4] 从索引1开始
arr.slice(1,3); // [2,3]
2.12 splice – 删除/插入/替换(改变原数组)
const arr = [1,2,3];
arr.splice(1, 1); // 从索引1删除1个 → [1,3]
arr.splice(1, 0, 99); // 插入 → [1,99,3]
三、对象核心处理函数
3.1 遍历三剑客:keys / values / entries
const obj = { a:1, b:2, c:3 };
Object.keys(obj); // ['a','b','c']
Object.values(obj); // [1,2,3]
Object.entries(obj); // [['a',1], ['b',2], ['c',3]]
// 遍历替代 for...in,方便使用数组方法
Object.entries(obj).forEach(([key, value]) => {
console.log(key, value);
});
3.2 Object.fromEntries – 键值对转回对象
// 过滤对象属性(只保留 name 和 age)
const obj = { name: '张三', age: 25, gender: '男' };
const picked = Object.fromEntries(
Object.entries(obj).filter(([k]) => ['name','age'].includes(k))
);
// { name:'张三', age:25 }
// 对象转 URL 参数
const query = { name: '张三', age: 25 };
const search = new URLSearchParams(query).toString(); // name=张三&age=25
3.3 Object.assign – 浅拷贝与合并
const obj1 = { a:1 };
const obj2 = { b:2 };
const merged = Object.assign({}, obj1, obj2); // { a:1, b:2 }
// 等价于 { ...obj1, ...obj2 }
3.4 可选链 ?. 与空值合并 ??
// 安全访问深层属性
const user = { profile: { name: '张三' } };
console.log(user?.profile?.name); // '张三'
console.log(user?.address?.city); // undefined(不报错)
// 空值合并:只有 null/undefined 才用默认值
const count = 0;
console.log(count ?? 10); // 0(不是 null/undefined,保留0)
console.log(count || 10); // 10(因为0是假值,不推荐)
四、函数式编程高级技巧
4.1 函数组合(Pipe / Compose)
将多个函数串联成数据流水线,代码线性、可读性强。
// 从左到右执行(pipe,更符合人类阅读习惯)
const pipe = (...fns) => (initValue) =>
fns.reduce((acc, fn) => fn(acc), initValue);
// 从右到左执行(compose,经典函数式)
const compose = (...fns) => (initValue) =>
fns.reduceRight((acc, fn) => fn(acc), initValue);
// 实战:处理商品数据
const filterValid = (list) => list.filter(item => item.price > 0 && item.status === 1);
const mapField = (list) => list.map(({ goods_id: id, goods_name: name, price }) => ({ id, name, price }));
const sortByPrice = (list) => [...list].sort((a, b) => a.price - b.price);
const groupByPriceRange = (list) => list.reduce((acc, item) => {
const range = item.price < 100 ? '低价' : item.price < 500 ? '中价' : '高价';
acc[range] = [...(acc[range] || []), item];
return acc;
}, {});
const processGoodsData = pipe(filterValid, mapField, sortByPrice, groupByPriceRange);
const result = processGoodsData(apiData);
4.2 柯里化(Currying)
将多参数函数拆分成单参数函数链,实现参数预置。
const curry = (fn) => (...args) =>
args.length >= fn.length ? fn(...args) : curry(fn).bind(null, ...args);
// 用法示例
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
const add5 = curriedAdd(5);
console.log(add5(3, 2)); // 10
console.log(curriedAdd(1)(2)(3)); // 6
五、异步并发控制(手写限流调度器)
解决批量请求并发过高导致崩溃的问题。类似银行柜台:同时最多 limit 个任务在处理,其余排队。
/**
* 异步并发限流
* @param {Array<Function>} tasks 任务函数数组(每个任务返回 Promise)
* @param {number} limit 最大并发数
* @returns {Promise<any[]>} 所有任务结果数组(顺序与原任务一致)
*/
const asyncPool = async (tasks, limit = 3) => {
const results = []; // 存储所有结果(保持顺序)
const running = []; // 存储正在执行的任务 Promise
for (let i = 0; i < tasks.length; i++) {
const task = tasks[i];
// 包装任务,确保它是一个 Promise
const promise = Promise.resolve().then(() => task());
results.push(promise); // 占位,顺序不变
// 任务完成后从 running 中移除
const finishPromise = promise.then(() => {
const index = running.indexOf(finishPromise);
if (index !== -1) running.splice(index, 1);
});
running.push(finishPromise);
// 当并发数达到上限,等待任意一个任务完成
if (running.length >= limit) {
await Promise.race(running);
}
}
// 等待所有任务完成
return Promise.all(results);
};
// 使用示例
const createTask = (id, delay) => () => new Promise(resolve => {
setTimeout(() => {
console.log(`任务${id}完成`);
resolve(id);
}, delay);
});
const tasks = [
createTask(1, 1000),
createTask(2, 500),
createTask(3, 800),
createTask(4, 200),
createTask(5, 600)
];
asyncPool(tasks, 2).then(results => console.log('全部结果', results));
// 同时最多2个任务在执行
六、复杂数据处理(树形结构)
6.1 扁平数据转树形(菜单/部门)
// 扁平数组,每个元素有 id, parentId
const flatList = [
{ id: 1, parentId: null, name: '根节点' },
{ id: 2, parentId: 1, name: '子节点A' },
{ id: 3, parentId: 1, name: '子节点B' },
{ id: 4, parentId: 2, name: '孙节点A1' }
];
function listToTree(list, parentId = null) {
return list
.filter(item => item.parentId === parentId)
.map(item => ({ ...item, children: listToTree(list, item.id) }));
}
const tree = listToTree(flatList);
6.2 树形数据过滤(保留符合条件的节点及其祖先)
function filterTree(tree, predicate) {
return tree
.filter(node => predicate(node) || (node.children && filterTree(node.children, predicate).length))
.map(node => ({ ...node, children: node.children ? filterTree(node.children, predicate) : [] }));
}
6.3 树形查找(findNode)
function findNode(tree, id) {
for (const node of tree) {
if (node.id === id) return node;
if (node.children) {
const found = findNode(node.children, id);
if (found) return found;
}
}
return null;
}
七、性能优化与不可变数据
7.1 记忆化(Memoization)
缓存函数执行结果,相同参数直接返回缓存,避免重复计算。
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// 示例:斐波那契数列
const fib = memoize(n => {
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
});
console.log(fib(40)); // 速度极快
7.2 不可变数据更新(Immutable)
铁律:绝不修改原始对象/数组,所有修改都生成全新的对象/数组。
// 对象深层属性更新
const state = { user: { name: '张三', age: 25 }, loading: false };
const newState = {
...state,
user: { ...state.user, age: 26 } // 只修改 age
};
// 数组修改(map)
const todos = [{ id: 1, done: false }, { id: 2, done: true }];
const toggled = todos.map(todo =>
todo.id === 1 ? { ...todo, done: true } : todo
);
// 数组删除(filter)
const withoutId1 = todos.filter(todo => todo.id !== 1);
// 数组新增(展开运算符)
const newTodo = { id: 3, done: false };
const added = [...todos, newTodo];
// 总结:
// - 改深层对象 → 用 ... 逐层展开
// - 改数组元素 → 用 map
// - 删数组元素 → 用 filter
// - 加数组元素 → 用 [...arr, newItem]
7.3 深拷贝方法
// 方法1:structuredClone(现代浏览器/Node 17+)
const clone = structuredClone(originalObject);
// 方法2:JSON 序列化(仅限纯数据,不含函数/undefined/循环引用)
const clone = JSON.parse(JSON.stringify(original));
// 方法3:Lodash(工业级最稳)
// const clone = _.cloneDeep(original);
八、全局错误捕获与边界
8.1 未捕获的 Promise 错误
window.addEventListener('unhandledrejection', event => {
console.error('未处理的 Promise 错误:', event.reason);
// 可上报日志或提示用户
});
8.2 同步代码错误捕获
window.addEventListener('error', event => {
console.error('全局错误:', event.error);
// 避免默认行为(如控制台报错仍保留)
// return true 可阻止默认处理
});
8.3 异步函数的错误边界包装
// 高阶函数,自动捕获 async 函数错误
const safeAsync = (fn) => async (...args) => {
try {
return await fn(...args);
} catch (err) {
console.error('异步函数错误:', err);
// 可返回默认值或 throw
return null;
}
};
// 使用
const fetchUser = safeAsync(async (id) => {
const res = await fetch(`/user/${id}`);
return res.json();
});
九、常用实战代码片段集锦
9.1 数组去重
const unique = [...new Set([1,2,2,3])]; // [1,2,3]
9.2 对象数组按字段去重
const uniqueByKey = (arr, key) => {
const map = new Map();
return arr.filter(item => !map.has(item[key]) && map.set(item[key], true));
};
9.3 批量重命名字段(解构 + 剩余运算符)
const renameKeys = (obj, keyMap) =>
Object.fromEntries(
Object.entries(obj).map(([k, v]) => [keyMap[k] || k, v])
);
// 示例
const user = { user_id: 1, user_name: '张三' };
const renamed = renameKeys(user, { user_id: 'id', user_name: 'name' });
// { id:1, name:'张三' }
9.4 防抖(Debounce)与节流(Throttle)
// 防抖:连续触发只执行最后一次(搜索输入)
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 节流:固定间隔执行一次(滚动/resize)
function throttle(fn, interval) {
let last = 0;
return function(...args) {
const now = Date.now();
if (now - last >= interval) {
last = now;
fn.apply(this, args);
}
};
}
十、总结速查表
| 需求 | 方法 |
|---|
| 判断全部满足 | every |
| 判断至少一个满足 | some |
| 筛选数据 | filter |
| 转换数据 | map |
| 查找第一个元素 | find / findIndex |
| 汇总统计 | reduce |
| 数组去重 | [...new Set(arr)] |
| 深拷贝 | structuredClone / JSON / lodash |
| 对象遍历 | keys / values / entries |
| 安全访问嵌套属性 | ?. |
| 空值默认 | ?? |
| 函数组合流水线 | pipe / compose |
| 参数预置 | curry |
| 异步限流 | 手写 asyncPool |
| 扁平转树 | 递归 filter + map |
| 不可变更新 | ... 展开、map、filter |
| 性能缓存 | memoize |
最后叮嘱:Promise 是异步的地基,async/await 是漂亮的房子;数组/对象方法是日常开发的利剑;函数式组合让代码像诗一样流畅。熟记这些,足以应对大部分企业级开发场景。
评论