内存管理优化
将内存占用量保持在一个较小的值可以让页面性能更好。
优化内存占用的最佳手段就是保证在执行代码时只保存必要的数据。如果数据不再必要,那么把它设置为null
,从而释放其引用。这也可以叫作解除引用 。
这个建议最适合全局变量和全局对象的属性。局部变量在超出作用域后会被自动解除引用。
1. 通过const
和let
声明提升性能
ES6增加这两个关键字不仅有助于改善代码风格,而且同样有助于改进垃圾回收的过程。因为const
和let
都以块(而非函数)为作用域,所以相比于使用var
,使用这两个新关键字可能会更早地让垃圾回收程序介入,尽早回收应该回收的内存。在块作用域比函数作用域更早终止的情况下,这就有可能发生。
2. 隐藏类和删除操作
3. 内存泄漏
JavaScript中的内存泄漏大部分是由不合理的引用导致的。
意外声明全局变量是最常见但也最容易修复的内存泄漏问题。
function setName() { name = 'Jake'; }
定时器也可能会悄悄地导致内存泄漏。只要定时器一直运行,回调函数中引用的name
就会一直占用内存。
let name = 'Jake'; setInterval(() => { console.log(name); }, 100);
使用JavaScript闭包很容易在不知不觉间造成内存泄漏。以下代码执行后创建了一个内部闭包,只要返回的函数存在就不能清理name
,因为闭包一直在引用着它。
let outer = function() { let name = 'Jake'; return function() { return name; }; };
4. 静态分配与对象池(静态分配是优化的一种极端形式。如果你的应用程序被垃圾回收严重地拖了后腿,可以利用它提升性能。但这种情况并不多见。大多数情况下,这都属于过早优化,因此不用考虑)
为了提升JavaScript性能,一个关键问题就是如何减少浏览器执行垃圾回收的次数。
浏览器决定何时运行垃圾回收程序的一个标准就是对象更替的速度。如果有很多对象被初始化,然后一下子又都超出了作用域,那么浏览器就会采用更激进的方式调度垃圾回收程序运行,这样当然会影响性能。
假如这个矢量加法函数频繁被调用,那么垃圾回收调度程序会发现这里对象更替的速度很快,从而会更频繁地安排垃圾回收。
该问题的解决方案是不要动态创建矢量对象。
// 原来 function addVector(a, b) { let resultant = new Vector(); resultant.x = a.x + b.x; resultant.y = a.y + b.y; return resultant; } // 修改优化后 function addVector(a, b, resultant) { resultant.x = a.x + b.x; resultant.y = a.y + b.y; return resultant; }
垃圾回收机制
JavaScript的垃圾回收程序可以总结如下。
- 离开作用域的值会被自动标记为可回收,然后在垃圾回收期间被删除。
- 主流的垃圾回收算法是标记清理,即先给当前不使用的值加上标记,再回来回收它们的内存。
- 引用计数是另一种垃圾回收策略,需要记录值被引用了多少次。JavaScript引擎不再使用这种算法,但某些旧版本的IE仍然会受这种算法的影响,原因是JavaScript会访问非原生JavaScript对象(如DOM元素)。
- 引用计数在代码中存在循环引用时会出现问题。
- 解除变量的引用不仅可以消除循环引用,而且对垃圾回收也有帮助。为促进内存回收,全局对象、全局对象的属性和循环引用都应该在不需要时解除引用。