前端灵魂拷问: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 }
  
  // 实现略...
}
相关推荐
林太白2 分钟前
NestJS-活动模块
前端·javascript·后端
林太白3 分钟前
NestJS-申请模块
前端·后端·nestjs
林太白6 分钟前
NestJS-文章数据模块
前端·javascript·nestjs
林太白9 分钟前
NestJS-通告数据模块
前端·nestjs
七月十二11 分钟前
[mind-elixir]Mind-Elixir 的交互增强:单击、双击与鼠标 Hover 功能实现
前端
Antonio91511 分钟前
【音视频】WebRTC-Web 音视频采集与播放
前端·音视频·webrtc
一只卡比兽12 分钟前
React `useRef` Hook: 全面解析
前端
林太白13 分钟前
Rust知识篇05-所有权和借用
前端·后端·rust
wordbaby15 分钟前
关于 CSS 线性渐变角度(linear-gradient 角度含义及记忆方法)
前端·css
王中阳Go15 分钟前
跟复旦硕士聊了1小时,没想到这些基础题他居然也栽了
后端·面试·go