需求:根据路径字符串(如'a[3].b')获取数据中对应的值,无对应值时返回默认值。
实现思路:
- 解析路径字符串,转换为可遍历的键数组(如
'a[3].b'→['a', '3', 'b']);
- 循环遍历键数组,逐级访问数据,若中间某级不存在则返回默认值;
- 处理数组索引(字符串数字)与对象属性的访问差异。
const get = (data, path, defaultValue = void 0) => {
// 解析路径为数组(处理 . 和 [] 分隔)
const keys = path.replace(/\[(\d+)\]/g, '.$1').split('.').filter(k => k);
let result = data;
for (const key of keys) {
// 若当前结果不存在,直接返回默认值
if (result === null || result === undefined) return defaultValue;
// 访问当前键(支持对象和数组)
result = result[key];
}
// 最终结果不存在时返回默认值
return result ?? defaultValue;
};
需求:实现函数柯里化,支持add(1)(2)(3)或add(1,2)(3)等调用方式,返回累加结果。
const curry = (fn) => {
const argLength = fn.length; // 原函数参数长度
return function curried(...args) {
// 若参数足够,执行原函数;否则返回新函数继续接收参数
return args.length >= argLength
? fn(...args)
: (...rest) => curried(...args, ...rest);
};
};
// 使用:
const add = curry((a, b, c) => a + b + c);
console.log(add(1)(2)(3)); // 6
console.log(add(1, 2)(3)); // 6
const curry = (fn) => {
return function curried(...args) {
// 若调用时无参数,执行累加;否则继续收集参数
if (args.length === 0) {
return fn(...args);
} else {
return (...rest) => curried(...args, ...rest);
}
};
};
// 使用:
const add = curry((...args) => args.reduce((a, b) => a + b, 0));
console.log(add(1)(2)(3)()); // 6
console.log(add(1, 2)(3, 4)()); // 10
核心原理:async函数本质是返回 Promise 的语法糖,可基于 Generator 函数 + 自动执行器模拟,await对应yield的 Promise 处理。
// 自动执行Generator函数
function runGenerator(gen) {
const iterator = gen();
return new Promise((resolve, reject) => {
function step(nextValue) {
const { value: promise, done } = iterator.next(nextValue);
if (done) {
return resolve(promise); // 迭代完成,返回最终结果
}
// 处理yield的Promise
Promise.resolve(promise).then(
res => step(res), // 成功:将结果传入下一次迭代
err => reject(err) // 失败:直接reject
);
}
step(); // 启动迭代
});
}
// 模拟async函数
const async = (fn) => {
return (...args) => runGenerator(() => fn(...args));
};
// 使用示例:
const fetchData = (val) => new Promise(resolve => setTimeout(() => resolve(val), 100));
const test = async(function* () {
const a = yield fetchData(1);
const b = yield fetchData(2);
return a + b;
});
test().then(console.log); // 3(100ms后)
- 利用构建工具:启用
DOCKER_BUILDKIT,支持并行构建和缓存优化,同时可查看各指令耗时;
- 基于历史镜像:
FROM YOUR_OLD_DOCK,复用已有依赖层,减少重复构建;
- 最大化缓存:
COPY指令按变更频率排序(如先复制package.json安装依赖,再复制代码),避免频繁失效;
- 精简基础镜像:使用
alpine版本(体积仅几 MB),替代完整版 OS;
- 减少依赖体积:
npm i --only=production,移除开发依赖(devDependencies);
- 静态资源分离:将前端资源、静态文件等抽离至 CDN,避免打包进镜像。
核心流程:
- 内存文件系统:webpack 编译结果存于内存(而非磁盘),加速读写;
- 监听变化:通过
watch模式监控文件修改,触发增量编译(仅更新变化模块);
- 服务器通信:devServer 通过 WebSocket 向浏览器发送更新通知(包含
hash和chunkId);
- 浏览器更新:
- 下载更新清单(
hash.hot-update.json)和更新模块(hash.hot-update.js);
- 调用
hotAddUpdateChunk替换旧模块;
- 触发
hotUpdateDownloaded完成更新,不刷新页面。
优化思考:
- 缩小监听范围(排除
node_modules等无关目录);
- 启用
cache缓存编译结果,减少重复编译;
- 拆分
chunk,降低每次更新的模块体积;
- 使用
webpack-dev-middleware结合koa/express,简化服务端通信逻辑。
两者性能差异极小,主要区别在于使用场景:
obj.a.b.c是静态属性访问,JS 引擎可在编译时优化(如预解析属性地址),理论上略快;
obj['a']['b']['c']支持动态属性名(如obj[var]),灵活性更高,但引擎无法提前优化;
- 实际开发中,性能差异可忽略,优先根据是否需要动态属性选择写法。
- 分片存储:将数据拆分到多个
localStorage键(如key_1、key_2),总容量接近限制总和(通常 5MB×N);
- 结合 IndexedDB:IndexedDB 容量更大(一般无硬性限制),可作为主要存储,
localStorage辅助;
- 服务器存储:敏感 / 大量数据存于服务端,客户端仅存令牌(如 JWT),通过接口获取;
- Cookie 辅助:虽容量小(4KB),但可配合
localStorage分担部分数据(不推荐存储大量数据)。
问题:计算将word1转换为word2的最少操作次数(插入、删除、替换)。
解法:动态规划(DP)
- 定义
dp[i][j]:word1[0..i-1]转换为word2[0..j-1]的最少操作数;
- 初始化:
dp[0][j] = j(插入j次),dp[i][0] = i(删除i次);
- 状态转移:
- 若
word1[i-1] === word2[j-1],则dp[i][j] = dp[i-1][j-1](无需操作);
- 否则,
dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])(删除、插入、替换的最小值 + 1)。
const levenshtein = (s1, s2) => {
const m = s1.length, n = s2.length;
// 初始化DP表
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
for (let i = 0; i <= m; i++) dp[i][0] = i;
for (let j = 0; j <= n; j++) dp[0][j] = j;
// 填充DP表
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (s1[i - 1] === s2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1 + Math.min(
dp[i - 1][j], // 删除
dp[i][j - 1], // 插入
dp[i - 1][j - 1] // 替换
);
}
}
}
return dp[m][n];
};
与 React 的联系:React 的虚拟 DOM Diff 算法需计算两棵树的最小更新成本,核心思想与编辑距离类似(通过层级对比、列表 key 优化,减少操作次数)。
- 清晰:结构明确,关键信息(姓名、经验、岗位)一眼可见;
- 简短:控制在 1-2 页,避免冗余内容;
- 定制化:针对岗位 JD 调整重点(如强调匹配的技术栈和项目经验)。
-
标题与格式:
- 标题需含姓名、学历、经验、岗位(如:“张三 - 本科 - 4 年 - 高级前端开发工程师.pdf”);
- 采用 PDF 格式,避免花哨设计(便于 OCR 识别和跨设备查看)。
-
个人介绍:
简洁列出联系方式(电话、邮箱)、意向地、岗位、学历,排版整齐。
-
技术部分:
- 避免罗列常识(如 “熟练使用 Git”)或不精通的领域;
- 措辞准确:
- 了解:知晓基本原理;
- 熟悉:有 Demo 或 1-2 个项目实践;
- 熟练掌握:工业级项目中灵活应用核心原理;
- 精通:深度研究,能解决复杂问题。
-
项目部分(占比 60%+):
按STAR 原则描述(情境 - 任务 - 行动 - 结果):
- 坏案例:“负责 XXX 需求,使用 A、B 技术”;
- 好案例:“XXX 项目中,因 [问题] 导致 [影响],我作为 [角色],通过 [技术方案] 解决,使 [指标] 提升 X%,[业务] 增长 Y%”。
-
加分项:
- 高质量技术博客(高转发 / 点赞);
- Github 活跃贡献(主导开源项目、代码级 PR);
- 跨领域技能(如 Rust 开发 Wasm、C++ 等)。