简答题

1、什么是防抖和节流?有什么区别?如何实现?

防抖

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间

  • 思路:

每次触发事件时都取消之前的延时调用方法

function debounce(fn) {
      let timeout = null; // 创建一个标记用来存放定时器的返回值
      return function () {
        clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
        timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
          fn.apply(this, arguments);
        }, 500);
      };
    }
    function sayHi() {
      console.log('防抖成功');
    }

    var inp = document.getElementById('inp');
    inp.addEventListener('input', debounce(sayHi)); // 防抖

节流

高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率

  • 思路:

每次触发事件时都判断当前是否有等待执行的延时函数

function throttle(fn) {
      let canRun = true; // 通过闭包保存一个标记
      return function () {
        if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
        canRun = false; // 立即设置为false
        setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
          fn.apply(this, arguments);
          // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
          canRun = true;
        }, 500);
      };
    }
    function sayHi(e) {
      console.log(e.target.innerWidth, e.target.innerHeight);
    }
    window.addEventListener('resize', throttle(sayHi));

2、 get请求传参长度的误区、get和post请求在缓存方面的区别

误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。

实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,我们必须再次强调下面几点:

  • HTTP 协议 未规定 GET 和POST的长度限制
  • GET的最大长度显示是因为 浏览器和 web服务器限制了 URI的长度
  • 不同的浏览器和WEB服务器,限制的最大长度不一样
  • 要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度 8182byte

补充补充一个get和post在缓存方面的区别:

  • get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
  • post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。

3、模块化发展历程

可从IIFE、AMD、CMD、CommonJS、UMD、webpack(require.ensure)、ES Module、

  • 使用 visibility替换 display: none,因为前者只会引起重绘,后者会引发回流(改变了布局)
    把 DOM 离线后修改,比如:先把 DOM 给 display(有一次 Reflow),然后你修改100次,然后再把它显示出来
    不要把 DOM 结点的属性值放在一个循环里当成循环里的变量for(let i = 0; i < 1000; i++) {// 获取 offsetTop 会导致回流,因为需要去获取正确的值console.log(document.querySelector(‘.test’).style.offsetTop)}
  • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
  • 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame
  • CSS 选择符从右往左匹配查找,避免 DOM 深度过深
  • 将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于 video标签,浏览器会自动将该节点变为图层。
  • react、Vue

    1、写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

    vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中(建议先了解一下diff算法过程)。在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。vue部分源码如下:

    // vue项目  src/core/vdom/patch.js  -488行
    // 以下是为了阅读性进行格式化后的代码
    
    // oldCh 是一个旧虚拟节点数组
    if (isUndef(oldKeyToIdx)) {
      oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
    }
    if(isDef(newStartVnode.key)) {
      // map 方式获取
      idxInOld = oldKeyToIdx[newStartVnode.key]
    } else {
      // 遍历方式获取
      idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
    }

    创建map函数

    function createKeyToOldIdx (children, beginIdx, endIdx) {
      let i, key
      const map = {}
      for (i = beginIdx; i <= endIdx; ++i) {
        key = children[i].key
        if (isDef(key)) map[key] = i
      }
      return map
    }

    遍历寻找

    // sameVnode 是对比新旧节点是否相同的函数
     function findIdxInOld (node, oldCh, start, end) {
        for (let i = start; i < end; i++) {
          const c = oldCh[i]
          
          if (isDef(c) && sameVnode(node, c)) return i
        }
      }

    2、React 中 setState 什么时候是同步的,什么时候是异步的?

    在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。

    **原因:**在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this.state,但是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state

    3、下面输出什么

    class Example extends React.Component {
      constructor() {
        super();
        this.state = {
          val: 0
        };
      }
      
      componentDidMount() {
        this.setState({val: this.state.val + 1});
        console.log(this.state.val);    // 第 1 次 log
    
        this.setState({val: this.state.val + 1});
        console.log(this.state.val);    // 第 2 次 log
    
        setTimeout(() => {
          this.setState({val: this.state.val + 1});
          console.log(this.state.val);  // 第 3 次 log
    
          this.setState({val: this.state.val + 1});
          console.log(this.state.val);  // 第 4 次 log
        }, 0);
      }
    
      render() {
        return null;
      }
    };
    1、第一次和第二次都是在 react 自身生命周期内,触发时 isBatchingUpdates 为 true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,所以打印时获取的都是更新前的状态 0
    
    2、两次 setState 时,获取到 this.state.val 都是 0,所以执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次。设置完成后 state.val 值为 1
    
    3、setTimeout 中的代码,触发时 isBatchingUpdates 为 false,所以能够直接进行更新,所以连着输出 23
    
    输出: 0 0 2 3

    4、为什么虚拟dom会提高性能?

    虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。

    具体实现步骤如下:

    用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中

    当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异

    把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

    css

    1、分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景

    结构:display:none: 会让元素完全从渲染树中消失,渲染的时候不占据任何空间, 不能点击, visibility: hidden,渲染元素继续占据空间,只是内容不可见,不能点击 opacity: 0: 不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击

    继承:display: none:是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示。visibility: hidden:是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式。

    性能:displaynone : 修改元素会造成文档回流,读屏器不会读取display: none元素内容,性能消耗较大 visibility:hidden: 修改元素只会造成本元素的重绘,性能消耗较少读屏器读取visibility: hidden元素内容 opacity: 0 :修改元素会造成重绘,性能消耗较少

    联系:它们都能让元素不可见

    2、清除浮动的方式有哪些?比较好的是哪一种?

    常用的一般为三种.clearfix, clear,overflow;

    比较好是 .clearfix,伪元素万金油版本,后两者有局限性.

    .clearfix:after {
      visibility: hidden;
      display: block;
      font-size: 0;
      content: " ";
      clear: both;
      height: 0;
    }
    
    <!--
    为毛没有 zoom ,_height 这些,IE6,7这类需要 csshack 不再我们考虑之内了
    .clearfix 还有另外一种写法,
    -->
    
    .clearfix:before, .clearfix:after {
        content:"";
        display:table;
    }
    .clearfix:after{
        clear:both;
        overflow:hidden;
    }
    .clearfix{
        zoom:1;
    }
    
    <!--
    用display:table 是为了避免外边距margin重叠导致的margin塌陷,
    内部元素默认会成为 table-cell 单元格的形式
    -->

    clear:both,那是贼好的,有时候在容器外就有些问题了, 比如相邻容器的包裹层元素塌陷

    overflow:hidden,可以形成 BFC避免浮动造成的元素塌陷

    4、css sprite 是什么,有什么优缺点

    概念:将多个小图片拼接到一个图片中。通过 background-position 和元素尺寸调节需要显示的背景图案。

    优点:

    1. 减少 HTTP 请求数,极大地提高页面加载速度
    2. 增加图片信息重复度,提高压缩比,减少图片大小
    3. 更换风格方便,只需在一张或几张图片上修改颜色或样式即可实现

    缺点:

    1. 图片合并麻烦
    2. 维护麻烦,修改一个图片可能需要重新布局整个图片,样式

    5、link与@import的区别

    1. link是 HTML 方式, @import是 CSS 方式
    2. link最大限度支持并行下载,@import过多嵌套导致串行下载,出现FOUC
    3. link可以通过rel=“alternate stylesheet”指定候选样式
    4. 浏览器对link支持早于@import,可以使用@import对老浏览器隐藏样式
    5. @import必须在样式规则之前,可以在 css 文件中引用其他文件
    6. 总体来说:link 优于@import

    6、display: block;和display: inline;的区别

    block元素特点:

    1.处于常规流中时,如果width没有设置,会自动填充满父容器 2.可以应用margin/padding 3.在没有设置高度的情况下会扩展高度以包含常规流中的子元素 4.处于常规流中时布局时在前后元素位置之间(独占一个水平空间) 5.忽略vertical-align

    inline元素特点

    1.水平方向上根据direction依次布局

    2.不会在元素前后进行换行

    3.受white-space控制

    4.margin/padding在竖直方向上无效,水平方向上有效

    5.width/height属性对非替换行内元素无效,宽度由元素内容决定

    6.非替换行内元素的行框高由line-height确定,替换行内元素的行框高由height,margin,padding,border决定 7.浮动或绝对定位时会转换为block8.vertical-align属性生效

    7、容器包含若干浮动元素时如何清理浮动

    1. 容器元素闭合标签前添加额外元素并设置clear: both
    2. 父元素触发块级格式化上下文(见块级可视化上下文部分)
    3. 设置容器元素伪元素进行清理推荐的清理浮动方法
    /**
    * 在标准浏览器下使用
    * 1 content内容为空格用于修复opera下文档中出现
    *   contenteditable属性时在清理浮动元素上下的空白
    * 2 使用display使用table而不是block:可以防止容器和
    *   子元素top-margin折叠,这样能使清理效果与BFC,IE6/7
    *   zoom: 1;一致
    **/
    
    .clearfix:before,
    .clearfix:after {
        content: " "; /* 1 */
        display: table; /* 2 */
    }
    
    .clearfix:after {
        clear: both;
    }
    
    /**
    * IE 6/7下使用
    * 通过触发hasLayout实现包含浮动
    **/
    .clearfix {
        *zoom: 1;
    }

    8、PNG,GIF,JPG 的区别及如何选

    GIF:

    1. 8 位像素,256 色
    2. 无损压缩
    3. 支持简单动画
    4. 支持 boolean 透明
    5. 适合简单动画

    JPEG

    1. 颜色限于 256
    2. 有损压缩
    3. 可控制压缩质量
    4. 不支持透明
    5. 适合照片

    PNG

    1. 有 PNG8 和 truecolor PNG
    2. PNG8 类似 GIF 颜色上限为 256,文件小,支持 alpha 透明度,无动画
    3. 适合图标、背景、按钮

    9、display,float,position 的关系

    1. 如果display为 none,那么 position 和 float 都不起作用,这种情况下元素不产生框
    2. 否则,如果 position 值为 absolute 或者 fixed,框就是绝对定位的,float 的计算值为 none,display 根据下面的表格进行调整。
    3. 否则,如果 float 不是 none,框是浮动的,display 根据下表进行调整
    4. 否则,如果元素是根元素,display 根据下表进行调整
    5. 其他情况下 display 的值为指定值 总结起来:绝对定位、浮动、根元素都需要调整display

    10、如何水平居中一个元素

    • 如果需要居中的元素为常规流中 inline 元素,为父元素设置text-align: center;即可实现

    • 如果需要居中的元素为常规流中 block 元素,1)为元素设置宽度,2)设置左右 margin 为 auto。3)IE6 下需在父元素上设置text-align: center;,再给子元素恢复需要的值

    • `

      aaaaaa aaaaaa a a a a a a a a