前端面试每日3+2(第31天)

1/1/2020 前端100问

当你发现自己的才华撑不起你的野心时,就请安静下来学习吧!

鲁迅说过:答案仅供参考...

# 1、(第 21 题)有以下 3 个判断数组的方法,请分别介绍它们之间的区别和优劣

Object.prototype.toString.call() 、 instanceof` 以及 Array.isArray()


🌿【解析】🌿

  1. Object.prototype.toString.call() 每一个继承Object的对象都有toString方法,如果toString方法没有重写的话,会返回[Object type],其中type为对象的类型。但当其它类型直接调用toString方法时,会直接返回的都是内容的字符串,所以我们需要使用call或者apply的方法来改变toString的执行上下文。

    const hi = ['Hello','world'];
    hi.toString(); // "Hellow,world"
    Object.prototype.toString.call(hi); // "[Object Array]"
    
    1
    2
    3

    这种方法对于所有基本的数据类型都能进行判断,即使是 null 和 undefined 。

    Object.prototype.toString.call('An') // "[object String]"
    Object.prototype.toString.call(1) // "[object Number]"
    Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
    Object.prototype.toString.call(null) // "[object Null]"
    Object.prototype.toString.call(undefined) // "[object Undefined]"
    Object.prototype.toString.call(function(){}) // "[object Function]"
    Object.prototype.toString.call({name: 'An'}) // "[object Object]"
    
    1
    2
    3
    4
    5
    6
    7

    Object.prototype.toString.call()常用于判断浏览器内置对象,详细见谈谈 Object.prototype.toString (opens new window)

  2. instanceof

    instanceof 的内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。

    使用 instanceof判断一个对象是否为数组,instanceof 会判断这个对象的原型链上是否会找到对应的 Array 的原型,找到返回 true,否则返回 false。

    [] instanceof Array; // true
    
    1

    instanceof 只能用来判断对象类型,原始类型不可以。并且所有对象类型 instanceof Object 都是 true。

    [] instanceof Object; // true
    
    1
  3. Array.isArray()

  • 功能:用来判断对象是否为数组

  • instanceof 与 isArray

    当检测Array实例时,Array.isArray 优于 instanceof ,因为 Array.isArray 可以检测出 iframes

    var iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    xArray = window.frames[window.frames.length-1].Array;
    var arr = new xArray(1,2,3); // [1,2,3]
    
    // Correctly checking for Array
    Array.isArray(arr);  // true
    Object.prototype.toString.call(arr); // true
    // Considered harmful, because doesn't work though iframes
    arr `instanceof` Array; // false
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • Array.isArray() 与 Object.prototype.toString.call()

    Array.isArray()是ES5新增的方法,当不存在 Array.isArray() ,可以用 Object.prototype.toString.call() 实现。

    if (!Array.isArray) {
    Array.isArray = function(arg) {
        return Object.prototype.toString.call(arg) === '[object Array]';
    };
    }
    
    1
    2
    3
    4
    5

判断JS数据类型的四种方法 (opens new window)
参考答案 (opens new window) --- 感谢【Daily-Interview-Question】 (opens new window)

# 2、(第 22 题)介绍下重绘和回流(Repaint & Reflow),以及如何进行优化


🌿【解析】🌿 :

  1. 浏览器的渲染机制
  • 浏览器采用流式布局模型(Flow Based Layout)
  • 浏览器会把HTML🌿【解析】🌿 成DOM,把CSS🌿【解析】🌿 成CSSOM,DOM和CSSOM合并就产生了渲染树(Render Tree)。
  • 有了RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。
  • 由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一。
  1. 重绘 repaint
    由于节点的几何属性发生改变或者由于样式发生改变而不会影响布局的,称为重绘,例如outline, visibility, color、background-color等,重绘的代价是高昂的,因为浏览器必须验证DOM树上其他节点元素的可见性。

  2. 回流 reflow
    回流是布局或者几何属性需要改变就称为回流。回流是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的回流可能会导致了其所有子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。大部分的回流将导致页面的重新渲染。
    回流必定会发生重绘,重绘不一定会引发回流。

  3. 浏览器优化
    现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。
    主要包括以下属性或方法:

    • offsetTop、offsetLeft、offsetWidth、offsetHeight
    • scrollTop、scrollLeft、scrollWidth、scrollHeight
    • clientTop、clientLeft、clientWidth、clientHeight
    • width、height
    • getComputedStyle()
    • getBoundingClientRect()

    所以,我们应该避免频繁的使用上述的属性,他们都会强制渲染刷新队列。

  4. 减少重绘与回流

    • css

      • 使用 transform 替代 top

      • 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局

      • 避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局。

      • 尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。

      • 避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。

        <div>
            <a> <span></span> </a>
        </div>
        <style>
            span {
                color: red;
            }
            div > a > span {
                color: red;
            }
        </style>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

        对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的 span 标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的 span 标签,然后找到 span 标签上的 a 标签,最后再去找到 div 标签,然后给符合这种条件的 span 标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平。

      • 将动画效果应用到position属性为absolute或fixed的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择 requestAnimationFrame,详见探讨 requestAnimationFrame。

      • 避免使用CSS表达式,可能会引发回流。

      • 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如will-change、video、iframe等标签,浏览器会自动将该节点变为图层。

      • CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。

    • JavaScript

      • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
      • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
      • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
      • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

参考答案 (opens new window) --- 感谢【Daily-Interview-Question】 (opens new window)

# 3、(第 23 题)介绍下观察者模式和订阅-发布模式的区别,各自适用于什么场景


🌿【解析】🌿

  • 观察者模式中主体和观察者是互相感知的,发布-订阅模式是借助第三方来实现调度的,发布者和订阅者之间互不感知。
  • 发布-订阅模式就好像报社, 邮局和个人的关系,报纸的订阅和分发是由邮局来完成的。报社只负责将报纸发送给邮局。
  • 观察者模式就好像 个体奶农和个人的关系。奶农负责统计有多少人订了产品,所以个人都会有一个相同拿牛奶的方法。奶农有新奶了就负责调用这个方法。

参考答案 (opens new window) --- 感谢【Daily-Interview-Question】 (opens new window)

# 4、(第 24 题)聊聊 Redux 和 Vuex 的设计思想


🌿【解析】🌿

Vuex、Flux、Redux、Redux-saga、Dva、MobX (opens new window)
参考答案 (opens new window) --- 感谢【Daily-Interview-Question】 (opens new window)

# 5、(第 25 题)说说浏览器和 Node 事件循环的区别


🌿【解析】🌿

  • 其中一个主要的区别在于浏览器的event loop 和nodejs的event loop 在处理异步事件的顺序是不同的,
  • nodejs中有micro event;其中Promise属于micro event 该异步事件的处理顺序就和浏览器不同.
  • nodejs V11.0以上 这两者之间的顺序就相同了

参考答案 (opens new window) --- 感谢【Daily-Interview-Question】 (opens new window)

Last Updated: 1/29/2020, 9:58:38 PM
    asphyxia
    逆时针向