Skip to content

202506

INFO

在张江度过了四年难忘的生活,现在因公司搬迁到静安,我随之搬到静安,有点不适应这边的老破小

0601

  • 搬到静安阳曲路的第一天,过的非常不好,实现不习惯这边的老破小

0610

  • 1462
  • 被收购尘埃落定,接下来就是等待慢慢被边缘化,然后被干掉

0612

  • 1464
  • 如果知道自己两个月之后被干掉,如何做好前端面试?又要准备前端面试?真的是已经麻木了,会不会写都不重要,重要的是思路和眼界?
  • 手写前端
js
//二分查找

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

⚙️ 一、技术深度强化:查漏补缺与前沿融合

  1. 基础复盘与框架原理

核心基础:重点复习 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 绑定。

js
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()
js
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()
js
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

js
// 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
js
// 普通 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 请求)

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

js
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
js
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 洋葱模型
js
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()吗?

js
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
js
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
js
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
js
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()

js

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 无疑是一个有效方案。

  • 简单的示例
js
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

js
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 方法,实现自定义分类

js
var array = [1, 2, 3, 4, 5] // sorted
// expected
var result = {
  bigger: [4, 5],
  smaller: [1, 2, 3],
}
  • 重温 groupBy()
js
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()

js
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]

js
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
js
// 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 的表达

js
/**
 * @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
}