面试备战录

154 阅读3分钟

1、跨域问题解决的方案以及引起跨域的问题 ?

答:跨域是指浏览器出于安全策略,阻止一个网站的JS脚本去请求另一个源(协议 + 域名 + 端口)的资源。这是浏览器的同源策略所致。

常见的解决方式:

  • CORS(服务端支持):后端配置响应头允许它跨域请求,由服务器“授权”跨域访问。
res.setHeader('Access-Control-Allow-Origin', '*'); // 或具体域名
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  • JSONP(仅支持 GET,已过时),原理是<script>不受同源策略限制。服务端返回callback({data})格式。
<script src="http://api.example.com/data?callback=handleData"></script>
function handleData(res) {
  console.log(res);
}
  • 代理转发(开发环境常用)
    • 本质上是 同源请求 → 代理服务 → 请求目标服务
    • 常见于前端开发时绕过 CORS 限制。
// vite.config.ts
server: {
  proxy: {
    '/api': {
      target: 'http://api.example.com',
      changeOrigin: true,
      rewrite: path => path.replace(/^\/api/, '')
    }
  }
}
// 开发中访问 /api/user,实际上由代理服务器转发到 http://api.example.com/user,浏览器不感知跨域。
  • Nginx 反向代理,在生产中常用 Nginx 配置跨域代理:
location /api/ {
  proxy_pass http://api.example.com/;
  add_header Access-Control-Allow-Origin *;
}
  • WebSocket 不受同源限制;WebSocket 是一种不同协议(ws://),不受 CORS 限制,可以作为某些实时跨域通信的替代。
  • iframe + postMessage(跨域通信);用于多个子应用间或主子应用通信:
// 父窗口向 iframe 发消息
iframe.contentWindow.postMessage('hello', 'http://target.com');
// 子窗口接收
window.addEventListener('message', e => {
  if (e.origin === 'http://your-allowed-origin.com') {
    console.log(e.data);
  }
});

为什么表单提交不会出现跨域

  • 因为表单提交是非常老的机制,早在浏览器发明之前就有,浏览器内部控制发送和接收
  • 没有开放给 JS 控制数据
  • 不会泄露用户信息到 JS 层

所以浏览器认为它是 “低风险”跨域,就允许了。

2、解释浏览器的事件循环(event loop)机制以及它与前端的异步编程有何关系?

  • 浏览器Event Loop是一个协调“执行同步任务”和“处理异步回调”的机制,它不断循环检查任务队列,把异步任务的回调推入主线程执行。
  • 当JS执行遇到setTimeout,Promise,async/await等异步代码时,浏览器会将这些异步任务注册到Web APIs区域,等主线程空闲后再通过事件循环(Event Loop)将回调推入任务队列执行。事件循环的存在使得JavaScript 单线程模型也能实现异步非阻塞编程。

3、宏任务微任务

答:宏任务微任务是JS在事件循环中安排异步任务执行的两个队列(任务类别)。在一次事件循环中,先清空微任务队列,再执行一个宏任务。

  • 常见的宏任务:setTimeout,setInterval,setImmediate,I/O,MessageChannel,requestAnimationFrame(部分实现),当前宏任务执行完成后,下一轮 Event Loop 开始前
  • 常见的微任务:Promise.then()catch()finally()MutationObserverqueueMicrotask()当前宏任务执行后、下一轮宏任务开始前

注:

  • Promise.thensetTimeout先执行(因为微任务优先)
  • async/awaitawait后面的代码其实是微任务
  • 浏览器绘制帧(如动画)通常在宏任务之后触发

浏览器事件循环简化版流程(核心阶段)

1、执行宏任务(Task)队列中的一个宏任务

  • 宏任务包括:事件回调(点击、键盘等)、定时器回调(setTimeout)、网络回调、脚本执行等。
  • 执行过程中同步代码直接运行,且产生的异步操作(Promise、queueMicrotask)排入微任务队列。

2、执行当前宏任务产生的所有微任务(Microtasks)

  • 包括 Promise 回调queueMicrotask 等。
  • 这些微任务会在当前宏任务结束后立即同步执行,直到微任务队列清空。

3、执行所有排队的 requestAnimationFrame 回调(Animation Frame Callbacks)

  • 这一步发生在当前宏任务和微任务执行完后,渲染之前。
  • 这些回调会在绘制前执行,方便同步动画更新。

4、浏览器绘制(渲染)阶段

  • 根据 DOM/CSS 的变化,浏览器执行布局(reflow)、绘制(repaint)等渲染操作,将更新后的画面显示到屏幕上。

5、然后开始下一次事件循环,继续执行下一个宏任务。