2023前端面试题

刘兵 / 2023-05-08 / 原文

1.什么是重绘和回流,有哪些措施可以避免回流,从而提高页面性能

重绘(repaint)和回流(reflow)是浏览器渲染页面时的两个重要的步骤。

重绘是指当一个元素的样式(如颜色、背景等)发生变化,但没有影响其布局(如位置、大小等)时,浏览器会将这个元素的新样式重新画到页面上。

回流则是指当一个元素的布局发生变化时,浏览器需要重新计算该元素在页面中的位置和大小,并且需要重新布局相关元素,这个过程可能会导致整个页面其他部分的重新渲染。

由于回流会涉及到多个元素的重新计算和布局,因此它的成本要比重绘更高,会对页面性能产生较大的影响。

为了避免回流,可以采取以下几个措施:

  1. 减少 DOM 操作:DOM 的增删改操作会引起回流,因此尽量减少这些操作,可以通过使用文档碎片、虚拟 DOM 等技术来优化。

  2. 使用 CSS3 动画代替 JavaScript 动画:CSS3 动画由浏览器自己处理,不会触发 JavaScript 脚本,因此可以避免回流。

  3. 避免频繁读取 offsetLeft、offsetTop 等属性:这些属性的读取会导致回流,如果需要多次读取,可以将其缓存起来。

  4. 使用 transform 和 opacity 属性:这两个属性不会触发回流,可以用来实现元素的动画效果。

  5. 避免使用 table 布局:table 的布局方式比较复杂,可能会导致频繁的回流,可以使用 div 布局代替。

通过上述措施的实践,可以显著提高页面性能,使用户的体验更加流畅和舒适。

2.什么是 BFC

BFC(块级格式化上下文)是前端开发中的一个重要概念,它是指一个独立的渲染区域,规定了内部元素如何布局,并且与外部元素互相隔离。在一个BFC中,所有的子元素会根据一定的规则进行布局,这些规则包括:浮动、清除浮动、尺寸计算、边距折叠等。

前端BFC可以解决一些常见的布局问题,例如:父容器和子元素之间的margin折叠问题、浮动元素造成的高度塌陷问题等。通过创建一个BFC,我们可以让元素具有一定的独立性,从而实现更加灵活的布局。

在HTML中,可以通过以下方式来创建一个BFC:

  1. float属性不为none;
  2. position属性为absolute或fixed;
  3. display属性为inline-block、table-cell、table-caption;
  4. overflow属性不为visible。

值得注意的是,创建BFC的元素之间也不会影响彼此的布局,因此在设计复杂页面时,合理利用BFC可以帮助我们简化布局,提高开发效率。

3.vue 实现数据绑定以及页面dom更新机制

Vue实现数据绑定和页面DOM更新的机制,主要依靠响应式系统和虚拟DOM技术。

  1. 响应式系统

Vue使用了一种称为响应式系统的观察者模式来监听数据的变化。当一个组件实例创建时,Vue会遍历data选项中的属性,对每个属性都使用Object.defineProperty方法将其转化为getter/setter,并且在内部建立一个依赖收集器(Dep),用来收集当前属性的所有订阅者(Watcher)。

当数据发生改变时,setter就会被调用,setter会通知依赖收集器向订阅该属性的所有Watcher发送通知,告诉它们数据已经发生了变化。Watcher接收到通知后,会重新计算表达式的值,并且更新视图。

  1. 虚拟DOM技术

Vue使用了虚拟DOM技术来优化DOM操作。当组件的数据发生变化,Vue会先将数据转换为虚拟DOM对象,并将其与上一次渲染的虚拟DOM进行比较,得出需要更新的部分,然后只更新这些部分的真实DOM,而不是全部重新渲染整个组件。这样可以减少DOM操作的次数,提高性能。

总的来说,Vue的数据绑定和DOM更新机制是通过响应式系统和虚拟DOM技术相结合来实现的。通过响应式系统,Vue能够监听数据变化并自动更新视图,而虚拟DOM技术可以优化DOM操作,提高性能。

4.什么是js原型链

JavaScript中的每个对象都有一个原型,也就是它们继承属性和方法的对象。而这个原型又可以有自己的原型,形成了所谓的“原型链”。

当我们访问一个JavaScript对象的属性或方法时,首先会在该对象本身查找是否存在该属性或方法。如果不存在,它会沿着该对象的原型链向上查找,一直到Object.prototype为止。如果还没有找到,则返回undefined。

因此,通过原型链,我们可以实现继承和代码复用。例如,我们可以创建一个基础对象,并将其作为其他对象的原型,使得后者能够共享基础对象的属性和方法。

5.js中event loop,以及浏览器和node的event loop的异同

  • 代码开始执行,创建一个全局调用栈,script作为宏任务执行
  • 执行过程过同步任务立即执行,异步任务根据异步任务类型分别注册到微任务队列和宏任务队列
  • 同步任务执行完毕,查看微任务队列
    • 若存在微任务,将微任务队列全部执行(包括执行微任务过程中产生的新微任务)
    • 若无微任务,查看宏任务队列,执行第一个宏任务,宏任务执行完毕,查看微任务队列,重复上述操作,直至宏任务队列为空
    • 宏任务(script、定时器、ajax、I/O);微任务(promise.then、async await、MutationObserve 监听dom变化)   
      node的事件循环分为 6 个阶段
      浏览器和 Node 环境下,microtask 任务队列的执行时机不同 Node 端,
       microtask(微任务) 在事件循环的各个阶段之间执行 浏览器端,
       microtask 在事件循环的 macrotask 执行完之后执行
6.什么是闭包,闭包的使用场景
7.原生js的事件监听
8.防抖和节流