什么是闭包?

闭包可以这样来理解:闭包是一个函数,它与普通函数之间区别在于,闭包可以访问另一个函数的作用域中的变量对象。闭包可以是一个匿名函数(lambda函数),任何函数都可以被理解为是闭包。

我们来看一个简单的例子:

上面的例子中定义了一个函数CSSer,该函数的执行结果将返回一个匿名函数(闭包)。该匿名函数可以访问CSSer函数中的变量对象title,即使CSSer函数执行完毕,其返回的匿名函数仍然可以访问CSSer函数环境中的变量。

闭包与变量

从闭包的概念中可以了解到,闭包可以访问其外部函数的变量对象,既然是变量对象,必然是变量最终的值,而不是变量某一状态的值,下面看一个很常见的例子:

上述例子用于实现当div被鼠标点击时捕捉其在整个DOM树中的索引位置,乍看是没有问题的,但当实际运行时,就会发现每次捕捉的索引值都是elems.length,而不是我们想要的结果。我们了解了闭包机制之后,很容易解释这个问题,并且给出解决方案:

执行环境

当函数被调用时,会创建一个执行环境。

当进入执行环境时,创建其作用域链,并把作用域链赋给函数的特殊内部属性[[scope]]。

然后使用this、arguments和形参初始化函数的活动对象(即执行环境的变量对象)。这里对于变量对象的初始化过程是有顺序的:

  1. this :所有活动的执行环境都有this值,它依赖并取决于代码执行时的caller。
  2. arguments :创建arguments对象作为函数的arguments属性,arguments对象是一个类数组集合,其length即函数实参的个数,其每一个索 引的值与对应的函数实参都指向同一个值的引用。 函数的arguments属性不可被delete动态删除 。
  3. 函数参数 :创建同名标识符作为property添加到变量对象中。如果调用者提供的参数少于形参,则其它形参值为undefined;如果遇到多个同名形参,最后一个形参值将被保留,如果最后的形参也未被提供值,则其值同样是undefined。
  4. 函数声明 :创建以函数名字为标识符作为property添加到变量对象中,其值为创建的函数对象。如果存在同名的property,则替换掉。
  5. 变量声明 :创建以变量名字为标识符作为property添加到变量对象中,其值为undefined,如果之前变量对象中存在同名property,则其值不变。

从进入执行环境到退出执行环境,完成了函数中代码语句的执行过程,一般情况下,函数执行完毕其执行环境的变量对象将被销毁以释放内存。由于闭包仍保 留着对其外部环境活动变量对象的引用,所以存在特殊性。即使其外部环境的作用域链被销毁,但它的活动对象仍保留在内存中直至闭包被销毁才会被释放。

延长作用域链

Javascript中有两个语句可以延长作用域链:try-catch语句的catch语句块、with语句。所谓延长作用域链即是说在作用域的最前端临时增加变量对象(unshift),该变量对象会在代码执行完毕后被移除(shift),不再深究。【完】

Tagged on:

发表评论