前端面试常考题目详解
一、JavaScript 基础理论
- 闭包的原理及实际应用场景
- 原理:闭包是指函数能够访问其自身作用域之外的变量,即使外部函数已经执行完毕。这是因为函数在创建时会捕获其所处的词法环境,使得外部变量能够被保留。
- 应用场景:
TypeScript取消自动换行复制
- 实现防抖节流函数,如:
- 模块化开发,通过闭包创建私有变量,避免全局污染。
- 柯里化函数,将多参数函数转化为单参数函数的序列。
- 原型链和继承
- 原型链:每个对象都有__proto__属性,指向其构造函数的prototype,多个对象通过这种方式形成链条,即原型链。当访问对象的属性或方法时,会沿着原型链向上查找。
- 继承实现方式:
- 原型链继承:将子类的原型指向父类的实例。
- 构造函数继承:在子类构造函数中调用父类构造函数,使用call或apply改变this指向。
- 组合继承:结合原型链继承和构造函数继承的优点。
- ES6 的class和extends:语法糖,本质是基于原型链实现。
- 异步编程方式及区别
- 回调函数:简单直接,但容易导致回调地狱,代码可读性差。
- Promise:解决回调地狱问题,提供then、catch、finally等方法,支持链式调用,三种状态(pending、fulfilled、rejected)一旦改变不可再变。
- async/await:基于 Promise 的语法糖,使异步代码看起来像同步代码,更易读,await后面跟 Promise 对象,async函数返回 Promise 对象。
二、框架应用(以 React 为例)
- hooks 相比 class 组件的优势
- 代码简洁性:无需编写constructor、super等代码,减少模板代码,使组件逻辑更清晰。
- 逻辑复用:通过自定义 hooks 可以轻松复用组件逻辑,而 class 组件的逻辑复用需要借助高阶组件或 render props,容易导致组件嵌套过深。
- 性能优化点:可以更精准地控制组件的重渲染,如使用useMemo缓存计算结果,useCallback缓存函数。
- 状态管理更直观:多个状态之间相互独立,避免 class 组件中setState的合并更新带来的问题。
- React 的虚拟 DOM 和 Diff 算法
- 虚拟 DOM:是一个 JavaScript 对象,用来描述真实 DOM 的结构,当组件状态发生变化时,React 会先构建新的虚拟 DOM,然后与旧的虚拟 DOM 进行对比,计算出差异后再更新真实 DOM,减少 DOM 操作带来的性能开销。
- Diff 算法:
- 只进行同级比较,不同层级的节点直接删除重建。
- 对于同一层级的节点,通过key来标识,相同key的节点进行属性对比和更新,不同key的节点直接删除重建。
- Redux 的工作原理
- Redux 是一个状态管理库,基于三大原则:单一数据源、状态只读、使用纯函数修改。
- 工作流程:组件通过dispatch方法触发action,reducer根据action的类型处理并返回新的状态,store接收新状态后通知组件重新渲染。
三、工程化
- webpack 打包体积过大如何优化
- loader 配置:对node_modules目录下的文件不进行 babel 转译,通过exclude: /node_modules/配置。
- tree-shaking:开启mode: 'production',利用 ES6 的模块特性(静态导入导出),移除未使用的代码。
- 代码分割:使用splitChunks配置将公共代码、第三方库代码分割成单独的 chunk,实现按需加载。
- CDN 加速:将静态资源(如图片、字体、JS、CSS 文件)部署到 CDN 上,减少服务器压力,提高加载速度。
- 压缩代码:使用terser-webpack-plugin压缩 JS 代码,mini-css-extract-plugin和css-minimizer-webpack-plugin压缩 CSS 代码。
- ESLint 和 Prettier 的作用及配合使用
- ESLint:用于检查代码中的语法错误、潜在问题和代码风格问题,可配置规则来规范团队代码。
- Prettier:专注于代码格式化,确保代码风格的一致性,不检查代码的逻辑错误。
- 配合使用:通过eslint-config-prettier禁用 ESLint 中与 Prettier 冲突的规则,eslint-plugin-prettier将 Prettier 的格式化规则作为 ESLint 的规则来执行,实现代码检查和格式化的统一。
四、性能优化
- 如何优化首屏加载速度
- 资源加载优化:
- 图片懒加载:使用IntersectionObserver API 或第三方库(如react-lazyload),只有当图片进入可视区域时才加载。
- 代码分割:将代码按路由或组件分割,实现按需加载,如 React 的React.lazy和Suspense。
- 压缩资源:对 JS、CSS、图片等资源进行压缩,减少文件体积。
- 渲染优化:
- 预渲染:在构建时生成静态 HTML 页面,提高首屏渲染速度。
- 骨架屏:在页面加载完成前显示骨架屏,提升用户体验。
- 减少 DOM 操作:避免频繁的 DOM 增删改查,可先操作文档片段,再一次性添加到 DOM 中。
- 缓存策略:
- 强缓存:设置Cache-Control和Expires响应头,使浏览器直接从缓存中获取资源。
- 协商缓存:设置Last-Modified和ETag响应头,浏览器发送请求时携带相关信息,服务器判断资源是否更新,未更新则返回 304 状态码,使用缓存。
- 列表性能优化
- 虚拟滚动:当列表数据量较大时,只渲染可视区域内的列表项,如使用react-window或vue-virtual-scroller库。
- 数据分页:将数据分成多页,每次只加载当前页的数据,减少一次性渲染的数据量。
- 避免不必要的重渲染:使用 React 的React.memo、useMemo、useCallback或 Vue 的memo、shallowRef等方法优化组件。
五、兼容性处理
- Flex 和 Grid 布局的区别及兼容性考量
- 区别:
- Flex 布局是一维布局,只能处理一行或一列的布局。
- Grid 布局是二维布局,可以同时处理行和列的布局。
- 兼容性考量:
- Flex 布局在主流浏览器(IE10+)中支持较好,但在一些旧版本浏览器中可能存在差异,需要添加前缀(如-webkit-、-moz-)。
- Grid 布局在 IE 浏览器中支持较差,IE11 部分支持且语法不同,在实际项目中如果需要兼容 IE 浏览器,应谨慎使用 Grid 布局,或通过其他方式实现相同的布局效果。
- 解决浏览器兼容性问题的常用方法
- 使用 CSS 前缀工具(如 Autoprefixer)自动添加浏览器前缀。
- 对于不支持 ES6 + 语法的浏览器,使用 Babel 进行转译,并通过core-js等库补充缺失的 API。
- 使用 polyfill 库(如html5shiv、respond.js)解决 HTML5 新标签和 CSS3 特性在旧浏览器中的兼容性问题。
- 针对不同浏览器编写特定的 CSS 或 JS 代码,如使用条件注释针对 IE 浏览器进行处理。