26~45 【高频 · 基础掌握】
Promise.allSettled | 生/熟/秒:
全部 settle 才 resolve,结果数组每项 {status, value|reason}。不短路,和 all 不同。
function promiseAllSettled(promises) {
return Promise.all(
promises.map(p =>
Promise.resolve(p).then(
value => ({ status: 'fulfilled', value }),
reason => ({ status: 'rejected', reason })
)
)
);
}sleep | 生/熟/秒:
new Promise + setTimeout 一行封装延迟。
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// await sleep(1000);千分位格式化 | 生/熟/秒:
正则从后往前每三位加逗号;或 toLocaleString。
function formatThousands(num) {
const [int, dec] = String(num).split('.');
const intFmt = int.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return dec ? `${intFmt}.${dec}` : intFmt;
}
// formatThousands(1234567.89) => "1,234,567.89"URL解析 | 生/熟/秒:
优先 URL API;手写则 split 协议、host、pathname、search、hash。
function parseUrl(href) {
const u = new URL(href);
const query = {};
u.searchParams.forEach((v, k) => { query[k] = v; });
return {
protocol: u.protocol,
host: u.host,
pathname: u.pathname,
search: u.search,
hash: u.hash,
query,
};
}时间格式化 | 生/熟/秒:
补零函数 + 取年月日时分秒拼接。注意月份 +1。
function formatDate(date = new Date(), tpl = 'YYYY-MM-DD HH:mm:ss') {
const pad = n => String(n).padStart(2, '0');
const map = {
YYYY: date.getFullYear(),
MM: pad(date.getMonth() + 1),
DD: pad(date.getDate()),
HH: pad(date.getHours()),
mm: pad(date.getMinutes()),
ss: pad(date.getSeconds()),
};
return tpl.replace(/YYYY|MM|DD|HH|mm|ss/g, m => map[m]);
}Omit/Pick(JS+TS类型) | 生/熟/秒:
Pick 选 key 子集;Omit = 解构去掉不要的 key。TS 用 Pick<T,K> / Omit<T,K>。
function pick(obj, keys) {
return keys.reduce((acc, k) => (k in obj && (acc[k] = obj[k]), acc), {});
}
function omit(obj, keys) {
const set = new Set(keys);
return Object.keys(obj).reduce((acc, k) => (set.has(k) ? acc : (acc[k] = obj[k], acc)), {});
}type PickUser = Pick<User, 'id' | 'name'>;
type OmitUser = Omit<User, 'password'>;文字截断 | 生/熟/秒:
超长加 …,按字符长度 slice;中英文混排可换 Intl.Segmenter(了解即可)。
function truncate(str, max, suffix = '…') {
if (str.length <= max) return str;
return str.slice(0, max - suffix.length) + suffix;
}隐藏元素的方式 | 生/熟/秒:
display:none 不占位;visibility:hidden 占位不可点;opacity:0 占位可点(需 pointer-events:none);position 移出视口;height:0;overflow:hidden。
.hide-a { display: none; }
.hide-b { visibility: hidden; }
.hide-c { opacity: 0; pointer-events: none; }
.hide-d { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }CSS画三角形 | 生/熟/秒:
宽高 0 + 三边 transparent + 一边有色。
.tri {
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-bottom: 12px solid #333; /* 朝下三角 */
}Counter计数器 | 生/熟/秒:
闭包保存 count,返回 inc/dec/get。
function createCounter(init = 0) {
let count = init;
return {
inc: (n = 1) => (count += n),
dec: (n = 1) => (count -= n),
get: () => count,
reset: () => (count = init),
};
}TodoList | 生/熟/秒:
状态数组 + id,增删改查过滤。面试写数据结构 + 纯函数即可。
let todos = [];
let id = 1;
function addTodo(text) {
todos.push({ id: id++, text, done: false });
}
function toggleTodo(id) {
const t = todos.find(x => x.id === id);
if (t) t.done = !t.done;
}
function removeTodo(id) {
todos = todos.filter(x => x.id !== id);
}CountDown倒计时 | 生/熟/秒:
先打一拍当前秒数,再 setInterval 每秒减一并回调,到 0 清定时器。要更准可用 setTimeout 链减漂移。
function countdown(seconds, onTick, onEnd) {
let left = seconds;
onTick(left);
const timer = setInterval(() => {
left--;
onTick(left);
if (left <= 0) {
clearInterval(timer);
onEnd && onEnd();
}
}, 1000);
return () => clearInterval(timer);
}模拟useState | 生/熟/秒:
数组存 state,cursor 记当前 hook 顺序,每次 render 重置 cursor。简版演示用模块级变量。
let stateSlot = [];
let cursor = 0;
function useState(initial) {
const c = cursor++;
if (stateSlot[c] === undefined) stateSlot[c] = typeof initial === 'function' ? initial() : initial;
const setState = (v) => {
stateSlot[c] = typeof v === 'function' ? v(stateSlot[c]) : v;
render(); // 触发重渲染
};
return [stateSlot[c], setState];
}
function render() { cursor = 0; /* 重新执行组件函数 */ }useDebounce防抖Hook | 生/熟/秒:
useEffect + useRef 存 timer,依赖变则防抖执行。
function useDebounce(value, delay) {
const [debounced, setDebounced] = React.useState(value);
React.useEffect(() => {
const t = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(t);
}, [value, delay]);
return debounced;
}useThrottle节流Hook | 生/熟/秒:
useRef 记上次时间或 trailing timer,useEffect 里包一层节流更新 state。
function useThrottle(value, interval) {
const [throttled, setThrottled] = React.useState(value);
const last = React.useRef(Date.now());
React.useEffect(() => {
const now = Date.now();
if (now - last.current >= interval) {
last.current = now;
setThrottled(value);
} else {
const t = setTimeout(() => {
last.current = Date.now();
setThrottled(value);
}, interval - (now - last.current));
return () => clearTimeout(t);
}
}, [value, interval]);
return throttled;
}Promise.any | 生/熟/秒:
任一 fulfilled 就 resolve;全 reject 才 reject(AggregateError)。与 race 区别:race 第一个 settle 含 reject。
function promiseAny(promises) {
if (!promises.length) return Promise.reject(new AggregateError([], 'All rejected'));
const errs = [];
let remaining = promises.length;
return new Promise((resolve, reject) => {
promises.forEach((p, i) => {
Promise.resolve(p).then(resolve, err => {
errs[i] = err;
if (--remaining === 0) reject(new AggregateError(errs, 'All rejected'));
});
});
});
}并发控制 | 生/熟/秒:
limit 个 worker 抢索引,各自 while 拉下一个 task,全部完成 Promise.all 收口。
async function parallelLimit(tasks, limit) {
const results = [];
let i = 0;
async function worker() {
while (i < tasks.length) {
const idx = i++;
results[idx] = await tasks[idx]();
}
}
const n = Math.min(limit, tasks.length) || 0;
await Promise.all(Array.from({ length: n }, worker));
return results;
}retry重试+超时控制 | 生/熟/秒:
循环尝试 catch 累加次数;超时用 Promise.race 包一层 reject。
async function withTimeout(promise, ms) {
let t;
const timeout = new Promise((_, rej) => { t = setTimeout(() => rej(new Error('timeout')), ms); });
try {
return await Promise.race([promise, timeout]);
} finally {
clearTimeout(t);
}
}
async function retry(fn, { times = 3, delay = 0 } = {}) {
let last;
for (let i = 0; i < times; i++) {
try { return await fn(); } catch (e) { last = e; if (delay) await new Promise(r => setTimeout(r, delay)); }
}
throw last;
}树转数组 | 生/熟/秒:
DFS 先序遍历 push,带 level 或 parentId 视题目;BFS 用队列。
function treeToList(root) {
const res = [];
function dfs(node) {
if (!node) return;
const { children, ...rest } = node;
res.push(rest);
(children || []).forEach(dfs);
}
dfs(root);
return res;
}二叉树遍历 | 生/熟/秒:
前中后序递归一行基线;非递归用栈模拟。层序用队列。
function preorder(root, acc = []) {
if (!root) return acc;
acc.push(root.val);
preorder(root.left, acc);
preorder(root.right, acc);
return acc;
}
function inorder(root, acc = []) {
if (!root) return acc;
inorder(root.left, acc);
acc.push(root.val);
inorder(root.right, acc);
return acc;
}
function levelOrder(root) {
if (!root) return [];
const q = [root], res = [];
while (q.length) {
const n = q.shift();
res.push(n.val);
if (n.left) q.push(n.left);
if (n.right) q.push(n.right);
}
return res;
}