JavaScript > 执行环境、作用域链、闭包

来源:互联网 发布:淘宝助手软件 编辑:程序博客网 时间:2024/06/07 01:34

执行环境与作用域链

以下面的代码为例:

<script language="JavaScript">    var color = "blue";    function changeColor(){        var anotherColor = "red";        function swapColors(){            var tempColor = anotherColor;            anotherColor = color;            color = tempColor;        }        swapColors();    }    changeColor();  </script>

什么是执行环境?执行环境定义了变量或者函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,执行环境中定义的所有变量和函数都保存在这个对象中。

对于上述代码,变量color和function changeColor()属于全局执行环境,在web浏览器中,全局执行环境被认为是window对象,因此全局变量color和function changeColor都是作为window对象的属性和方法创建的。

每个函数都有自己的执行环境,所以全局方法changeColor也有自己的执行环境,对应的变量对象中包含变量anotherColor和函数swapColor()。

以此类推,函数swapColor()也有自己的执行环境,对应的变量对象中包含变量tempColor。

当执行流进入一个函数时,函数的执行环境就会被推入一个环境栈中,而在函数执行之后,栈将其环境弹出,把控制权交给之前的执行环境。

某个执行环境中所有的代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁,全局执行环境直到应用程序退出,例如关闭网页或者浏览器时才会被销毁。

什么是作用域链?当代码在一个执行环境中执行时,会创建当前执行环境的作用域链,作用域链上的内容就是各层执行环境的变量对象。比如代码在changeColor()函数的局域执行环境中执行时,创建的作用域链包含两个变量对象:一个是changeColor()函数对应的执行环境的变量对象,另一个是全局执行环境对应的变量对象。作用域链的最前端是当前执行环境的变量对象,即changeColor()函数局域执行环境的变量对象。

标识符的解析,就是沿着作用域链一级一级搜索标识符的过程。比如代码执行到函数swapColors()时,作用域链上有三个变量对象,从下往上分别是:swapColors()函数自己的执行环境对应的变量对象(E1)、changeColor()函数对应的局域执行环境的变量对象(E2),最外围的全局执行环境对应的变量对象(E3)。在swapColors()函数中解析到color变量时,就会在E1中寻找,如果找不到,沿着作用域链向上到E2上找,依然找不到,最后到E3上找,结果找到了。如果遍历了作用域链的所有变量对象都找不到该变量,则报错。

任何执行环境都不能通过向下搜索作用域链而进入另一个执行环境。

闭包

理解函数在创建和被调用时发生了什么,对理解闭包很重要。那么先从一个简单的函数说起。

function compare(value1,value2){    return value1 > value2;}var result = compare(5,10);

在创建compare()函数时,会创建一个预先包含外部执行环境的作用域链(这里是全局变量的作用域链),并将其保存在compare()函数内部的[[Scope]]属性中。
当调用compare()的时候,会为函数创建一个执行环境,然后通过复制compare()函数的[[Scope]]属性的作用域链,此后,又有一个活动对象(在此作为变量对象使用)被创建,这个活动对象包含arguments(入参数组)、value1和value2,并被推入执行环境作用域链的前端。如下图所示:
这里写图片描述

接下来说闭包。闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。看下述代码:

function createComparisonFunction(propertyName){    return function(object1,object2){        var value1 = object1[propertyName];        var value2 = object2[propertyName];        return value1 > value2;    };}var compare = createComparisonFunction("name");var result = compare({name:"Nicholas"},{name:"Greg"});

这里内部定义的匿名函数被返回并被调用了,它依然可以访问createComparisonFunction()函数内部的propertyName属性。为什么?
在匿名函数从createComparisonFunction(“name”)函数中被返回时,它的作用域链被初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。在createComparisonFunction()函数执行完毕后,其执行环境的作用域链会被销毁,但它的活动对象仍然留存在内存中,因为匿名函数的作用域链仍在引用这个活动对象,知道匿名函数被销毁后,createComparisonFunction()活动对象才会被销毁。
这里写图片描述

0 0
原创粉丝点击