一小时复习前端面试|2024年年初30道面试题冲刺金三银四

这套题目列举的都是前端面试最最高频的问题,如果回答的太差,会减分!回答好了基本中高级没问题。

*****************************************

1. 从输入url到渲染页面

  1. 浏览器的地址栏输入URL并按下回车。
  2. 浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
  3. DNS解析URL对应的IP。
  4. 根据IP建立TCP连接(三次握手)。
  5. HTTP发起请求。
  6. 服务器处理请求,浏览器接收HTTP响应。
  7. 渲染页面,构建DOM树。
  8. 关闭TCP连接(四次挥手)

2. Js中的单线程和事件循环

  • Js是单线程,但是浏览器是多线程。
  • Js中采用了事件循环(Event Loop)来执行异步任务。
  • 所以,事件循环是一种异步编程模型,事件循环会不断地从任务队列(Task Queue)中取出待处理的任务并执行,直到任务队列为空为止。任务可以分为两类:宏任务(Macro Task)和微任务(Micro Task)。
  • 微任务会优先于宏任务执行

3. Js实现继承有哪几种方式?

  1. 原型链继承:将父类的实例作为子类的原型,通过 prototype 进行继承
  2. 构造继承:将父类的实例属性复制给子类,通过 call 进行继承
  3. 实例继承:为父类实例添加新特性,作为子类实例返回
  4. 拷贝继承:将父类实例通过循环拷贝给子类
  5. 组合继承:就是 原型链继承 和 构造继承,一起使用
  6. 寄生组合继承:通过寄生方式,砍掉父类的实例属性,避免了 组合继承中,在调用两次父类的构造时,初始化两次实例方法/属性 的缺点

4. Js 中bind(),call()和apply()的区别

JavaScript 中的 bind()、call() 和 apply() 方法都可以用来改变函数内部的 this 指向

它们有一些重要的区别:

  • 三种方法最大的区别在于参数传入方式不同:bind() 方法接受一系列参数列表,而 call() 和 apply() 方法则分别接受一组参数和一个参数列表。具体而言,bind() 将参数作为一个个单独的值传入,而 call() 和 apply() 都允许传递一个数组作为参数。
  • 执行时间不同:bind() 绑定后返回一个新函数,并不会立即执行,需要调用该函数才会执行;而 call() 和 apply() 则会立即执行函数。
  • 返回值不同:bind() 方法返回一个绑定后的新函数,而 call() 和 apply() 则直接执行原始函数并返回执行结果。

它们的作用分别如下:

  • bind() 方法:bind() 可以指定函数内部的 this 指向,并将其绑定到一个新函数上进行返回。该函数并不会立即执行,而是等待调用。bind() 也可以用来实现柯里化(currying)
  • call() 方法:call() 可以在指定的 this 值和若干个参数(参数的列表)的前提下调用某个函数或方法。注意,call() 方法需要将参数逐个传递进去,而不能像 apply() 方法一样将所有参数打包成一个数组。
  • apply() 方法:apply() 和 call() 的作用非常类似,都是改变函数内部的 this 指向。区别在于,apply() 方法需要将参数打包成一个数组传递进去,而 call() 则是将参数逐个传递。

5. Js 中的闭包及其使用场景

  • 官方说法:闭包就是指有权访问另一个函数作用域中的变量的函数。
  • MDN说法:闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。
  • 深度回答:浏览器在加载页面会把代码放在栈内存( ECStack )中执行,函数进栈执行会产生一个私有上下文( EC ),此上下文能保护里面的使用变量( AO )不受外界干扰,并且如果当前执行上下文中的某些内容,被上下文以外的内容占用,当前上下文不会出栈释放,这样可以保存里面的变量和变量值,所以我认为闭包是一种保存和保护内部私有变量的机制。

*****************************************

6. Vue2与Vue3中的双向数据绑定

双向数据绑定就是:数据劫持 + 发布订阅模式(观察者模式)

Vue2中在实例初始化时遍历 data 中的所有属性,并使用 Object.defineProperty把这些属性全部转为 getter/setter。并 劫持各个属性 getter 和 setter,在数据变化时发布消息给订阅者,触发相应的监听回调,而这之间存在几个问题

  • 初始化时需要遍历对象所有 key,如果对象层次较深,性能不好
  • 通知更新过程需要维护大量 dep 实例和 watcher 实例,额外占用内存较多
  • Object.defineProperty 无法监听到数组元素的变化,只能通过劫持重写数方法
  • 动态新增,删除对象属性无法拦截,只能用特定 set/delete API 代替
  • 不支持 Map、Set 等数据结构

Vue3中使用 Proxy 来监控数据的变化。Proxy 是 ES6 中提供的功能,其作用为:用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。

7. Vue中template模版的编译原理

  1. 解析(parse):将模板字符串解析成 AST(抽象语法树)。
  2. 静态分析(static analysis):对 AST 进行静态分析,标记出其中的静态节点(Static Node)。
  3. 优化(optimize):遍历 AST,对静态节点进行优化,去掉不必要的操作。
  4. 代码生成(code generation):将 AST 转换成渲染函数(render function)的可执行代码。
  5. 最终的渲染:将生成的渲染函数运用到数据上,最终生成视图。

8. Vue2中的diff流程

Vue2中的虚拟DOM diff算法,其核心是采用双端比较(Two-Ended Algorithm)。

具体来说,Vue2中diff算法的步骤如下:

  1. 首先比较新旧虚拟DOM树的根节点,如果它们是不同类型的节点,则直接替换整个节点树;
  2. 如果根节点相同,则比较它们的子节点,这个过程称之为“Diff Children”;
  3. 在“Diff Children”过程中,使用双端比较算法,即同时从新虚拟DOM树和旧虚拟DOM树的两端开始向中间遍历,找到相应的节点进行比较,找到更新的节点后就停止比较;
  4. 如果新旧虚拟DOM树的子节点数组长度不同,则根据差异进行添加或删除节点;
  5. 对于相同位置的节点,判断它们是否相同,如果不同则进行更新;
  6. 最后返回新的虚拟DOM树。

9. Vue3中的diff流程

在Vue3中,Diff流程主要分为两个阶段:标记阶段和应用阶段。其中标记阶段是用来比较新老VNode树的差异,并记录下来需要进行的具体操作;应用阶段则是将这些具体操作应用在真实DOM上,完成页面的更新。

10. Vue中的组件间通信有哪些?

  1. props:父组件通过 props 属性向子组件传递数据。子组件接收该数据后,即可在其模板中直接使用。
  2. $emit() 和事件:子组件通过 $emit() 方法触发一个自定义事件,并把需要传递的数据作为参数传入。父组件监听该自定义事件,并在回调中处理数据。通过事件可以实现任意级别的组件间通信。
  3. $parent 和 $children:父组件可以通过 $children 找到所有的子组件,子组件可以通过 $parent 找到其父组件。这种方式属于直接引用和修改组件对象,需要注意和谨慎使用。
  4. provide 和 inject:祖先组件通过 provide 属性向子孙组件传递数据,子孙组件通过 inject 属性来获取这些数据。provide 和 inject 绑定并非响应式的,但是可以将一个观察者实例注入到 provide 中,使得组件在 inject 期间发生变化时获得通知。
  5. Vuex 状态管理(Vue3中的pinia):Vuex 是一个专门为 Vue.js 应用程序开发的状态管理库,提供了一种集中式存储管理应用程序中的所有组件的状态。组件通过调用 mutation 方法来改变状态,其他组件通过监听 state 属性来获取最新的状态。
  6. 兄弟组件间的传值:Vue2中使用eventBus中央事件总线 , Vue3中使用的mitt库

*****************************************

11. Vue-router中如何实现懒加载?

路由配置文件使用动态导入import()语句,并用箭头函数返回实现路由懒加载。

12. Vue3相对于Vue2进行了哪些优化?

  1. 更灵活的响应式系统:Vue 2.x 中响应式系统的核心是 Object.defineProperty,劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,实现响应式。Vue 3.x 中使用 Proxy对象重写响应式系统。
  2. 更快的渲染速度:Vue3 的编译器生成的渲染函数比 Vue2 生成的更高效。
  3. 编译阶段:Vue 2.x 通过标记静态节点,优化 diff 的过程。Vue 3.x中标记和提升所有的静态节点,diff的时候只需要对比动态节点内容。
  4. 更小的体积:Vue3 将源码拆分为多个独立的模块,这样就可以按需导入所需的模块,从而减小了整个库的体积。
  5. 更好的 TypeScript 支持:Vue3 对 TypeScript 的支持更加友好,内部使用了更先进的 TypeScript 特性,并为其提供了更好的声明文件。
  6. 更好的组件系统:比如,Vue3中引入了一个新的 Fragment 组件,它可以替代原来的 template 标签作为根节点
  7. 新增了setup组合式API

13. 介绍一下React Fiber

React Fiber是React框架的一种重新实现,旨在改善渲染性能和用户体验。它通过引入优先级调度增量渲染可中断的工作单元等机制,将渲染任务分解成小的可中断的单元,从而使React能够更好地处理大型应用程序和高优先级任务,提供流畅且响应迅速的用户界面。

14. React中常用的高阶组件有哪些?

  1. withRouter:将路由信息注入到组件中,使它们能够访问到路由对象(如location、history和match等)。
  2. connect:将React组件与Redux Store连接起来,并将State和Dispatch作为Props传递给组件。这使得组件能够直接从Store中读取和操作数据。
  3. memo:对于纯函数组件,使用memo可以缓存组件输出,以提高性能。
  4. withStyles:用于添加CSS样式到组件中。
  5. redux-thunk:使Action Creator返回一个函数而不是一个Action对象,从而可以执行异步操作并dispatch新的Action。
  6. recompose:提供了一组高阶功能,用于增强函数式React组件。例如,compose函数可以将多个HOC组合在一起。
  7. react-redux:提供了一组基于Redux Store的React组件,并简化了React与Redux之间的集成。

15. React Hook为什么不能放到条件语句中?

React Hook 不能放到条件语句中的原因是:React 需要使用 Hook 的规则是必须确保每次渲染时,Hook 调用的顺序都是一致的。也就是说,在一个组件内部,每一次渲染时,Hook 的调用顺序必须是相同的。

如果将 Hook 放到条件语句中,当条件发生变化时,Hook 的调用顺序就可能被打乱,从而导致组件状态不一致、出现错误等问题。因此,React 在运行时会对 Hook 的调用顺序进行验证,来确保 Hook 的使用符合规范。

*****************************************

16. React有哪些常用的hooks?

  1. useState:该 Hook 用于在函数组件中添加一个状态管理器。通过 useState,可以创建一个状态变量及其更新函数,并在组件内使用该变量来保存和更新组件的状态。
  2. useEffect:该 Hook 用于在组件渲染完成后执行一些副作用操作(例如订阅数据、更新 DOM 等)。通过 useEffect,可以在组件加载、更新和卸载时设置和清理副作用操作,并且可以在副作用操作之间共享状态。
  3. useContext:该 Hook 用于在组件之间共享一些全局的状态或函数,以避免通过多层嵌套的 Props 传递进行数据传输。通过 useContext,可以让组件在全局状态或函数的上下文中运行,并让它们能够方便地读取或更新全局状态或函数。
  4. useReducer:该 Hook 用于在组件中使用一种“状态容器”模式,以避免通过多层 Props 传递或 Context 共享进行状态管理。通过 useReducer,可以创建一个状态容器及其更新函数,并在组件内使用该容器来保存和更新组件的状态。
  5. useMemo:该 Hook 用于在组件渲染完成后缓存一些计算结果,以避免因为重复计算导致的性能问题。通过 useMemo,可以创建一个缓存变量,并在组件内使用该变量来保存计算结果并缓存。
  6. useCallback:该 Hook 用于在组件渲染完成后,将一些函数进行缓存,以避免因函数重复创建导致的性能问题。通过 useCallback,可以创建一个缓存函数,并在组件内使用该函数来代替重复创建的函数。
  7. useRef:该 Hook 用于在组件渲染完成后创建一个引用,以便在组件多次渲染时能够保留上一次渲染中的值。通过 useRef,可以创建一个引用变量,并在组件内使用该变量来保存一些持久化的数据。
  8. useImperativeHandle:该 Hook 用于在组件中实现一些自定义的 Ref 对象,并且要求将一些组件内部的方法或状态暴露给父组件使用。通过 useImperativeHandle,可以创建一个自定义的 Ref 对象,并在组件内指定一些公开的方法或属性。
  9. useLayoutEffect:该 Hook 与 useEffect 类似,但它会在浏览器渲染更新之前同步执行副作用操作,以确保 React 组件与浏览器同步更新。通常情况下,应该使用 useEffect,但在需要直接操作 DOM 元素或进行测量布局界面时,应当使用 useLayoutEffect。
  10. useDebugValue:该 Hook 可以帮助开发者在调试工具中显示额外的信息,以便更好地理解 Hook 的使用和行为。通常情况下,这个 Hook 只用于调试过程中,而不是实际的应用程序代码中。

17. 介绍下React中的useEffect

  • 在 React 中,useEffect 是一个用于处理副作用的 Hook。
  • 副作用是指在组件生命周期中的某些特定时刻需要执行的操作,例如数据获取、订阅事件、手动操作 DOM 等。
  • useEffect 的作用就是在组件渲染完成后执行这些副作用操作。

18. 介绍一下Promise的状态及其方法

在JavaScript中,Promise对象包含三种状态Pending(进行中)、Fulfilled(已成功)和Rejected(已失败)。

Promise的方法如下:

  1. Promise对象可以通过 then() 方法添加成功(Fulfilled)和失败(Rejected)时的回调函数。then() 方法可以链式调用,每次返回一个新的 Promise 对象,因此可以很容易地实现异步任务的连续执行。
  2. Promise对象还提供了 catch() 方法用于捕获错误和 finally() 方法用于在 Promise 被解析后运行代码块。
  3. Promise.all() 方法接收一个 Promise 数组作为参数,返回一个新的 Promise,只有当所有 Promise 都解析成功时才会被解析,否则该 Promise 会被拒绝。
  4. Promise.race() 方法接收一个 Promise 数组作为参数,返回一个新的 Promise,只要有一个 Promise 被解析或拒绝就会被解析或拒绝。
  5. Promise.resolve() 和 Promise.reject() 方法分别返回一个已解析和一个已拒绝的 Promise 对象,可以用于快速创建 Promise。

19. 介绍一下async/await 的实现原理

在 JavaScript 引擎中,async/await 函数的实现原理是基于 Promise 对象生成器函数(Generator Function)的协作。

20. 介绍一下let、const、var的区别

*********************************

21. 箭头函数和普通函数有什么区别

  1. 写法不同:箭头函数使用箭头(=>)来定义,而普通函数使用 function 关键字定义。
  2. this 的处理方式不同:在箭头函数中,this 的值与外层作用域的 this 绑定。而在普通函数中,this 的值由调用该函数的方式决定。
  3. 箭头函数没有 arguments 对象:箭头函数中没有自己的 arguments 对象,它的参数只能通过参数列表来传递。
  4. 箭头函数不能用作构造函数:由于箭头函数中没有自己的 this 值,因此不能用作构造函数来创建对象实例。

22. Css中常用的垂直居中解决方案有哪些?

vertical-align

属性

用于控制元素内部的行内元素(如文本或图片)的垂直对齐方式。通常与

display: table-cell

结合使用,实现单元格内部的元素垂直居中。

line-height

属性

用于设置行内元素的行高,可以使单行文本元素垂直居中。但是如果元素高度超过一行,则无法实现垂直居中。

flexbox

布局

使用

display: flex

和相关属性可以实现容器内元素的水平和垂直居中。

grid

布局

使用

display: grid

和相关属性可以实现网格布局中元素的水平和垂直居中。

绝对定位 + 负边距

将需要垂直居中的元素绝对定位到容器中心,然后通过负边距调整元素位置。

23. 什么是BFC?

BFC(Block Formatting Context)是 CSS 中一个很重要的概念。它是指一个块级容器,其中的元素按照特定规则布局和渲染,同时也影响着其内部和外部元素的布局。

24. Css中移动端适配有哪些方案?

  1. 首先,通过meta标签设置viewport
  2. rem单位搭配@media媒体查询:可以通过使用rem单位,它以HTML元素的font-size为比例,也可以搭配 postcss-pxtorem 搭建项目
  3. vw/vh 布局:也可以通过使用vw/vh 布局,vw/vh 方案与 rem 方案类似,都是将页面分成一份一份的,只不过 vw/vh 是将页面分为 100 份,也可以搭配 postcss-px-to-viewport 搭建项目
  4. 百分比布局:也可以使用百分比来实现布局,但是需要特定宽度时,这个百分比的计算对开发者来说并不友好,且元素百分比参考的对象为父元素,元素嵌套较深时会有问题。

25. 什么是Css中的回流(重排)与重绘?

回流(重排)(reflow)和重绘(repaint)是浏览器渲染页面时的两个核心概念。

  • 回流(重排)指的是当页面中的元素发生布局或几何属性发生变化时,浏览器需要重新计算这些元素的位置和大小,然后重新构建页面的渲染树,这个过程称为回流。由于需要重新计算布局,回流的代价很大,会对页面的性能产生负面影响。
  • 重绘指的是当页面中的元素样式发生改变时,浏览器会重新绘制这些元素的外观,但不会改变它们在页面中的位置和大小。重绘的代价相对较小,但仍然会对页面性能产生一定的影响。

*****************************************

26. 常用的跨域解决方案有哪些?

  1. CORS:跨域资源共享(Cross-Origin Resource Sharing),是一种允许浏览器向跨域服务器发送 Ajax 请求的机制,支持现代浏览器,服务器端需要设置 Access-Control-Allow-Origin 头信息,指定允许的源或通配符,从而实现跨域请求。
  2. 代理:在同源页面内部发送 AJAX 请求到同域服务器,由服务器代理转发请求到跨域服务器,最后再将结果返回给同源页面。
  3. WebSocket:WebSocket 是一种 HTML5 协议,它使得浏览器和服务器之间可以建立持久化的连接,可以直接使用 Socket 进行通信,避免了浏览器的跨域限制。

27. Webpack中有哪些核心概念?

  1. Entry(入口):Webpack在打包时需要从哪个文件开始构建依赖关系图,就是入口。可以设置多个入口文件,以生成多个输出文件。
  2. Output(输出):打包后的文件放在哪里,以及如何命名这些文件。可以指定输出目录、文件名、公共路径等。
  3. Loader(模块加载器):Webpack只能处理JavaScript文件,而其他类型的文件如CSS、图片等需要通过Loader转换才能被Webpack处理。Loader用于对模块内容进行转换处理。
  4. Plugin(插件):Plugin可以用于执行各种任务,例如打包优化、错误处理和环境变量注入等。Webpack本身只提供了一些基本的Plugin,但社区中有很多第三方Plugin可供使用。
  5. Mode(模式):Webpack提供了三种模式:development、production和none。不同的模式会启用不同的Webpack内置Plugin和Loader,以便于开发和生产环境的优化。
  6. Chunk(代码块):Webpack在打包时会把所有相关联的模块组成一个Chunk。可以通过Code Splitting技术将代码拆分成多个Chunk,以实现按需加载。
  7. Module(模块):Webpack把每个文件都看作一个模块,它可以是JavaScript、CSS、图片等。这些模块通过依赖关系进行组合,构成整个应用程序。

28. Vite 和 Webpack 的区别

  • 优点:快速的冷启动: 采用No Bundle和esbuild预构建,速度远快于Webpack高效的热更新:基于ESM实现,同时利用HTTP头来加速整个页面的重新加载,增加缓存策略真正的按需加载: 基于浏览器ESM的支持,实现真正的按需加载
  • 缺点生态:目前Vite的生态不如Webapck,不过我觉得生态也只是时间上的问题。生产环境由于esbuild对css和代码分割不友好使用Rollup进行打包

29. Webpack常见的优化方案有哪些?

  1. 升级 webpack 版本,3升4,实测是提升了几十秒的打包速度
  2. 使用Tree Shaking和Scope Hoisting来减少代码体积和模块构建时间,其中Tree Shaking可以去除未使用的代码,而Scope Hoisting可以将模块内的代码尽量合并到一个函数(单一作用域)中,以减少函数声明和闭包的数量。
  3. 使用splitChunksPlugin插件来将公共代码抽离成单独的chunk,以减少代码重复和提高缓存命中率。
  4. 合理配置resolve.alias和resolve.extensions选项来减少Webpack查找文件的时间。
  5. 针对生产环境,可以开启代码压缩以及多进程并行处理等优化方式,以减少构建时间和服务器负载。
  6. 使用DLLPlugin和DllReferencePlugin来预先编译一些稳定不变的代码,以减少每次构建的时间。
  7. 使用HappyPack来启用多线程并发处理,以加速代码构建和增强开发体验。
  8. 对于图片、字体等资源文件,可以通过url-loader和file-loader等loader设置较小的limit值,将文件转换成base64编码的字符串内嵌在js文件中,以减少http请求次数。

30. 浏览器中强缓存与协商缓存的缓存机制

强缓存和协商缓存是两种不同的缓存策略。

强缓存通过设置响应头中的Cache-Control或Expires字段,告诉浏览器在一定时间内直接使用本地缓存,不需要发送请求到服务器。只有当缓存过期或被清除时,浏览器才会发送请求到服务器获取新的资源。

协商缓存通过设置响应头中的ETag和Last-Modified字段,浏览器在每次请求时携带If-None-Match和If-Modified-Since字段,与服务器进行比较。如果资源未发生变化,则服务器返回304 Not Modified状态码,并告知浏览器继续使用缓存;如果资源发生变化,则服务器返回新的资源。

强缓存是基于时间的缓存控制,而协商缓存则是通过与服务器进行交互来判断是否使用缓存,更加灵活。

*****************************************

全部评论

相关推荐

36 162 评论
分享
牛客网
牛客企业服务