> 文章列表 > JavaScript每日五题面试题(第三天)

JavaScript每日五题面试题(第三天)

JavaScript每日五题面试题(第三天)

1、常见的内存泄漏案例?

  • 意外的全局变量: 由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
  • 被遗忘的计时器或回调函数: 设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
  • 脱离 DOM 的引用: 获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。
  • 闭包: 不合理的使用闭包,从而导致某些变量一直被留在内存当中。

2、如何避免内存泄漏?

记住一个原则:不用的东西,及时归还。

  1. 减少不必要的全局变量,使用严格模式避免意外创建全局变量。
  2. 在你使用完数据后,及时解除引用(闭包中的变量,dom引用,定时器清除)。
  3. 组织好你的逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。

3、什么是回流和重绘?

  • 第一种回答
    • 回流
      • 当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做回流。回流也叫重排,简单的说就是重新生成布局,重新排列元素。
      • 下面几种情况会发生回流:
        • 页面初始渲染,这是开销最大的一次重排
        • 添加/删除可见的DOM元素
        • 改变元素位置
        • 改变元素尺寸,比如边距、填充、边框、宽度和高度等
        • 改变元素内容,比如文字数量,图片大小等
        • 改变元素字体大小
        • 改变浏览器窗口尺寸,比如resize事件发生时
        • 激活CSS伪类(例如::hover)
        • 设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow
        • 查询某些属性或调用某些计算方法:offsetWidth、offsetHeight等,除此之外,当我们调用 getComputedStyle方法,或者IE里的 currentStyle 时,也会触发重排,原理是一样的,都为求一个“即时性”和“准确性”。
    • 重绘
      • 一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。
  • 第二种回答:(偏向底层)
    • 回流
      • reflow 的本质就是重新计算 layout 树。当进行了会影响布局树的操作后,需要重新计算布局树,会引发 layout。为了避免连续的多次操作导致布局树反复计算,浏览器会合并这些操作,当 JS 代码全部完成后再进行统一计算。所以,改动属性造成的 reflow 是异步完成的。也同样因为如此,当 JS 获取布局属性时,就可能造成无法获取到最新的布局信息。浏览器在反复权衡下,最终决定获取属性立即 reflow。
    • 重绘
      • repaint 的本质就是重新根据分层信息计算了绘制指令。当改动了可见样式后,就需要重新计算,会引发 repaint。由于元素的布局信息也属于可见样式,所以 reflow 一定会引起 repaint。

4、apply、call、bind三个方法的区别?

applycallbind都可以为函数指定this
applycall 就是传参方式不一样,apply 参数以一个数组的形式传入。但是两个都是会在调用的时候同时执行调用的函数。bind则会返回一个绑定了this的函数。

  • 实现call

    • const myCall = function(context,...args) {context = context || window;args = args ? args : [];const key = Symbol();context[key] = this;const result = context[key](...args);delete context[key];return result;
      }
      
  • 实现apply

    • const myApply = function(context,args) {// 不传this默认指向windowcontext = context || window;args = args ? args : [];// 给context新增一个独一无二的属性以免覆盖原有属性const key = Symbol();// this 指向调用它的对象context[key]=this;// 通过隐式绑定的方式调用函数const result = context[key](...args);// 删除添加的属性delete context[key];return result
      }
      
  • 实现bind

    • const myBind = function(context,...args){const self = this;args = args?args:[];return function(...newArgs) {return self.apply(context,[...args,...newArgs])}
      }
      

5、为什么transform的效率高?

因为 transform 既不会影响布局也不会影响绘制指令,它影响的只是渲染流程的最后一个「draw」阶段。由于 draw 阶段在合成线程中,所以 transform 的变化几乎不会影响渲染主线程。
反之,渲染主线程无论如何忙碌,也不会影响 transform 的变化。