How JavaScript Work 学习笔记(二)

本文是我看 How JavaScript Work 系列文章的学习笔记。
英文原文地址:https://blog.sessionstack.com/tagged/tutorial
中文翻译版地址:https://github.com/Troland/how-javascript-works

必须理解 JavaScript 的内存管理

虽然 JavaScript 看似是做到了自动释放内存,但其实这种自动的能力只是一种内存管理策略,代码编写不当必然会引起内存泄漏。所以想写好 JavaScript 必须理解内存管理。

内存的生命周期

内存生命周期一共分为3步,分别是:

  • 分配内存
  • 使用内存
  • 释放内存

JavaScript 中的内存

分配内存

在 JavaScript 中分配内存的方式就是定义变量、对象或方法:

var n = 374; // 为数字分配内存
var s = 'sessionstack'; // 为字符串分配内存

var o = {
  a: 1,
  b: null
}; // 为对象及其值分配内存

var a = [1, null, 'str']; // (类似对象)为数组及其数组元素值分配内存

function f(a) {
  return a + 3;
} // 分配一个函数(一个可调用对象)

// 函数表达式也分配一个对象
someElement.addEventListener('click', function() {
  someElement.style.backgroundColor = 'blue';
}, false);

使用内存

使用内存就是对所定义的变量、对象或函数进行读写操作。读就是获取变量、写则是赋值变量、修改对象、函数传参等行为。

释放内存

释放内存有两种算法方案:引用计数和标记清除。

引用计数就是指记录内存被引用的次数,如果次数变为 0 则可以释放。
标记清除则复杂一些,下面是标记清除的逻辑:

  • 根:一般来说,根指的是代码中引用的全局变量。就拿 JavaScript 来说,window 对象即是根的全局变量。Node.js 中相对应的变量为 "global"。垃圾回收器会构建出一份所有根变量的完整列表。
  • 随后,算法会检测所有的根变量及他们的后代变量并标记它们为激活状态(表示它们不可回收)。任何根变量所到达不了的变量(或者对象等等)都会被标记为内存垃圾。
  • 最后,垃圾回收器会释放所有非激活状态的内存片段然后返还给操作系统。

我理解就是通过遍历变量标记激活的变量,并且将失效的变量进行内存释放。

这两种方案中标记清除是 JavaScript 最常用的释放内存方案。

四种常见的 JavaScript 内存泄漏

  • 全局变量 —— 由于 JavaScript 在非严格模式下会为未定义的变量定义一个全局变量,就会造成一些变量的内存泄漏。
  • 定时器和被遗忘的回调函数 —— 回调函数作为函数必然是占用内存的,如果回调函数被定时器、事件监听器使用,函数所在内存就不会被释放。
  • 闭包 —— 闭包能够长期持有函数内变量数据想必大家都知道,所以也要注意在不用闭包的时候释放内存。
  • 源自 DOM 的引用

内存优化方案总结

  1. 使用 CSS3、SVG、IconFont、Canvas 替代图片。展示大量图片的页面,建议使用 Canvas 渲染而非直接使用img标签。具体详见 Javascript的Image对象、图像渲染与浏览器内存两三事。
  2. 适当压缩图片,可减小带宽消耗及图片内存占用。
  3. 使用恰当的图片尺寸,即响应式图片,为不同终端输出不同尺寸图片,勿使用原图缩小代替 ICON 等,比如一些图片服务如 OSS。
  4. 使用恰当的图片格式,如使用WebP格式等。详细图片格式对比,使用场景等建议查看web前端图片极限优化策略。
  5. 按需加载及按需渲染图片。
  6. 预加载图片时,切记要将 img 对象赋为 null,否则会导致图片内存无法释放。当实际渲染图片时,浏览器会从缓存中再次读取。
  7. 将离屏 img 对象赋为 null,src 赋为 null,督促浏览器及时回收内存及像素格式内存。
  8. 将非可视区域图片移除,需要时再次渲染。和按需渲染结合时实现很简单,切换 src 与 v-src 即可。

我只是个搬运工

声明下,本文是我的学习笔记,原汁原味还请查看原文

你可能感兴趣的:(How JavaScript Work 学习笔记(二))