Javascript在内存管理方面已经不需要像C/C++那样人工的进行内存的管理和垃圾回收,Javascript会自动对它觉得"不再使用"的变量或引用进行删除和回收,这种机制可以让前端开发人员更加专注于业务逻辑代码的开发。但是随着Javascript被应用到越来越复杂的应用,比如单页面应用、移动HTML5应用、游戏等,所以Javascript的自动垃圾回收和内存管理机制可能已经不能再支撑我们随意的coding,以下将从Javascript的语言层面对内存的使用和优化进行探讨。
1、语言层面的内存管理
1.1 作用域
Javascript的作用域在内存管理中起到了至关重要的作用。Why?
1.2 作用域链
在Javascript中,变量标识符的查找遵循从当前作用域开始向上查找,直到顶层的全局作用域为止,这就是Javascript中的作用域链。
Javascript中每个上下文对象都有一个内部属性[[scope]],该变量保存了当前上下文中所有变量对象的列表。怎样在代码层面维护这个上下文对象的[[scope]]对象,对Javascript的内存管理起到了重要作用。
1.3 闭包
闭包是一个定义在函数内部的函数,目的是为了将函数内部的局部变量带到函数外部。所以由该闭包带出到函数外部的变量并不会在函数执行结束之后就自动销毁,这也是闭包的应用容易引起内存无法释放的主要原因。
2、Javascript的内存回收机制
2.1 作用域与引用
引用是指“代码对对象的访问”这一抽象关系,引用也是Javascript引擎在垃圾回收中最关键的一个机制。在Javascript中,即使是简单的写一行变量的名称而不做任何操作,Javascript引擎也会认为这是对该对象的访问行为,存在对该对象的引用。
3、优化你的Javascript
3.1 善用函数
利用函数将一些不需要Javascript引擎保存其引用关系的变量、 对象进行包装,从而保证可以在函数运行完毕后Javascript引擎对其进行自动回收。
3.2 小心使用全局变量
全局变量的弊端:
- 不容易被回收
- 多人协作时容易混淆
- 在作用域链中容易被干扰
3.3 手工解除变量引用
可以使用data = null;对变量进行手工的引用解除
3.4 善用回调
回调函数相对闭包的好处:
- 回调函数的形参如果是基础数据类型(如字符串、数值),那么该形参只是该数据的复制值,容易被回收
- 利于异步编程,当前比较流行的编程方式
- 回调函数一般也是当前作用域下的一个匿名函数,一旦请求函数执行完毕,回调函数本身也容易被回收
3.5 良好的闭包管理
在 JavaScript
的函數式編程中,函數調用時傳入的參數是基本類型對象,那麼在函數體內得到的形參會是一個複製值,這樣這個值就被當作一個局部變量定義在函數體的作用域內,在完成事件綁定之後就可以對events
變量進行手工解除引用,以減輕外層作用域中的內存佔用了。而且當某個元素被刪除時,相應的事件監聽函數、事件對象、閉包函數也隨之被銷毀回收。