做用域、闭包、内存泄露

  做用域前端

  做用域指的是变量的有效访问范围。做用域对Javascript有重要意义,了解做用域的工做原理是在性能角度和功能角度理解Javascript的关键。浏览器

  每个JavaScript函数都被表示为对象,是一个函数实例。如下两种定义函数的方式是等价的。闭包

var sayName = function(){
    alert('hello world!');
}

var sayName = new Function('alert("hello world!")');

  函数对象正如其余对象那样,拥有能够被Javascript代码访问的属性,和一系列不能被Javascript代码访问,仅供JavaScript引擎使用的内部属性。其中一个内部属性是[[Scope]]。函数

  内部[[Scope]]属性包含一个函数被建立的做用域中对象的集合。此集合被称为函数的做用域链,它决定哪些数据可由函数访问。此函数做用域链中的每一个对象被称为一个可变对象,每一个可变对象都以“键值对”的形式存在。当一个函数建立后,它的做用域链被填充以对象,这些对象表明建立此函数的环境中可访问的数据。例以下面这个全局函数:性能

function add(num1, num2) {
    var sum = num1 + num2;
    return sum;
}

  当add()函数建立后,它的内部属性指向一个做用域链,该做用域链中被填入一个单独的可变对象,这个可变对象是一个全局对象。此全局对象包含诸如窗口、浏览器和文档之类的访问接口。this

  

  而当add函数运行时会创建一个内部对象,称做“运行期上下文”。一个运行期上下文定义了一个函数运行时的环境。对函数的每次运行而言,每一个运行期上下文都是独一的,因此屡次调用同一个函数就会致使屡次建立运行期上下文(execution context)。当函数执行完毕,运行期上下文就被销毁。spa

  一个运行期上下文有它本身的做用域链,用于标识符解析。当运行期上下文被建立时,它的做用域链被初始化。包括函数的[[Scope]]属性中所包含的对象会按照它们出如今做用域链中的顺序,被复制到运行期上下文的做用域链中。这项工做一旦完成,一个被称做“激活对象”的新对象就为运行期上下文建立好了。此激活对象做为函数执行期的一个可变对象,包含访问全部局部变量,命名参数,参数集合,和this的接口。而后,此对象被推入做用域链的前端。看成用域链被销毁时,激活对象也一同销毁。下图显示了add函数运行时所对应的运行期上下文和它的做用域链。3d

var total = add(5, 10);

  

  须要记住的是两点:code

  1. [[scope]]属性是函数建立到运行时一直存在的,知道函数被销毁后占用的内存才会被释放
  2. 运行期上下文只存在于函数运行期间,函数运行结束后该对象被销毁

  

  闭包、内存泄露对象

  闭包是JavaScript最强大的一个方面,它容许函数访问局部范围以外的数据。

function assignEvents() {
    var id = "xdi9592";
    document.getElementById('save').onclick = function(event){
        saveDocument(id);
    }
}

  当assignEvents()被执行时,一个激活对象被建立,并包含了该函数做用域内全部可访问的变量和函数,其中包括id变量。它将成为运行期上下文做用域链上的第一个对象,全局对象是第二个。当闭包建立时,[[Scope]]属性包含了做用域内全部对象的集合(等于assignEvents运行期上下文的做用域链,即assignEvents的激活对象,全局对象)。

  

  因为闭包的[[Scope]]属性包含与运行期上下文做用域链相同的对象引用,会产生反作用。一般,一个函数的激活对象与运行期上下文一同销毁。当涉及闭包时,运行期上下文对象,以及他的做用域链被销毁,但激活对象就没法销毁,由于引用仍然存在于闭包的[[Scope]]属性中。除非手动接触全部对匿名函数的引用,等到垃圾收集器下次运行时,assignEvents的激活对象才会随着匿名函数一同被销毁。

document.getElementById('save').onclick = null;

  因此在Javascript代码中闭包与非闭包函数相比,须要更多内存开销。在大型网页应用中,这可能会致使内存泄露与难以排查的性能问题。

  随着浏览器的升级,大部分浏览器对于闭包引发的循环引用问题都可以顺利解决。但IE9以前使用非本地JavaScript对象实现DOM对象,对于Javascript对象跟DOM对象使用不一样的垃圾收集器。因此闭包在IE的这些版本中发生循环引用时便会致使内存泄露。

相关文章
相关标签/搜索