Interview experience-01

80 阅读5分钟

1. 箭头函数和普通函数的区别

  • this: 箭头函数不绑定this,从外层词法捕获;普通函数this由调用方式决定
  • arguments: 箭头函数没有arguments; 用剩余参数(...args);普通函数有arguments
  • new:箭头函数不能作为构造函数,没有 prototype;普通函数可以 new
  • super/new.target:箭头函数里取的是外层的。
  • return箭头函数:如果函数体是一个单表达式,可以省略 {}return,隐式返回这个值。 普通函数:必须写 return
const obj = {
  x: 1,
  a() { 
  // 如果在这里直接console.log(this.a)那么打印出 1; 但是下面的setTimeOut不是立即执行函数,
  // 是把传进去的回调函数function()存起来,直到0ms后触发,
  // 而setTimeout是window的方法,相当于window.setTimeout(function(){ console.log(this.x) }, 0)
    setTimeout(function() {
      console.log(this.x) 
    }, 0) 
  }, // 打印 undefined

  b() { 
  // 箭头函数中,this会继承定义的this,定义时this是obj
    setTimeout(() => { 
      console.log(this.x) 
    }, 0) 
  }  // 1
};

obj.a();
obj.b();
a() 方法中的 function 回调
setTimeout(function() { console.log(this.x) }, 0)
  • 普通函数里的 this 取决于调用者
  • setTimeout 执行时,这个回调函数是由 window (浏览器环境) / global (Node.js) 调用的,而不是 obj
  • 因此 this 指向全局对象,this.xundefined

2. b() 方法中的箭头函数回调
setTimeout(() => { console.log(this.x) }, 0)
  • 箭头函数 不会创建自己的 this,它会从定义时所在的作用域捕获 this
  • 这里箭头函数定义在 b() 方法里面,而调用 obj.b() 时,this 就是 obj
  • 所以箭头函数中的 this 依然指向 obj
  • 因此 this.x 输出 1

2.Promise 解决了什么问题?

  • 痛点:
    • 回调地狱
    • 反转控制(失败时没人兜底)
    • 错误很难统一捕获
  • Promise 的改进
    • 链式调用,顺序/并行 更清晰
    • 统一错误通道:链末尾 .catch()
    • 组合:all / race / allSettled / any

3. 重排(回流)和 重绘

  • 重排:布局变化(几何属性)→ 计算布局 → 成本高

    • 触发:增删 DOM、改变尺寸/位置、读写混用布局信息(offsetTop 等)
  • 重绘:仅视觉更新(颜色、阴影、背景)→ 成本低

优化:
  • 批量 DOM 操作应使用:(DocumentFragment / requestAnimationFrame

  • 读写分离:先读再写,避免强制同步布局
  • 使用 transform/opacity 做动画(避免重排)
  • will-change(慎用,别滥开)
浏览器渲染流程:
    1. JavaScript 改变 DOM 或 CSS
    1. 浏览器需要重新计算样式(Recalculate Style)
    1. 计算元素的几何信息(Layout / Reflow = 回流/重排)
    1. 把结果绘制到位图(Paint = 重绘)
    1. 把位图送到 GPU 合成(Composite) 👉 性能最贵的是 Layout (重排) ,其次是 Paint (重绘),最轻的是 Composite。
      所以优化的目标就是:尽量少触发回流 / 重绘,能走 GPU 就别走 CPU

4. 跨域

  • 原因:同源策略限制(协议/域名/端口三者任一不同即跨域),浏览器出于安全阻止 JS 读取响应。

  • 解决: 服务端设置响应头(根本办法)

    • Access-Control-Allow-Origin: https://xxx.com(或 *,但配合凭证要配置具体域)
    • Access-Control-Allow-Methods / Headers
    • 凭证:Access-Control-Allow-Credentials: true + 前端 fetch(..., { credentials: 'include' })

5. 首屏优化(为何“路由懒加载不一定优化首屏”)

首屏的瓶颈通常在:首屏 HTML、关键 CSS、首屏 JS 执行、数据获取、首屏图片。

  • 路由懒加载会把非首屏路由的 JS 切分并延后——对首屏不一定有帮助(首屏仍需加载其自身 JS/CSS/数据)。

  • 真正有效的首屏优化:

    • SSR/SSG(直出 HTML)
    • Critical CSS(内联首屏关键样式)
    • 按组件级切分(首屏仅加载必需组件)
    • 缓存与预取(HTTP 缓存、<link rel=prefetch/preload>
    • 图像懒加载与占位(LQIP/Skeleton)
    • 降低 JS 体积(tree-shaking、移除未用依赖)

6.代码切分方式 & 路由异步加载

通用代码切分

  • 动态导入:const Comp = lazy(() => import('./Comp'))
  • Webpack magic comments:import(/* webpackChunkName: "user" */ './User')
  • 分离 vendor / runtime(SplitChunks)

React 路由(v6)异步

import { lazy, Suspense } from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const Home = lazy(() => import('./pages/Home'));
const User = lazy(() => import('./pages/User'));

const router = createBrowserRouter([
  { path: '/', element: <Home/> },
  { path: '/user', element: <User/> },
]);

export default function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <RouterProvider router={router}/>
    </Suspense>
  );
}

要点:首屏就是 /,那 / 页的包仍会进入首屏;只有非首屏路由才被延后加载。


7) 异步路由怎么实现(原理一嘴)

  • 路由匹配到组件时,返回的不是组件本身,而是一个 动态 import 的 Promise
  • 路由层在渲染前等待(或用 Suspense 边界托底),当模块加载完成再渲染真正组件。
  • Bundler 看到 import() 就会把该模块打进独立 chunk,按需加载。

8) XSS 攻击场景 & 防护

类型

  • 反射型:恶意脚本在 URL 参数中,服务器回显。
  • 存储型:恶意脚本存进数据库,所有访问者都会中招。
  • DOM 型:前端直接把不可信数据注入 DOM(innerHTML)。

防护

  • 转义输出(HTML、URL、JS 上下文分别转义)
  • 内容安全策略(CSP)Content-Security-Policy 阻止内联脚本等
  • 输入校验 & 白名单(富文本用 DOMPurify 等进行 sanitize)
  • Cookie 设置 HttpOnlySameSite

9) 防抖(debounce)——含取消

语义:短时间内高频触发,只在最后一次第一次(可选)触发后执行。常用于输入框搜索。

lodash 文档(英文要点)
_.debounce(func, wait, [options])

  • options.leading (boolean): invoke on the leading edge

  • options.trailing (boolean): invoke on the trailing edge

  • options.maxWait (number): maximum wait time before forced invoke

  • Returns a debounced function with methods:

    • .cancel() → cancel delayed invocation
    • .flush() → immediately invoke pending

使用示例(含取消)

import debounce from 'lodash/debounce';

// 500ms 内只执行一次;首次不执行,最后一次执行
const onSearch = debounce((q) => {
  fetch(`/api?q=${encodeURIComponent(q)}`)
}, 500, { leading: false, trailing: true });

// 组件卸载时取消
// React:
useEffect(() => {
  return () => onSearch.cancel();
}, []);

// 需要立刻执行当前等待的调用
onSearch.flush();

// 需要中途取消(比如清空输入时不再请求)
onSearch.cancel();

面试对比

  • 防抖(debounce):频繁触发 → 合并为一次(最后或第一次)。
  • 节流(throttle):固定间隔内最多执行一次。