前端灵魂拷问:10道题

一、观察者模式 vs 中介者模式 vs 发布-订阅

🔍 底层机制

核心差异

  • 观察者:直接耦合(Subject持有Observer引用)
  • 中介者:通过中介解耦(Colleagues不直接通信)
  • 发布订阅:通过事件总线完全解耦

🛠️ 实现对比

js 复制代码
// 观察者模式
class Subject {
  observers = []
  add(o) { this.observers.push(o) }
  notify() { this.observers.forEach(o => o.update()) }
}

// 发布订阅
class EventBus {
  events = {}
  on(type, fn) { (this.events[type] ||= []).push(fn) }
  emit(type, data) { this.events[type]?.forEach(fn => fn(data)) }
}

🌐 应用场景

模式 典型案例 反例场景
观察者 Vue响应式系统 跨组件深层通信
中介者 聊天室(Room作为中介) 简单的一对一通信
发布订阅 全局事件总线 需要严格顺序的事件流

💡 举一反三

  1. Redux属于哪种模式?(发布订阅的变体)
  2. Node.js的EventEmitter实现原理

二、HTTP连接复用机制

🔍 底层机制

sequenceDiagram participant Browser participant Server Browser->>Server: TCP握手 (SYN) Server->>Browser: SYN-ACK Browser->>Server: ACK rect rgb(200, 220, 255) Browser->>Server: HTTP/1.1请求1 (keep-alive) Server->>Browser: 响应1 Browser->>Server: HTTP/1.1请求2 (复用连接) Server->>Browser: 响应2 end rect rgb(255, 220, 200) Browser->>Server: HTTP/2多路复用 Note over Browser,Server: 单连接并行传输 end

关键差异

  • HTTP/1.1:串行请求(队头阻塞)
  • HTTP/2:二进制分帧 + 多路复用

🛠️ 配置对比

http 复制代码
// HTTP/1.1 头部
Connection: keep-alive
Keep-Alive: timeout=5, max=1000

// HTTP/2 自动复用
:method: GET
:scheme: https
:path: /api/data

🌐 性能优化

js 复制代码
// 检测连接复用
const connection = performance.getEntriesByType('navigation')[0]
console.log(connection.nextHopProtocol) // "h2" 或 "http/1.1"

三、CSS Position全解析

🔍 底层机制

flowchart TD A[position属性] --> B[static] A --> C[relative] A --> D[absolute] A --> E[fixed] A --> F[sticky] C --> G[占据原空间] D --> H[脱离文档流] F --> I[吸附定位] I --> J[阈值触发] J --> K[top/right/bottom/left]

🛠️ sticky实战

css 复制代码
.table-header {
  position: sticky;
  top: 0;
  background: white;
  z-index: 10;
}

/* 兼容性处理 */
@supports not (position: sticky) {
  .table-header { position: fixed; }
}

🌐 应用场景

定位方式 典型用例 注意事项
sticky 表头/侧边栏吸附 需父级无overflow:hidden
absolute 弹窗/下拉菜单 最近定位祖先元素
fixed 返回顶部按钮 iOS键盘弹出时可能失效

四、浏览器事件流与代理

🔍 底层机制

flowchart TD A[事件触发] --> B[捕获阶段] B --> C[目标阶段] C --> D[冒泡阶段] E[事件代理] --> F[绑定到父级] F --> G[通过target判断] G --> H[减少监听器数量]

🛠️ 事件代理实现

js 复制代码
// 动态列表的事件代理
ul.addEventListener('click', (e) => {
  if (e.target.matches('li.item')) {
    handleItemClick(e.target.dataset.id)
  }
})

// 性能对比
// 直接绑定: O(n)个监听器
// 事件代理: O(1)个监听器

🌐 优缺点分析

优点

  • 减少内存占用
  • 支持动态元素
  • 简化初始化

缺点

  • 事件类型限制(如focus/blur不支持冒泡)
  • 事件层级过深时target可能不准确

五、this绑定规则全解

🔍 底层机制

flowchart TD A[this绑定] --> B[默认绑定] A --> C[隐式绑定] A --> D[显式绑定] A --> E[new绑定] A --> F[箭头函数] B --> G[严格模式undefined] C --> H[对象方法调用] D --> I[call/apply/bind] E --> J[构造函数] F --> K[词法作用域]

🛠️ 优先级排序

js 复制代码
const obj = {
  fn: function() {
    console.log(this)
  }
}

// 优先级:new > 显式 > 隐式 > 默认
new obj.fn() // this指向新对象
obj.fn.call(window) // this指向window
obj.fn() // this指向obj

🌐 经典陷阱

js 复制代码
// 回调函数丢失this
setTimeout(obj.fn, 100) // this指向window

// 解决方案
setTimeout(obj.fn.bind(obj), 100)
// 或
setTimeout(() => obj.fn(), 100)

六、表单跨域深度解析

🔍 底层机制

sequenceDiagram participant Browser participant SiteA participant SiteB Browser->>SiteA: 加载页面 Browser->>SiteB: 表单POST action="SiteB" SiteB->>Browser: 返回302重定向 Note over Browser: 浏览器自动跟随重定向 SiteB->>Browser: 最终响应

🛠️ 跨域方案对比

方案 是否支持 实现方式
表单POST action指向跨域地址
AJAX提交 需CORS或JSONP
隐藏iframe target指向iframe

🌐 安全限制

html 复制代码
<!-- 允许跨域提交但无法读取响应 -->
<form action="https://api.cross.com/submit" method="POST">
  <input name="data" value="test">
</form>

<!-- 现代方案:fetch + CORS -->
fetch('https://api.cross.com/submit', {
  method: 'POST',
  body: new FormData(form),
  credentials: 'include'
})

七、Promise vs async/await

🔍 底层机制

flowchart LR A[Promise] --> B[微任务队列] C[async/await] --> D[生成器+Promise] D --> E[await暂停执行] E --> F[Promise.resolve] F --> G[恢复执行]

🛠️ 语法对比

js 复制代码
// Promise链
fetch('/api')
  .then(r => r.json())
  .then(data => console.log(data))
  .catch(err => console.error(err))

// async/await
try {
  const r = await fetch('/api')
  const data = await r.json()
  console.log(data)
} catch(err) {
  console.error(err)
}

🌐 性能差异

  • async/await本质是Promise的语法糖
  • 错误处理更直观(try/catch)
  • 调试时调用栈更清晰

八、搜索防抖实现

🔍 底层机制

flowchart TD A[输入事件] --> B{是否等待中} B -->|是| C[重置计时器] B -->|否| D[启动计时器] D --> E[延迟执行] E --> F[发送请求]

🛠️ 实现方案

js 复制代码
// 基础防抖
function debounce(fn, delay) {
  let timer = null
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

// React Hook版本
function useDebounce(value, delay) {
  const [debounced, setDebounced] = useState(value)
  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay)
    return () => clearTimeout(timer)
  }, [value, delay])
  return debounced
}

🌐 优化策略

  • 立即执行版本(leading edge)
  • 取消功能(cancel方法)
  • 最大等待时间(throttle + debounce混合)

九、中文搜索请求处理

🔍 底层机制

flowchart TD A[输入中文] --> B[URL编码] B --> C[encodeURIComponent] C --> D[UTF-8字节] D --> E[%E4%B8%AD%E6%96%87] F[服务端] --> G[decodeURIComponent] G --> H[还原中文]

🛠️ 实现方案

js 复制代码
// 现代浏览器自动处理
const keyword = '前端面试'
fetch(`/api/search?q=${keyword}`) // 自动编码

// 手动处理
const encoded = encodeURIComponent(keyword) // "%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95"
const decoded = decodeURIComponent(encoded)

// POST方案(避免URL长度限制)
fetch('/api/search', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ keyword })
})

🌐 兼容性处理

js 复制代码
// IE兼容
function encodeChinese(str) {
  return encodeURIComponent(str)
    .replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16)}`)
}

十、并发请求控制

🔍 底层机制

flowchart TD A[请求数组] --> B[并发池] B --> C[最大并发数限制] C --> D[任务队列] D --> E[完成一个补充一个] F[Promise.all] --> G[全部并发] F --> H[任一失败即失败] I[Promise.allSettled] --> J[等待全部完成] J --> K[不短路]

🛠️ 实现方案

js 复制代码
// 并发池控制
async function concurrentRequest(urls, max = 5) {
  const results = []
  const executing = []
  
  for (const url of urls) {
    const p = fetch(url).then(res => res.json())
    results.push(p)
    
    if (urls.length >= max) {
      const e = p.then(() => executing.splice(executing.indexOf(e), 1))
      executing.push(e)
      if (executing.length >= max) {
        await Promise.race(executing)
      }
    }
  }
  return Promise.all(results)
}

// 使用示例
const urls = Array.from({length: 20}, (_,i) => `/api/data/${i}`)
const data = await concurrentRequest(urls, 3)

🌐 高级控制

js 复制代码
// 带进度回调
async function* concurrentWithProgress(urls, max) {
  let completed = 0
  const update = () => yield { completed: ++completed, total: urls.length }
  
  // 实现略...
}
相关推荐
gnip1 小时前
链式调用和延迟执行
前端·javascript
SoaringHeart1 小时前
Flutter组件封装:页面点击事件拦截
前端·flutter
杨天天.1 小时前
小程序原生实现音频播放器,下一首上一首切换,拖动进度条等功能
前端·javascript·小程序·音视频
Dragon Wu1 小时前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
Jinuss1 小时前
Vue3源码reactivity响应式篇之watch实现
前端·vue3
YU大宗师2 小时前
React面试题
前端·javascript·react.js
木兮xg2 小时前
react基础篇
前端·react.js·前端框架
ssshooter2 小时前
你知道怎么用 pnpm 临时给某个库打补丁吗?
前端·面试·npm
IT利刃出鞘3 小时前
HTML--最简的二级菜单页面
前端·html
yume_sibai3 小时前
HTML HTML基础(4)
前端·html