day08 JS 柯里化 以及 V8引擎的垃圾回收

233 阅读4分钟

函数柯里化是为了简化代码, 有以下应用


  • 事件监听
var addEvent = (function () {
  if (window.addEventListener) {
    return function(ele, type, fn) {
      ele.addEventListener(type, fn);
    }
  } else if (window.attachEvent) {
    return function (ele, type, fn) {
      ele.attachEvent("on" + type, fn)
    }
  }
})()

  • 防抖和函数节流
// 防抖
function debounce(fn, delay) {
  var timer;
  return function () {
    var that = this;
    innerArgs = [].slice.call(arguments);
    clearTimeout(timer);
    timer = setTimeout(function () {
      fn.apply(that, innerArgs);
    },delay)
  }
}

// 使用
window.onscoll = debounce(function () {}, 1000)

  • 节流
// 节流
function throtte (fn, delay) {
  var prevTime = +new Date();
  return function () {
    var curTime = +new Date();
    if (curTime - prevTime > delay) {
      fn.apply(this, argument);
      prevTime = curTime;
    }
  }
}

// 使用
window.throtteScoll = throtte(function () {}, 500);

  • 缓存参数(避免重复传参)
var currying = function (fn, currentArgs) {
  return function () {
    let args = [].slice.call(arguments);
    // console.log(args)
    if (currentArgs) {
      args = args.concat(currentArgs);
    }
    // 递归调用
    if (args.length < fn.length) {
      return currying(fn, args);
    } else {
      // console.log(args);
      return fn.apply(null, args);
    }
  }
}


const sum = function (a, b, c) {
  return a + b + c;
}
const fn = currying(sum);
console.log(fn(1)(2)(3))

- 垃圾回收

为何需要垃圾回收

我们知道,在V8引擎逐行执行JavaScript代码的过程中,当遇到函数的情况时,会为其创建一个函数执行上下文(Context)环境并添加到调用堆栈的栈顶,函数的作用域(handleScope)中包含了该函数中声明的所有变量,当该函数执行完毕后,对应的执行上下文从栈顶弹出,函数的作用域会随之销毁,其包含的所有变量也会统一释放并被自动回收。试想如果在这个作用域被销毁的过程中,其中的变量不被回收,即持久占用内存,那么必然会导致内存暴增,从而引发内存泄漏导致程序的性能直线下降甚至崩溃,因此内存在使用完毕之后理当归还给操作系统以保证内存的重复利用。

V8引擎的内存限制

默认情况下,V8引擎在64位系统下最多只能使用约1.4GB的内存,在32位系统下最多只能使用约0.7GB的内存。

为什么会有这种限制呢?这个要回到V8引擎的设计之初,起初只是作为浏览器端JavaScript的执行环境,在浏览器端我们其实很少会遇到使用大量内存的场景,因此也就没有必要将最大内存设置得过高。但这只是一方面,其实还有另外两个主要的原因:

  • JS单线程机制

单线程意味着执行的代码必须按照顺序去执行,在同一时间只能处理一个任务。这也就意味着,在垃圾回收的时候,程序中的各项逻辑都必须等待垃圾回收完成之后才能继续执行。垃圾回收阻塞了主进程逻辑的执行。

  • 垃圾回收机制 垃圾回收本身也是一件非常耗时的操作,假设V8的堆内存为1.5G,那么V8做一次小的垃圾回收需要50ms以上,而做一次非增量式回收甚至需要1s以上,可见其耗时之久,而在这1s的时间内,浏览器一直处于等待的状态,同时会失去对用户的响应,如果有动画正在运行,也会造成动画卡顿掉帧的情况,严重影响应用程序的性能。因此如果内存使用过高,那么必然会导致垃圾回收的过程缓慢,也就会导致主线程的等待时间越长,浏览器也就越长时间得不到响应

基于以上两点,V8引擎为了减少对应用的性能造成的影响,采用了一种比较粗暴的手段,那就是直接限制堆内存的大小,毕竟在浏览器端一般也不会遇到需要操作几个G内存这样的场景。但是在node端,涉及到的I/O操作可能会比浏览器端更加复杂多样,因此更有可能出现内存溢出的情况。不过也没关系,V8为我们提供了可配置项来让我们手动地调整内存大小,但是需要在node初始化的时候进行配置。

V8的垃圾回收策略

V8的垃圾回收策略主要是基于分代式垃圾回收机制,其根据对象的存活时间将内存的垃圾回收进行不同的分代,然后对不同的分代采用不同的垃圾回收算法。

如何避免内存泄露

  • 尽可能少的创建全局变量

  • 手动清除定时器

  • 少用闭包

  • 清楚DOM引用

  • 弱应用

    作者:小维FE 链接:juejin.cn/post/684490… 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。