202506
INFO
在张江度过了四年难忘的生活,现在因公司搬迁到静安,我随之搬到静安,有点不适应这边的老破小
0601
- 搬到静安阳曲路的第一天,过的非常不好,实现不习惯这边的老破小
0610
- 1462
- 被收购尘埃落定,接下来就是等待慢慢被边缘化,然后被干掉
0612
- 1464
- 如果知道自己两个月之后被干掉,如何做好前端面试?又要准备前端面试?真的是已经麻木了,会不会写都不重要,重要的是思路和眼界?
- 手写前端
//二分查找
function bindSearch(arr, target) {
let l = 0
let r = arr.length - 1
while (l <= r) {
let mid = (l + r) >> 1
if (arr[mid] == target) {
return mid
} else if (arr[mid] > target) {
r = mid - 1
} else {
l = mid + 1
}
}
}
// 10. 找出第一个不良版本
// This is a JavaScript coding problem from BFE.dev
/**
* @typedef {(version: number) => boolean} IsBad
*/
/**
* @param {IsBad} isBad
* @return {(v: number) => number}
*/
function firstBadVersion(isBad) {
// firstBadVersion receive a check function isBad
// and should return a closure which accepts a version number(integer)
return (version) => {
// write your code to return the first bad version
// if none found, return -1
}
}
- 前端老兵如何准备面试?
INFO
⚙️ 一、技术深度强化:查漏补缺与前沿融合
- 基础复盘与框架原理
核心基础:重点复习 JavaScript 闭包/原型链/事件循环、CSS 布局(Flex/Grid) 和 浏览器渲染机制(重绘/回流)。 框架底层:深入理解 React/Vue 的虚拟 DOM、Diff 算法、状态管理(Redux/Vuex),并能手写简易实现。 工程化:掌握 Webpack 优化(Tree Shaking、Code Splitting) 和 CI/CD 流程(如 Jenkins 部署)。 2. 性能优化实战
准备 可量化的优化案例:例如通过 懒加载、资源压缩、SSR 将首屏加载时间从 5s 降至 1.5s,使用 Chrome DevTools 或 Lighthouse 分析过程。 掌握 性能优化闭环:分析 → 定位瓶颈(如 JS 执行耗时)→ 实施优化 → 验证效果(Lighthouse 评分提升)。 3. 前沿技术结合
智能化工具:如 InsCode AI IDE 辅助代码生成与调试,展示技术敏感度。 跨端/新兴技术:了解 PWA 离线方案、WebAssembly 应用,或 微前端架构 的落地经验。
📂 二、项目经验包装:突出技术决策与业务价值 采用 STAR 法则 结构化表达项目,并聚焦以下维度:
难点突破: 例如解决 10W+ 数据列表渲染卡顿,采用 虚拟滚动(react-window) + Web Worker 拆分计算。 处理 老旧浏览器兼容,通过 PostCSS 自动前缀 + Polyfill 方案。 业务影响: 量化结果:如“优化后用户转化率提升 20%”或“错误率下降 40%(接入 Sentry)”。 工程化贡献: 基建建设:推动 组件库开发(节省 40% 重复代码) 或 自动化测试覆盖。 💡 提示:选择 3 个核心项目 深度准备,确保技术细节清晰,避免泛泛而谈。
🎯 三、面试全流程策略 阶段 关键动作 面试前 - 刷题:LeetCode 中等难度算法(侧重数组/字符串处理),牛客网前端专项题。
- 模拟面试:找同行模拟技术追问(如“React Fiber 如何中断任务?”)。 面试中 - 技术回答:先概括思路 → 分步实现 → 复杂度分析。
- 开放题:结合项目经验答(例:“从输入 URL 到页面显示”可关联性能优化实践)。 HR 面 - 薪资谈判:依据岗位预算(参考 Boss 直聘/脉脉薪资范围),强调技术落地能力。
- 文化匹配:提前调研公司技术栈(如是否重 Vue3),表达技术认同。
🚀 四、差异化竞争力:凸显资深角色价值 1.架构思维: 说明技术选型权衡(如 Vue3 vs React 的取舍依据)。 2.跨职能协作: 举例如何推动 后端定制 API 格式 或 与产品沟通需求合理性。 3.新人赋能: 分享 Code Review 经验 或 内部技术分享 案例,体现领导力。
💪 五、心态与资源准备 建立个人品牌:更新 GitHub(突出高质量项目)、技术博客(复盘难点解决方案)。 行业动态:关注 前端框架更新(如 React 19)、Web 生态变化(如 HTTP3 应用)。 心理建设:将裁员视为职业跃迁机会,聚焦长期成长。
📌 资源推荐:
知识体系构建:前端面试体系核心战略 高频题库:前端面试题全集 最后提醒:资深开发者的核心价值在于 技术深度 + 业务理解 + 工程效率 的结合。面试时保持自信,用数据和逻辑说话,你的经验正是团队最需要的稳定力量。
0613
- 1465
- 微前端框架(乾坤)实践下
- 如何下载一个固定地址?
URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的 URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。
function download(blob) {
const url = window.URL.createObjectURL(blob) // url
const a = document.createElement("a")
a.href = url
a.download = "test.doc" // 文件名
document.body.appendChild(a)
a.click()
}
0615
- 1467
- 手写 什么是 Composition?实现 pipe()
function pipe(fns) {
if (fns.length === 0) return (...arg) => arg
if (fns.length === 1) return fns[0]
// return fns.reduce((pre, cur) => {
// return cur(pre)
// }, Promise.resolve())
return function (arg) => {
return fns.reduce((pre, cur) => cur(pre), arg)
}
}
- 手写实现
memo()
const memoed = memo(func, () => "samekey")
memoed(1, 2)
// 3,func被调用,缓存key是 'samekey'
memoed(1, 2)
// 3,因为key是一样的,3被直接返回,func未调用
memoed(1, 3)
// function memo(fn, key = Array.from(arguments).join("_")) {
// let cache = new Map()
// return (...arg) => {
// let val = fn.apply(this, arg)
// if (cache.has(key)) {
// return cache.get(key)
// }
// cache.set(key, val)
// }
// }
function memo(fnc, resolver = (...arg) => arg.join("_")) {
let cache = new Map()
return function (...arg) {
let key = resolver(arg)
if (cache.has(key)) {
return cache.get(key)
}
let val = fnc.apply(this, arg)
cache.set(key, val)
return val
}
}
手写:实现一个 DOM element store
// wrong
class NodeStore {
let obj = {}
set(node, value) {
obj[JSON.stringfy(node)] = value
}
get(node) {
return obj[JSON.stringfy(node)]
}
has(node) {
if (obj[JSON.stringfy(node)]) {
return true
} else {
retun false
}
}
}
// ok
class NodeStore {
static nodes = {}
/**
* @param {Node} node
* @param {any} value
*/
set(node, value) {
node.__node_key_ = Symbol()
this.nodes[node.__node_key_] = value
}
/**
* @param {Node} node
* @return {any}
*/
get(node) {
return this.nodes[node.__node_key_]
}
/**
* @param {Node} node
* @return {Boolean}
*/
has(node) {
return !!this.nodes[node.__node_key_]
}
}
0616
- 1469
- redux 中的 Thunk 是啥
INFO
在前端开发中,Thunk 是一种用于处理异步操作的编程模式,尤其在 Redux 等状态管理库中被广泛使用。它的核心思想是将异步逻辑封装成一个函数(高阶函数),延迟执行并动态分发(dispatch)同步的 Action,从而解决 Redux 原生仅支持同步 Action 的限制。
- 定义 Thunk 是一个返回函数的函数(高阶函数),这个返回的函数接收 dispatch 和 getState 作为参数,允许在异步操作完成后手动触发 Action
// 普通 Action Creator(同步)
const syncAction = () => ({ type: "SYNC_ACTION" })
// Thunk Action Creator(异步)
const thunkAction = () => {
return (dispatch, getState) => {
// 异步操作(如 API 请求)
fetch("/api/data").then((data) => {
dispatch({ type: "DATA_LOADED", payload: data })
})
}
}
- 作用 解耦异步逻辑 :将异步代码从 React 组件中抽离,保持组件纯净。 动态控制流程 :支持条件分发 Action(例如根据异步结果决定后续操作)。 错误处理 :集中处理异步错误,避免在组件中重复编写 try/catch
二、Thunk 的典型应用场景 场景 1:异步数据获取(如 API 请求)
// Action Creator
const fetchUser = (userId) => {
return async (dispatch, getState) => {
dispatch({ type: 'FETCH_USER_REQUEST' });
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
dispatch({ type: 'FETCH_USER_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'FETCH_USER_FAILURE', error });
}
};
};
// 在组件中使用
componentDidMount() {
this.props.dispatch(fetchUser(123));
}
三、Thunk 的实现原理 1. 中间件机制 Redux Thunk 通过中间件拦截 Action,判断其类型: 若 Action 是普通对象(plain object),按正常流程处理。 若 Action 是函数(Thunk),则执行该函数并传入 dispatch 和 getState
const thunkMiddleware =
({ dispatch, getState }) =>
(next) =>
(action) => {
if (typeof action === "function") {
return action(dispatch, getState) // 执行 Thunk 函数
}
return next(action) // 普通 Action 继续传递
}
2. 与 Generator 的对比 - Thunk:基于闭包和回调,代码简单但需手动处理异步流程。 - Redux-Saga:基于 Generator 函数,通过 yield 暂停/恢复异步任务,适合复杂异步流控制
四:总结 Thunk 是 Redux 生态中处理异步操作的基础工具,通过将异步逻辑封装为函数,实现了状态管理的解耦和代码的可维护性。对于简单场景(如 API 请求),Thunk 足够高效;复杂场景可结合 Saga 或 RxJS 等方案。理解 Thunk 的原理和适用边界,能帮助开发者更合理地设计前端架构。
- 手写扁平化的 Thunk
type Callback = (error: Error, result: any | Thunk) => void
// Thunk是接受Callback为参数的函数。
type Thunk = (callback: Callback) => void
const func1 = (cb) => {
setTimeout(() => cb(null, "ok"), 10)
}
const func2 = (cb) => {
setTimeout(() => cb(null, func1), 10)
}
const func3 = (cb) => {
setTimeout(() => cb(null, func2), 10)
}
flattenThunk(func3)((error, data) => {
console.log(data) // 'ok'
})
// /**
// * @param {Thunk} thunk
// * @return {Thunk}
// */
// function flattenThunk(thunk) {
// return (error, data) => {
// if (error) {
// throw error
// }
// let val = thunk(data)
// }
// }
//当Error发生时,尚未被调用的函数需要被跳过
/**
* @param {Thunk} thunk
* @return {Thunk}
* /简单来说就是劫持callback,自己实现一个支持链式调用的callback;
*/
function flattenThunk(thunk) {
return function (cb) {
const _cb = (error, data) => {
if (error) {
cb(error)
} else if (typeof data === "function") {
data(_cb)
} else {
cb(error, data)
}
}
thunk(_cb)
}
}
function flattenThunk(thunk) {
return function (cb) {
function wrapper(err, data) {
return typeof data == "function" ? data(wrapper) : cb(err, data)
}
thunk(wrapper)
}
}
- 手写 compose 组合,koa 洋葱模型
function compose(fns) {
return (context, next) => {
return dispatch(0)
function dispatch(i) {
let fn = fns[i]
if (!fn) return Promise.resolve()
if (i === fns.length) fn = next
try {
return Promise.resolve(fn.call(context, () => dispatch(i + 1)))
} catch (e) {
return Promise.reject(e)
}
}
}
}
- 手写 Promise.race()
该问题有些类似 31. 实现 async helper - race(),只不过处理的是 Promise。
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。source: MDN
你能实现自己的 Promise.race()吗?
Promise.prototype.myRace = function (arr) {
return new Promise((resolve, reject) => {
arr.forEach((item) => {
return Promise.resolve(item).then(resolve).catch(reject)
})
})
}
- 核心原理:同源策略(Same-Origin Policy)限制的是不同源脚本读取对方数据的能力,而非发送请求本身。核心逻辑:浏览器阻止的是 JavaScript 代码获取跨域请求的响应数据,而非阻止请求发出
0618
- 1471
- 下周二进行半年述职,总结下上半年做的事,好像也没做啥特别的需求,都是正常的业务迭代
- 手写 Promise.all
function all(promises) {
return new Promise((resolve, reject) => {
let len = promises.length
let data = []
if (len === 0) return resolve([])
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i]).then(
(res) => {
data[i] = res
if (data.length === len) {
resolve(data)
}
},
(err) => {
reject(err)
}
)
}
})
}
- 手写 Promis.any
function any(promises) {
// your code here
return new Promise((resolve, reject) => {
let len = promises.length
let errors = []
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i]).then(
(res) => {
resolve(res)
},
(err) => {
errors[i] = err
if (errors.length === len) {
reject(
new AggregateError(
"No Promise in Promise.any was resolved",
errors
)
)
}
}
)
}
})
}
- 实现 DOM store
class NodeStore {
constructor() {
this.store = {}
}
/**
* @param {Node} node
* @param {any} value
*/
set(node, value) {
node.__store_key__ = node.__store_key__ || Symbol()
this.store[node.__store_key__] = value
}
/**
* @param {Node} node
* @return {any}
*/
get(node) {
return this.store[node.__store_key__]
}
/**
* @param {Node} node
* @return {Boolean}
*/
has(node) {
return !!this.store[node.__store_key__]
}
}
手写实现 Object.groupBy()
0619
- 1472
- 前端 AI 应用练习
- SSE: SSE:Server-Sent Events 服务器推送事件,简称 SSE,是一种服务端实时主动向浏览器推送消息的技术。SSE 是 HTML5 中一个与通信相关的 API,主要由两部分组成:服务端与浏览器端的通信协议( HTTP 协议)及浏览器端可供 JavaScript 使用的 EventSource 对象.
应用场景:前端流式渲染:服务端将大模型生成的文本分块推送到前端,实现逐词输出效果(类似 ChatGPT)。
INFO
EventSource
EventSource 接口是 web 内容与服务器发送事件通信的接口。
一个 EventSource 实例会对 HTTP 服务器开启一个持久化的连接,以 text/event-stream 格式发送事件,此连接会一直保持开启直到通过调用 EventSource.close() 关闭。
一旦连接开启,来自服务端传入的消息会以事件的形式分发至你代码中。如果接收消息中有一个 event 字段,触发的事件与 event 字段的值相同。如果不存在 event 字段,则将触发通用的 message 事件。
与 WebSocket 不同的是,服务器发送事件是单向的。数据消息只能从服务端到发送到客户端(如用户的浏览器)。这使其成为不需要从客户端往服务器发送消息的情况下的最佳选择。例如,对于处理如社交媒体状态更新、消息来源(news feed)或将数据传递到客户端存储机制(如 IndexedDB 或 web 存储)之类的,EventSource 无疑是一个有效方案。
- 简单的示例
const eventSource = new EventSource('http_api_url', { withCredentials: true })
// 关闭连接
eventSource.close()
// 可以使用addEventListener()方法监听
eventSource.addEventListener('open', function(event) {
console.log('Connection opened')
})
eventSource.addEventListener('message', function(event) {
console.log('Received message: ' + event.data);
})
// 监听自定义事件
eventSource.addEventListener('xxx', function(event) {
console.log('Received message: ' + event.data);
})
eventSource.addEventListener('error', function(event) {
console.log('Error occurred: ' + event.event);
})
// or 也可以使用属性监听的方式
eventSource.onopen = function(event) {
console.log('Connection opened')
}
eventSource.onmessage = function(event) {
console.log('Received message: ' + event.data);
}
eventSource.onerror = function(event) {
console.log('Error occurred: ' + event.event);
})
sse 与 websocket 比较:
- 协议不同 sse 基于 Http 协议,websocket 基于 TCP
- 通信不同 sse 单工,只能服务端单向发送消息 websocket 双工,可以同时发送和接收信息
- 自动重连 sse 内置断线重连和消息追踪,websocket 不在协议范围内须手动实现
- 数据格式 sse 文本或使用 Base64 编码和 gzip 压缩的二进制消息 websocket 支持多种
- 事件 sse 支持自定义事件类型,websocket 不支持自定义事件类型
- 连接数 sse 连接数 HTTP/1.1 6 个,HTTP/2 可协商(默认 100)websocket 无限制
0620
const items = [
{
id: 1,
kind: "a",
},
{
id: 2,
kind: "b",
},
{
id: 3,
kind: "a",
},
]
const groups = Object.groupBy(items, ({ kind }) => kind)
// {
// a: [
// {
// id: 1,
// kind: 'a'
// },
// {
// id: 3,
// kind: 'a'
// }
// ],
// b: [
// {
// id: 2,
// kind: 'b'
// }
// ]
// }
/**
* @template T
* @template { keyof any } K
* @param { Array<T> } items
* @param { (item: T) => K } callback
* @returns { Record<K, Array<T>> }
*/
function ObjectGroupBy(items, callback) {
// Your code here
// return items.reduce((cur, pre) => {
// if (pre) {
// let key = callback(pre)
// let val = pre[key]
// cur[key] = val
// return cur
// }
// }, {})
let res = Object.create(null)
for (let item of items) {
let key = callback(item)
if (!(key in res)) {
res[key] = []
}
res[key].push(item)
}
return res
}
;[1, 2, 3].map((item) => item * 2) // 2 4 6
Array.prototype.myMap = function (fn, context) {
let arr = this || []
let res = []
for (let i = 0; i < arr.length; i++) {
res.push(fn.call(context, arr[i], i, this))
}
return res
}
;[(1, 2, 3)].reduce((cur, pre) => {
return pre + cur
}, 0)
Array.prototype.myReduce = function (fn, init) {
let arr = this || []
let res = init ? init : arr[0]
let start = init ? 0 : 1
for (let i = start; i < arr.length; i++) {
res = fn.call(null, res, arr[i], i, this)
}
return res
}
0623
- 1476
- 周末都在带孩子,基本没有自己的时间,这种生活希望赶快过去
增加数组原型 group 方法,实现自定义分类
var array = [1, 2, 3, 4, 5] // sorted
// expected
var result = {
bigger: [4, 5],
smaller: [1, 2, 3],
}
- 重温 groupBy()
function groupBy(items, fn) {
let res = Object.create(null)
for (let item of items) {
let key = fn(item)
if (!(key in item)) {
res[key] = []
}
res[key].push(item)
}
return res
}
0624
- 1477
实现 Object.is()
Object.is() 和===基于一致,除了以下情况:
Object.is(0, -0) // false
0 === -0 // true
Object.is(NaN, NaN) // true
NaN === NaN // false
/**
* @param {any} a
* @param {any} b
* @return {boolean}
*/
function is(a, b) {
// your code here
if (Number.isNaN(a) && Number.isNaN(b)) return true
if (a === 0 && b === 0) return 1/a === 1/b
return a === b
}
实现辅助函数(我们称之为 to),它接收一个 Promise 作为参数,并且永远不会被 reject。相反,它总是 resolve 一个数组,格式为 [error, data]
function to<T>(promise: Promise<T>): Promise<[Error | null, T | undefined]> {
return (
promise.then <
[null, T] >
((data: T) => [null, data]).catch <
[Error, undefined] >
((error) => [error, undefined])
)
}
function To(promise) {
return promise.then((res) => [null, red]).catch((err) => [err, undefined])
}
0630
- 1482
- 来这边四年,后面 2 年,从 2023 年 11 月开始担心被裁,担心了 2 年,在这边,永无出头之日,年纪轻轻的,有这种想法很危险,不愿你多么努力,基本上都是垫底的那一个,早已习惯
- 这次裁员意料之内,如何准备后续面试,还要保持带小孩的精力跟时间,实在不行,就休息半年,全职带小孩
- 虚拟 dom 转换真实 DOM
// obj = {
// tag: '',
// children: []
// props: {}
// }
function render(VNode, app) {
if(type of VNode) {
return app.appendChild(document.ceateTextNode(VNode))
}
let target = app.appendChild(document.createElement(VNode.tag))
// props
let props = VNode.props
Object.keys(props).forEach(key => {
target.setAttribute(key, props[key])
})
// children
for(let child of VNode.children) {
render(child, target)
}
return target
}
function render(vnode,parent = null) {
let mount = parent ? (el) => parent.appendChild(el) : (el) => el
if (typeof vnode === 'string' || typeof vnode === 'number') {
return mount(document.createTextNode(vnode))
} else {
const dom = mount(document.createElement(vnode))
for(const child of vnode.children) {
render(child, dom)
}
for(let prop of vnode.props) {
dom.setAttribute(prop, vnode.props[prop])
}
return dom
}
}
virtualize() 接受一个 DOM tree 然后返回一个 object literal 的表达
/**
* @param {HTMLElement}
* @return {object} object literal presentation
*/
function virtualize(element) {
// your code here
const res = {
type: element.tagName.toLowerCase(),
props: {},
}
for (let arrt of element.attributes) {
const name = attr.name === "class" ? "className" : attr.name
res.props[name] = attr.value
}
const children = []
for (let node of element.childNodes) {
if (node.nodeType === 3) {
children.push(node.textContent)
} else {
children.push(virtualize(node))
}
}
res.props.children = children.length === 1 ? children[0] : children
return res
}