2021 常见的前端面试题整理

259 阅读10分钟

1.回流和重绘

  1. 回流(reflow), 就是布局引擎为 frame 计算图形, 确定节点位置的一个动作。其中触发回流的原因主要是 DOM节点大小或位置的改变才会触发回流。
  2. 重绘则是表面的视觉效果的改变从而引发重绘。
  3. 其中触发回流就必定会触发重绘, 而触发重绘不一定会触发回流。
  4. 可以在编写代码时根据触发回流重绘的特点有意的控制代码的编写
  5. 可以通过 chrome devtools 的 rendering 面板进行渲染性能分析

如何避免回流

  1. 如果想设定元素的样式,通过改变元素的 class 类名 (尽可能在 DOM 树的最里层)
  2. 避免设置多项内联样式
  3. 应用元素的动画,使用 position 属性的 fixed 值或 absolute 值(fixed 定位只会对自身元素进行渲染, 而不会影响身边的DOM)
  4. 权衡平滑和速度
  5. 避免使用 table 布局
  6. 避免使用 CSS 的 JavaScript 表达式 (仅 IE 浏览器)
  7. 创建多个Dom节点时,可以使用DocumentFragment创建完后一次性加入documnet

2. apply call bind this

共同点:

  • apply、 call、bind 三者都是用来改变this指向的
  • apply、call、bind 三者的第一个参数都是this要指向的对象,也就是指定的上下文

区别:

  • apply、call作用一样,只是传递参数形式不一样。apply传参是数组形式,call传参是按顺序传递进去
  • apply、call函数立即执行,bind返回对应函数,便于稍后调用 注意:
  • 某个函数的参数是明确知道数量时用 call ; 而不确定的时候用 apply,然后把参数 push进数组传递进去
  • 当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数
  • 在Javascript中,多次 bind() 是无效的。 示例如下:
const obj1 = {
  name: '杨幂'
}
const obj2 = {
  name: '赵丽颖'
}
const obj3 = {
  name: '迪丽热巴'
}
function test1 (){
  console.log(this.name);
}
test1.bind(obj1).bind(obj2).bind(obj3)()   //杨幂

3.什么是CDN?

CDN的全称是Content Delivery Network,即内容分发网络。CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。 通俗讲其主要功能就是让在各个不同地点的网络用户,都能够快速访问到网站提供的内容,不会经常出现等待或是卡顿的状况。

4.讲讲输入完网址按下回车,到看到网页这个过程中发生了什么

  1. 域名解析
  2. 发起TCP三次握手
  3. 建立tcp连接后发送http请求
  4. 服务器端响应http请求,浏览器得到html代码
  5. 浏览器解析html,并请求html中的相关资源
  6. 浏览器对页面进行渲染呈现给用户

5.BFC的理解

1. 英文全称 Block Format Context 的缩写
2. 简单理解: 具备BFC特性的元素, 就像被一个容器所包裹, 容器内的元素在布局上不会影响外面的元素。
使用 BFC 的场景:

解决 margin 层叠、文字环绕、浮动元素的父元素高度有误

比较常见的触发 BFC 的条件

  1. 根元素或包含根元素;
  2. 浮动元素 float: left / right;float不是none
  3. 位置属性设置为 position: absolute / fixed;
  4. 行内块元素 display: inline-block;
  5. 表格单元格 display: table-cell;
  6. overflow 值不为 visible 的块元素;
  7. 弹性元素 display: flex;
  8. 网格元素 display: grid;

6.前端性能优化

1. 原则

  • 多使用内存、缓存或者其他办法
  • 减少cpu计算,减少网络请求
  • 减少IO操作(硬盘读写)

2.加载资源优化

  • 静态资源的合并和压缩
  • 静态资源缓存(浏览器缓存策略)
  • 使用cdn让静态资源加载更快

3.渲染优化

  • CSS放在head中,JS放到Body后
  • 图片懒加载
  • 减少Dom操作,对Dom操作进行缓存
  • 事件节流
  • 尽早执行操作 DOMContentLoaded

4.异步加载

  • 动态脚步加载
  • <script src="script.js"></script> 没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素> 之前,也就是说不等待后续载入的文档元素,读到就加载并执行。
  • <script async src="script.js"></script> 有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。
  • <script defer src="myscript.js"></script> 有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所> > 有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

5.浏览器缓存

  • 强缓存
  • 弱缓存

6.DNS预解析

<meta http-equiv="x-dns-prefetch-control" content="on">

<link rel="dns-prefetch" href="//www.zhix.net">

在一些浏览器的a标签是默认打开dns预解析的,在https协议下dns预解析是关闭的,加入meta后会打开。

7.防抖与节流

防抖与节流

效果:

  • 防抖: 固定时间内,如果有新的触发,则重新开始进时
  • 节流: 固定时间内,如果有新的触发,则取消本次操作

使用场景:

  • 防抖:适合复杂运算的时候不要让它们过于频繁的执行,等页面“冷静”下来再去执行。
  • 节流:必须要间隔固定时间执行一次,ui界面等高优先级的事情让它去做。
// 防抖
function debounce(fn, delay = 500) {
    // timer 是闭包中的
    let timer = null

    return function () {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        }, delay)
    }
}
   
// 节流
function throttle(fn, delay = 100) {
   let timer = null

   return function () {
       if (timer) {
           return
       }
       timer = setTimeout(() => {
           fn.apply(this, arguments)
           timer = null
       }, delay)
   }
}

8.高阶组件

  • 高阶组件接收组件作为参数,并返回一个新的组件。高阶组件是一个函数,并不只是一个组件。
  • 高阶组件必须是纯函数,无副作用
  • 利用高阶组件可以将我们的代码写的更加优雅

高阶组件示例

export default connect(mapStateToProps, mapDispatchToProps)(memo(HYRecommend));

9.事件委托

原理:

利用冒泡的原理,把事件加到父级上,触发执行效果。 通俗的讲,事件就是onclick,onmouseover,onmouseout等事件,委托呢,就是让别人来做,这个事件本来是加在某些元素上的,然而你却加到别人身上来做,完成这个事件。

优点

  • 1.可以大量节省内存占用,减少事件注册。比如ul上代理所有li的click事件就很不错。
  • 2.可以实现当新增子对象时,无需再对其进行事件绑定,对于动态内容部分尤为合适

缺点:

事件代理的常用应用应该仅限于上述需求,如果把所有事件都用事件代理,可能会出现事件误判。即本不该被触发的事件被绑定上了事件。

10.清除浮动

原因: 清除浮动主要是为了解决,父元素因为子级元素浮动引起的内部高度为0的问题

清除浮动后:

  • 方案一:

额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(不推荐\color{red}{不推荐}) 优点: 通俗易懂,方便 缺点:创建了额外无意义的标签

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .father {
      border: 1px #333 solid;
      background-color: yellow;
    }

    .child1,
    .child2 {
      float: left;
      height: 50px;
      width: 100px;
      border: 1px solid #a55724;
    }

    .extra {
      clear: both;
    }
  </style>
</head>

<body>
  <div class="father">
    <div class="child1">1</div>
    <div class="child2">2</div>
    <div class="extra"></div>
  </div>
</body>

</html>
  • 方案二:

通过触发BFC方式,实现清除浮动。 父级添加overflow属性(父元素添加overflow:hidden)(不推荐\color{red}{不推荐}

.father {
      border: 1px #333 solid;
      background-color: yellow;
      overflow: hidden;
    }

优点:代码简洁 缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素

  • 方案三

使用after伪元素清除浮动(推荐使用)

.clearFix::after {
      content: '';
      height: 0;
      clear: both;
      display: block;
      visibility: hidden;
    }

    .clearFix {
      *zoom: 1;
      /*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行*
    }
    

 <body>
  <div class="father clearFix">
    <div class="child1">1</div>
    <div class="child2">2</div>
  </div>
</body>
  • 方案四 使用before和after双伪元素清除浮动

    .clearFix:after,.clearFix:before{
        content: "";
        display: table;
    }
    .clearFix:after{
        clear: both;
    }
    .clearFix{
        *zoom: 1;
     }

11.异步编程方案

  • 回调函数 优点: 简单 容易理解 缺点: 不利于维护,耦合度高

  • 事件监听(采用事件驱动模式,取决于某个事件是否发生) 优点: 容易理解,可以绑定多个事件,每个事件可以指定多个回调函数 缺点: 事件驱动,流程不够清晰

  • 发布订阅模式

  • promise 优点:可以利⽤then⽅法,进⾏链式写法;可以书写错误时的回调函数; 缺点:编写和理解,相对⽐较难

  • Generator函数 优点:函数体内外的数据交换、错误处理机制 缺点:流程管理不⽅便

  • async函数 优点:内置执⾏器、更好的语义、更⼴的适⽤性、返回的是Promise、结构清晰。 缺点:错误处理机制

12.服务端渲染和客户端渲染

概念:

  • 服务端渲染(SSR) :页面上的内容,在服务器端已经生成好了,服务器将内容返回给浏览器,浏览器再直接显示出来
  • 客户端渲染(CSR): 一个网页是由JS网页渲染出来的,不是服务器直接返回回来的。

常见的react项目 客户端渲染流程

  1. 浏览器发送请求
  2. 服务器返回html
  3. 浏览器发送bundle.js请求
  4. 服务器返回bundle.js
  5. 浏览器执行bundle·js中的react代码

对比:

  • 首屏加载时间CSR相对于SSR时间会长一些,所以SSR用户体验更好。
  • 针对SEO优化,只能使用SSR,不能使用CSR。原因:爬虫软件只能识别HTML中的内容,不能识别JS中的内容。

13. 数组扁平化

let arr = [[1,3,4],[33,55,22],[6,7,[66,6.987,88,[5,8]]]];

//输出结果:[1, 3, 4, 33, 55, 22, 6, 7, 66, 6.987, 88, 5, 8]

//方法一: 用Es6 提供的数组方法
const res1 = arr.flat(Infinity);
//方法二: 
const res2 = arr.toString().split(',').map(item => parseFloat(item));
//方法三: 
const res3 = JSON.stringify(arr).replace(/\[|\]/g,'').split(',').map(item => parseFloat(item))

//方法四:
while(arr.some(item => Array.isArray(item))){
  arr = [].concat(...arr)
}

//方法五
自己手写用递归代码实现

14. 在JS中,如何让(a==1 && a==2 && a == 3)(宽松相等)的值为true?

if (a == 1 && a == 2 && a == 3) {
  console.log('a为何值时 相等')
}
  • 方法一: 利用valueOf或者toString方法
var a = {
  x: 0,
  valueOf:function(){
    return ++this.x;
  }
}

var a = {
  x: 0,
  toString:function(){
    return ++this.x;
  }
}

  • 方法二: 利用toString方法的变形
var a = [1, 2, 3];
a.toString = function () {
  return a.shift()
}

  • 方法三: 数据劫持
var i = 0
Object.defineProperty(window, 'a', {
  get: function () {
    return ++i;
  },
})

15. memo、useMemo、useCallback、useEffect用法总结

总结:

  • 在子组件不需要父组件的值和函数的情况下,只需要使用memo函数包裹子组件即可
  • 如果有函数传递给子组件,父组件上需用useCallback包裹传递的函数在进行传递
  • 如果有值传递给子组件,使用useMemo
  • useEffect会用来处理副作用
  • useMemo、useCallback、useEffect都是自带闭包的。也就是说,每一次组件渲染都会捕获当前组件函数上下文中的状态(state、props),所有这三种hooks的执行,反映的也是当前状态,你无法使用他们来捕获上一次的状态,对于这种情况,我们应使用ref来访问

16.浏览器的运行机制

    1. 构建Dom树
    • 浏览器会将HTML解析成一个DOM树,DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
    • 将CSS解析成 CSS Rule Tree
    1. 构建渲染树
    • 根据DOM树和CSSOM来构造 Rendering Tree。注意:Rendering Tree 渲染树并不等同于 DOM 树,因为一些像Header或display:none的东西就没必要放在渲染树中了
    1. 布局渲染树
    • 有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。下一步操作称之为layout,顾名思义就是计算出每个节点在屏幕中的位置。
    1. 绘制渲染树
    • 再下一步就是绘制,即遍历render树,并使用UI层绘制每个节点