函数作用域链、闭包、匿名函数的原理分析

      首先我们来看一下JavaScript对执行环境的说明:执行环境定义了变量或函数有权访问的其他数据,决定了他们各子的行为。每个执行环境都有一个与之关联的“变量对象”,这个变量对象实际是上保存在函数的内部属性[[scope]]中的,客户端无法直接获取,但是可以用来为后台解析提供依据。每个函数都有自己的执行环境(也叫作用域),当执行流进入一个函数时,函数的环境就会被推入一环境栈,函数执行之后,栈将环境弹出,然后把控制权返回给之前的执行环境,这套程序跳转机理和底层硬件寄存器的程序运行是一样的,下面我们来具体看一下这个作用域到底都包含什么。

    对于函数而言,它的执行环境中的变量对象是其活动对象,最开始时只包含一个变量,即arguments对象。当执行流进入函数环境时,函数不仅有权访问自己作用域内的变量对象,而且对于其外部变量对象也有访问权,这个访问权限可以一直向外扩展到最外层环境(全局作用域),而这些扩展起来的作用域按访问顺序连接起来就成了作用域链。其实作用域链可以比作一个指针列表,包含了指向其对应变量对象的指针,也就是说作用域链上的内容只是对变量对象的引用,而不直接包含实际的变量对象,这一点对理解后面要将的闭包原理很重要,一句话概括作用域链的作用就是:保证对在访问权限内的所有变量对象的有序访问,这里还要说明一下容易引起混淆的一点:红宝书写过这么一句话,当一个函数被调用执行时,会在其内部创建一个作用域链,包含所有指向有权访问的环境变量的指针。但其实一个函数的作用域链在其定义时就已经被创建好,在其定义之初,内部就已经有了一个作用域链,其中包含“除去自身执行环境外”的所有外部环境,至少包括了全局环境。书中的意思是指在函数被调用执行时,会首先通过复制[[scope]]属性中的对象来构建此执行环境的作用域链,并在其最前端插入当前变量对象。

    一般情况下,当执行流离开一个执行环境时(即函数内的代码执行完毕后),其内部局部环境变量将被销毁,但“闭包”就是我们要说的非一般情况。它只是有权访问其他函数环境变量的函数。通过闭包函数,我们可以“窥视”定义在其他函数中的局部变量对象。这是因为内部的闭包函数的作用域链实际上会包含其外部函数的变量对象。当外部环境执行完毕后,其作用域链被销毁,但注意销毁的只是作用域链(也就是指向变量对象的指针),由于内部函数存在对外部函数环境对象的引用,因此外部变量对象仍存留在内存中,只有当手动将内部闭包函数赋值null,即解除对环境变量的引用时,才会等下一次垃圾回收的到来将其清除。

    而匿名函数其实和闭包并无直接关联,只有定义在其他函数内部的匿名函数才能称作闭包,单独的使用匿名函数,往往是为了创建函数执行环境或者立即执行函数的使用等场合。

你可能感兴趣的:(JavaScript)