javaScript执行环境、作用域链与闭包

一、执行环境

执行环境定义了变量和函数有权访问的其他数据,决定了他们各自的行为;每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器处理数据时会在后台使用它。

全局执行环境是最外围的一个执行环境,在web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。

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

二、作用域链

当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行代码所在环境的变量对象。作用域链中的下一个变量对象来自包含环境,而再下一个变量对象则来自下一个包含环境。这样一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直至找到标识符为止。

每个环境都可以向上搜索作用域链,以查询变量和函数名;但任何环境都不能通过向下搜索作用域链而进入另一个执行环境。

虽然执行环境的类型总共只有两种——全局和局部(函数),但通过try-catch语句的catch块和with语句可以延长作用域链。这些语句会在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。

js没有块级作用域

使用var声明的变量会自动被添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境;在with语句中,最接近的环境是函数环境。如果初始化变量时没有使用var声明,该变量会自动被添加到全局环境。

三、闭包

闭包指有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式,就是在一个函数内部创建另一个函数。

理解闭包:在创建函数时,会创建一个预先包含全局变量的作用域链,这个作用域链被保存在内部的[[Scope]]属性中。当调用此函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链,此后又有一个活动对象被创建并被推入执行环境的作用域链的前端。因此创建的函数的作用域链包含两个变量对象:本地活动对象和全局变量对象。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。在另一个函数内部定义的函数会将外部函数的活动对象添加到它的作用域链中。当外部函数执行完毕后,其活动对象不会销毁,因为内部函数的作用域链仍然在引用这个活动对象。当没有变量引用内部函数时,外部函数的活动对象才会被销毁。

四、关于this和arguments

this和arguments是函数内部的两个特殊的对象。

arguments是一个类数组对象,包含着传入函数中的所有参数,该对象有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。

this对象引用的是函数据以执行的环境对象,是在函数运行时基于函数的执行环境绑定的。当在网页的全局作用域中调用函数时,this对象引用的就是window,当函数被作为某个对象的方法调用时,this等于那个对象。匿名函数的执行环境具有全局性,因此其this对象通常指向window。每个函数在搜索arguments和this这两个变量时,只会搜索到其活动对象为止,因此内部函数不可能直接访问外部函数的这两个变量。

posted @ 2017-12-27 16:42  snsart  阅读(181)  评论(0编辑  收藏  举报