字节跳动 Ads Infra 前端一面

421 阅读13分钟

这套题目总的来说,覆盖了广泛的领域,包含了许多经典问题,是前端面试过程中的高频问题,也是准备前端面试时必须要掌握的题目。

wx公众号:大厂前端面试真题,查看详细答案及百余套面试真题!!

1. 性能优化怎么做,单帧渲染时间和内存是如何计算的,直接看性能指标的?

Web性能优化的关键点包括减少页面加载时间、提高渲染性能和节省内存消耗。常用:减少HTTP请求、延迟加载、缓存机制、压缩文件大小、使用CDN加速、优化图片、懒加载和预加载、减少重绘和重排等优化策略。

关于单帧渲染时间和内存计算,单帧渲染时间是指浏览器在一帧时间内完成页面渲染所需的时间,一般目标是保持在16毫秒以内,以保证流畅的动画效果。内存消耗是指页面加载和运行过程中所占用的内存大小,可以通过浏览器的开发者工具来监测。

直接查看性能指标是一种评估网页性能的方法,常用的性能指标包括页面加载时间(如首次渲染时间、DOMContentLoaded事件触发时间)、资源加载时间、CPU使用率、内存占用等。通过浏览器的开发者工具或性能分析工具可以获取这些指标,并根据指标来评估页面性能,并针对性地进行优化。

2. 权限模型是怎么设计的,前端如何限制,其他还有什么鉴权方式,cookie session?

权限模型设计包括用户角色管理和资源控制,前端通过路由拦截、条件渲染等方式限制权限,其它鉴权方式包括JWT、OAuth等,可使用cookie、session或token进行身份验证和权限控制。

3. jwt的优缺点

JWT(JSON Web Token)的优点包括无状态、跨域支持、可扩展性强;缺点包括一旦签发无法撤回、携带信息量较大、需要额外保护避免被篡改。

4. flex:0 1 auto 分别代表什么?

flex: 0 1 auto 是flex-growflex-shrinkflex-basis的缩写,分别代表flex项的放大比例、缩小比例和初始大小。其中flex-grow指定项目在剩余空间中的放大比例,默认为0;flex-shrink指定项目在空间不足时的缩小比例,默认为1;flex-basis指定项目的初始大小,默认为auto(根据项目内容自动计算)。

5. 浏览器是如何渲染一个html文件的?

浏览器渲染HTML文件的过程包括解析HTML文档构建DOM树、解析CSS样式构建CSSOM树、合并DOM树和CSSOM树形成Render树,然后进行布局(Layout)计算元素位置和大小,最后绘制(Paint)将元素转换为像素,再通过合成(Composite)将像素绘制到屏幕上。在这个过程中,浏览器会按照特定的规则处理脚本、样式表、图片和其他资源,并根据需要执行脚本和处理用户交互事件,实现页面的展示和交互功能。

wx公众号:大厂前端面试真题,查看详细答案及百余套面试真题!!

6. 浏览器的存储方式有哪些,有什么差异,用过indexDB吗?

浏览器的存储方式包括Cookie、Web Storage(LocalStorage和SessionStorage)以及IndexedDB。Cookie是一小段文本信息,存储在用户的计算机上;Web Storage使用键值对存储数据,包括LocalStorage(长期存储)和SessionStorage(会话期间存储);IndexedDB是一个基于JavaScript的高性能、事务型的数据库系统,可以存储大量结构化数据,并支持复杂的查询。

它们之间的主要差异:在于存储容量、数据结构、性能和功能灵活性。IndexedDB,它适合存储大量结构化数据并支持高级查询操作,但使用起来较为复杂。

7. css加载会阻塞dom树的解析吗,会阻塞渲染吗?

CSS加载会阻塞DOM树的解析,但不会阻塞渲染。当浏览器解析HTML文档时,遇到外部CSS文件引用或内嵌样式,浏览器会停止DOM树的构建,直到CSS文件加载完成,然后再继续构建DOM树。这是因为CSS样式可以影响DOM元素的布局和渲染,所以浏览器需要先获取CSS规则以正确计算渲染结果。但即使CSS文件尚未加载完成,浏览器也会继续解析HTML文档并显示内容,只是可能会出现没有样式的"FOUC"(无样式内容闪烁)效果。

8. 讲一下浏览器的事件循环,如果在执行微任务的时候又产生了微任务,这个是在什么时候执行的?

浏览器的事件循环是一种机制,用于处理JavaScript代码中的任务队列。事件循环包括宏任务和微任务两个队列。当执行主线程中的代码时,遇到宏任务(如setTimeout、setInterval等)会被放入宏任务队列,而遇到微任务(如Promise、MutationObserver等)则会被放入微任务队列。

当执行一个宏任务结束后,在渲染之前,浏览器会检查微任务队列并依次执行其中的所有微任务。如果在执行微任务的过程中又产生了新的微任务,那么这些新的微任务会被添加到当前微任务队列的末尾,并在之后依次执行。

换句话说,当执行微任务的时候,如果产生了新的微任务,它们会在当前微任务队列的末尾排队等待执行,直到当前微任务队列为空才会结束。这样保证了微任务的执行优先级高于宏任务,且能够及时响应新产生的微任务。

9. 列举你知道的微任务和宏任务有哪些,process.nextTick是什么时候执行的?

常见的微任务包括Promise、MutationObserver、process.nextTick(Node.js环境下);而常见的宏任务包括setTimeout、setInterval、requestAnimationFrame、I/O操作、事件回调等。

process.nextTick是Node.js中的一个特殊的微任务。它会在当前阶段结束后、下一个阶段开始前执行,即在事件循环的每个阶段之间。具体来说,它会在每次I/O事件的回调执行完毕后、事件循环进入下一个阶段之前执行。这使得process.nextTick的优先级高于其他微任务,可以立即执行,常被用于确保某些操作在下一个阶段之前执行,避免出现竞争条件或异步回调的延迟。

10. node事件循环的6个阶段
  1. Timer阶段:处理定时器回调函数。
  2. I/O callbacks阶段:处理几乎所有的异步I/O回调函数,如网络请求、文件操作等。
  3. Idle, prepare阶段:仅供内部使用。
  4. Poll阶段:等待新的I/O事件,执行与I/O相关的回调函数。
  5. Check阶段:执行setImmediate()注册的回调函数。
  6. Close callbacks阶段:处理关闭的回调函数,例如socket.on('close', ...)

wx公众号:大厂前端面试真题,查看详细答案及百余套面试真题!!

11. 为什么需要有微任务这个概念,可以从执行时机方面考虑?

微任务的引入是为了解决事件循环中执行时机的问题,确保异步操作的回调函数在宏任务执行完毕前得以执行,提高响应性能和用户体验。微任务在主线程空闲时执行,避免阻塞和提高页面渲染流畅度,同时提供更细粒度的控制代码执行顺序。

12. 网络请求xhr是什么任务?

网络请求(XMLHttpRequest)是一种异步任务,通常在浏览器中使用。它允许客户端向服务器发送请求并在不刷新页面的情况下接收响应。当执行XMLHttpRequest时,浏览器会创建一个异步HTTP请求,并在请求成功或失败时调用相应的回调函数。由于XMLHttpRequest是异步任务,因此它可以在等待服务器响应时不阻塞主线程,从而提高了页面的性能和用户体验。

13. promise.all和promise. race,如何处理报错?

在Promise.all中,如果任何一个Promise被reject(即出现错误),整个Promise.all就会被reject,并返回该错误。而在Promise.race中,只要有一个Promise settled(无论是resolve还是reject),整个Promise.race就会 settled,并返回这个Promise的结果或错误。

14. promise.race是什么?

Promise.race是一个Promise方法,接受一个Promise对象组成的数组作为参数。它返回一个新的Promise,当参数中的任意一个Promise对象解决(settled)时,新的Promise也将解决,并获得第一个解决的Promise的值或错误作为结果。

15. promise.all如何实现所有的执行完再返回?

Promise.all是一个Promise方法,接受一个Promise对象组成的数组作为参数。它返回一个新的Promise,当参数中的所有Promise对象都成功解决时,新的Promise也将成功解决,并返回所有Promise对象的结果组成的数组作为结果。Promise.all通过在内部创建一个计数器来实现等待所有Promise对象执行完成。每当一个Promise对象解决后,计数器减一,直到所有的Promise对象都成功解决为止,Promise.all才会返回所有Promise对象的结果组成的数组。如果其中任何一个Promise对象失败,则Promise.all立即失败并返回该错误。

wx公众号:大厂前端面试真题,查看详细答案及百余套面试真题!!

16. new的过程中做了什么,和构造函数有什么关联?

在使用new关键字创建对象时,首先会创建一个新的空对象,然后将这个空对象作为this关键字绑定到构造函数上。接着执行构造函数内部的代码,对新对象进行初始化操作。最后,如果构造函数没有显式返回其他对象,就会隐式返回这个新对象。

因此,new的过程与构造函数紧密相关,它通过构造函数来初始化并返回一个新对象,从而实现了对象的创建和初始化。

17. 讲一下原型和原型链

每个JavaScript对象都有一个原型(prototype),原型又是一个对象。当试图访问一个对象的属性时,如果该对象没有这个属性,JavaScript引擎会沿着原型链向上查找直到找到相应的属性或者到达原型链的末尾(通常是Object.prototype)。

原型链是由对象之间的原型关系构成的,它允许对象继承另一个对象的属性。当我们访问一个对象的属性时,如果对象本身没有这个属性,JavaScript引擎会去它的原型对象中查找,如果还没有,就会顺着原型链一直向上查找,直到找到对应的属性或者到达原型链的末尾。

18. 用过ts吗,讲一下泛型?

在TypeScript中,泛型(Generics)允许我们在定义函数、类或接口时使用参数化类型。通过使用泛型,我们可以编写可重用的代码,以适应不同类型的数据。

泛型可以用于函数参数、函数返回值和类成员的类型注解。通过在尖括号<>中指定泛型参数,可以创建泛型函数或泛型类。泛型参数允许我们在使用函数或类时指定具体的类型,从而提供类型安全和灵活性。

例如,function identity<T>(arg: T): T是一个泛型函数,它接受一个参数并返回相同类型的值。可以使用identity<number>(5)来指定参数类型为number,或者直接调用identity(5),让TypeScript根据参数推断出类型。这样,可以在不同的上下文中使用同一个泛型函数来处理不同类型的数据。

19. 为什么用vue可以直接用this.xxx来赋值,而react不行?

这是因为Vue和React在处理组件的方式上有所不同。

在Vue中,组件的方法默认绑定到组件实例上,因此可以直接通过this.xxx来访问组件实例的属性或方法。

而React中,默认情况下,组件的方法并没有绑定到组件实例上,因此无法直接通过this.xxx来访问组件实例的属性或方法。需要使用箭头函数、显式绑定或者将方法定义为类属性的方式来确保方法内部的this指向组件实例。

20. 为什么react要使用setState显式的设置值?

在React中,使用setState来显式设置组件的状态值,是为了确保状态的一致性和可控性。React将组件的状态视为不可直接修改的,而是通过setState方法进行更新。

通过使用setState,React可以进行状态的批量更新和合并,并且会触发组件的重新渲染,以反映最新的状态。此外,setState还提供了一些优化机制,比如批量更新和异步更新,以提高性能和减少不必要的渲染。

通过明确使用setState来设置状态,React可以更好地管理组件的生命周期和渲染过程,并提供更可靠的状态管理机制。

wx公众号:大厂前端面试真题,查看详细答案及百余套面试真题!!

21. hooks为什么不能在循环或条件语句中执行?

React hooks 不能在循环或条件语句中执行,因为 React 需要依赖 hooks 的调用顺序来正确地管理组件的状态。将 hooks 放在循环或条件语句中可能导致 hooks 调用顺序发生变化,从而造成状态管理混乱和错误。为了确保 hooks 的稳定调用顺序,React 规定 hooks 只能在函数组件的最顶层进行调用。

22. express和koa有什么区别,中间件执行方面?

在中间件执行方面,Express使用基于回调函数的中间件机制,一个中间件函数处理请求后可以通过调用next()将控制权传递给下一个中间件。这种方式简单直观,但在处理异步操作时可能需要额外的工作。

而Koa使用基于Promise的中间件机制,在中间件函数内部可以使用await来处理异步操作,可以更方便地编写和处理异步代码。此外,Koa的中间件机制也更加轻量级和易于扩展。

Koa在中间件执行方面更加现代化和灵活,而Express则更加传统和简单。

23. 题3:大数相加

在JavaScript中,可以使用第三方库如BigInt.js或BigNumber.js来处理大数相加。这些库提供了高精度计算的功能,可以直接对大数进行相加操作,避免了JavaScript数字类型的精度限制。例如,使用BigInt.js库可以通过BigInt对象进行大数相加:BigInt(num1).plus(BigInt(num2)).toString()。

24. 题4:实现一个eventBus(发布订阅)

点击下方,阅读原文,查看详细答案

25. 题5:封装hooks

点击下方,阅读原文,查看详细答案

wx公众号:大厂前端面试真题,查看详细答案及百余套面试真题!!