读JavaScript高级程序设计系列之一(你所不知道的闭包)

370 阅读3分钟

从定义上讲

  • 闭包是有权访问另一个函数作用域值中的变量的函数。

创建的闭包常见方式

  • 就是再一个函数内部创建另一个函数
在理解闭包之前,我们不忙先理解下作用域链
  • 作用域链本质上是 一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
  • 无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲, 当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。 但是,闭包的情况又有所不同。

接下来出现活动对象这一概念:

  • 在一个函数对象被调用的时候,会创建一个活动对象,首先将该函数的每个形参和实参,都添加为该活动对象的属性和值;将该函数体内显示声明的变量和函数,也添加为该活动的的属性(在闭包内指外部函数)
  • ​ 然后将这个活动对象做为该函数执行环境的作用域链的最前端,并将这个函数对象的[[scope]]属性里作用域链接入到该函数执行环境作用域链的后端。(闭包内这个函数指:内部函数)

一般而言,普通的函数调用,不含闭包情况:

  • 全局环境的对象始终存在,而像函数内的局部环境的变量对象,则只有在函数执行的过程中存在。
  • 在创建函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]]属性中。
  • 当调用函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。
含闭包函数的调用:
  • 在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。
  • 在匿名函数,从外部函数调用中被返回后,它的作用域链被初始化为包含外部函数的活动对象和全局变量对象。这样,匿名函数就可以访问在外部函数中定义的所有变量。更为重要的是,外部函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换 句话说,当外部函数调用后,其执行环境的作用域链会被销毁,但它的活 动对象仍然会留在内存中;直到匿名函数被销毁后,外部函数的活动对象才会 被销毁。 

闭包中使用this会导致一些问题

  • 在闭包中使用 this 对象也可能会导致一些问题。我们知道,this 对象是在运行时基于函数的执 行环境绑定的:在全局函数中,this 等于 window,而当函数被作为某个对象的方法调用时,this 等 于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。但有时候 由于编写闭包的方式不同,这一点可能不会那么明显
  • 内存泄露
  • 。。。