执行环境,作用域

在javascript的学习中,执行环境、作用域是2个非常非常重要和基本的概念,理解了这2个概念对于javsacript中很多脚本的运行结果就能明白其中的道理了,比如搞清作用域和执行环境对于闭包的理解至关重要。

一、执行环境(exection context,也有称之为执行上下文)

所有 JavaScript 代码都是在一个执行环境中被执行的。执行环境是一个概念,一种机制,用来完成JavaScript运行时在作用域、生存期等方面的处理,它定义了变量或函数是否有权访问其他数据,决定各自行为。

在javascript中,可执行的JavaScript代码分三种类型: 
       1. Global Code,即全局的、不在任何函数里面的代码,例如:一个js文件、嵌入在HTML页面中的js代码等。 
      2. Eval Code,即使用eval()函数动态执行的JS代码。 
      3. Function Code,即用户自定义函数中的函数体JS代码。 
     不同类型的JavaScript代码具有不同的执行环境,这里我们不考虑evel code,对应于global code和function code存在2种执行环境:全局执行环境和函数执行环境。

在一个页面中,第一次载入JS代码时创建一个全局执行环境,全局执行环境是最外围的执行环境,在Web浏览器中,全局执行环境被认为是window对象。因此,所有的全局变量和函数都是作为window对象的属性和方法创建的。

当调用一个 JavaScript 函数时,该函数就会进入与该函数相对应的执行环境。如果又调用了另外一个函数(或者递归地调用同一个函数),则又会创建一个新的执行环境,并且在函数调用期间执行过程都处于该环境中。当调用的函数返回后,执行过程会返回原始执行环境。因而,运行中的 JavaScript 代码就构成了一个执行环境栈。

Code1:

 function Fn1(){
     function Fn2(){
        alert(document.body.tagName);//BODY
        //other code...
     }
   Fn2();
}
 Fn1();
 //code here

(图1 执行环境栈  摘自:笨蛋的座右铭的博文)

程序在进入每个执行环境的时候,JavaScript引擎在内部创建一个对象,叫做变量对象(Variable Object)。对应函数的每一个参数,在Variable Object上添加一个属性,属性的名字、值与参数的名字、值相同。函数中每声明一个变量,也会在Variable Object上添加一个属性,名字就是变量名,因此为变量赋值就是给Variable Object对应的属性赋值。在函数中访问参数或者局部变量时,就是在variable Object上搜索相应的属性,返回其值。(另外注意:一般情况下Variable Object是一个内部对象,JS代码中无法直接访问。规范中对其实现方式也不做要求,因此它可能只是引擎内部的一种数据结构。)

大致处理方式就这样,但作用域的概念不只这么简单,例如函数体中可以使用全局变量、函数嵌套定义时情况更复杂点。这些情况下怎样处理?JavaScript引擎将不同执行位置上的Variable Object按照规则构建一个链表,在访问一个变量时,先在链表的第1个Variable Object上查找,如果没有找到则继续在第2个Variable Object上查找,直到搜索结束。这就是Scope/Scope Chain的大致概念。

二、Scope/Scope Chain(作用域/作用域链)

当代码在一个环境中执行时,都会创建基于Variable Object的一个作用域链。 作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。整个作用域链是由不同执行位置上的Variable Object按照规则所构建一个链表。作用域链的最前端,始终是当前正在执行的代码所在环境的Variable Object。如果这个环境是函数(比如Fn2),则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,就是函数内部的arguments对象。作用域链中的下一个Variable Object来自该函数(Fn2)的包含环境(也就是Fn1),而再下一个Variable object来自再下一个包含环境。这样,一直延续到全局执行环境,全局执行环境的Variable Object始终是作用域链中的最后一个对象。

如上所述,作用域链感觉就是一个Variable Object链表,当访问一个变量时,先在链表的第一个Variable Object(最前端)上查找,如果没有找到则继续在第二个Variable Object上查找,直到搜索结束,也就是搜索到全局执行环境的Variable Object中。这也就形成了Scope Chain的概念。

与上面Code1代码对应的作用域链图如下所示(摘自:笨蛋的座右铭的博文,这个图感觉不是很理想,不如下面的图3更形象,把右侧这部分调个个就好了。)

(图2 作用域链图)

如上图所示,对于Code1代码中的函数Fn2所对应的作用域链为:Fn2 Variable Object->Fn1 variable Object->全局对象。也就是说,在Fn2函数中进行变量访问时,首先会在Fn2 Variable object访问内进行寻找,如果没有找到,则向上,搜索Fn1 Variable Object中是否存在,直至找到,停止搜索。

再拿《javascript高级程序设计第二版》中提到的例子来说明一下。代码如下所示:

Code2:

 var color="blue";
 function changecolor(){
    var anothercolor="red";
    function swapcolors(){
	var tempcolor=anothercolor;
	anothercolor=color;
	color=tempcolor;
       // Todo something
     }
    swapcolors();
}
changecolor();
 //这里不能访问tempcolor和anocolor;但是可以访问color;
alert("Color is now  "+color);

在Code2代码中,涉及3个执行环境全局环境、changecolor函数的局部环境和swapcolor局部环境。该段代码的作用域链如下图所示。

                                      (图3摘自:《javascript高级程序设计第二版》)

上图中,

  • -》全局环境有1个变量color和1个函数changecolor()。
  • -》changecolor()函数的局部环境中具有1个anothercolor属性和1个swapcolors函数,当然,changecolor函数中可以访问自身以及它外围(也就是全局环境)中的变量。
  • -》swapcolor()函数的局部环境中具有1个变量tempcolor。在该函数内部可以访问上面的2个环境(changecolor和window)中的所有变量,因为那2个环境都是它的父执行环境。

通过上面的分析,我们可以得知内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。这些环境之间是线性、有次序的。每个环境都可以向上搜索作用域链,以便查询变量和函数名;但任何环境不能通过向下搜索作用域链条而进入另一个执行环境。对于上述例子的swapcolor()函数而言,其作用域链包括:swapcolor()的变量对象、changecolor()变量对象和全局对象。swapcolor()的局部环境开始先在自己的Variable Object中搜索变量和函数名,找不到,则向上搜索changecolor作用域链。。。。。以此类推。但是,changecolor()函数是无法访问swapcolor中的变量。

关于作用域总结以下几条:

1、javascript 没有块级作用域。

直接上代码:

for(var i=0;i<10;i++){
   doSomething(i);
}
alert(i); // output :10,why?

上述代码运行后会返回10,为什么呢?如果是同样的java或是c#代码,则不会是10,可能会提示运行错误,因为i只存在于for循环体重,在运行完for循环后,for中的所有变量就被销毁了。而在javascript中则不是这样的,在for中的变量声明将会添加到当前的执行环境中(这里是全局执行环境),因此在for循环完后,变量i依旧存在于循环外部的执行环境。因此,会输出10。

2、声明变量

使用var声明变量时,这个变量将被自动添加到距离最近的可用环境中。对于函数而言,自然声明的变量就会被添加到函数的局部环境中,变量在整个函数环境内都是可用的。

但是,如果变量没有是用var进行声明,将会被添加到全局环境,也就是说成位全局变量了。

所以在函数体内,进行声明时,一般要在开头用var进行声明。

最后出几个小例题:

var x = 1;
function rain(){
    alert( x );    //弹出 ‘undefined‘,而不是1,也不是10,why?
     var x = ‘10‘;
    alert( x );    //弹出 ‘10‘,why?
}
rain()

为什么会是代码中所说明的结果呢?

我认为和2个事情有关:作用域和预解析。我们可以很容易得出上述代码的作用域链。

window全局环境和rain()函数局部环境。window全局环境中存在全局变量x和rain,而rain()函数的局部环境中包括:局部变量x。因此,在执行rain()函数时,不可能去访问全局变量x的了,因为在当前的rain()函数内已经有局部变量x。所以alert出1,

但为什么第一次alert出undefined呢?这可能和预解析有关。在代码执行时,首先会解析出变量和函数的定义,上述代码等价于下面的代码。

function rain(){
    var x;
    alert( x );
    x = ‘10‘;
    alert( x );
}

by Aaron:https://www.cnblogs.com/aaronjs/articles/2167431.html

时间: 2024-10-08 16:50:17

执行环境,作用域的相关文章

执行环境 作用域 作用域链 闭包的理解

1.首先 当一个变量或者函数被声明的时候 它的执行环境便被确认 , 执行环境定义了变量和函数有权访问的其他数据,决定了他们各自的行为, 而作用域就是变量和函数的可访问范围,控制着变量和函数的可见性与生命周期 每次进入一个新的执行环境,都会创建一个用于搜索变量和函数的作用域链.作用域链是函数被创建的作用域中对象的集合.作用域链可以保证对执行环境有权访问的所有变量和函数的有序访问. 作用域链的最前端始终是当前执行的代码所在环境的变量对象(如果该环境是函数,则将其活动对象作为变量对象),下一个变量对象

*JS:执行环境、变量对象、活动对象和作用域链

var a=1; function b(x){ var c=2; console.log(x); } b(3); ·执行环境(execution context),也称为环境.执行上下文.上下文环境.执行上下文环境: 每次当控制器转到ECMAScript可执行代码的时候,即会进入到一个执行上下文.执行上下文(简称-EC)是ECMA-262标准里的一个抽象概念,用于同可执行代码(executable code)概念进行区分. 通俗的话来讲就是,JS中的函数运行不能仅仅看函数内部有哪些变量,再简单的

执行环境,作用域,作用域链详解

声明:该文章有些概念摘自<JavaScript高级程序设计> 1.执行环境:也称"环境",执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为.(全局定义的变量,函数里面可以访问.一般情况下,函数里面定义的变量,全局无法访问). 2.全局执行环境:全局执行环境是最外围的一直执行环境,(在web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和和方法创建的),全局环境直到应用程序退出(关闭网页或者刷新网页)才会被

4.2 执行环境和作用域

执行环境和作用域 每个函数都有自己的执行环境 作用域链(scope chain) 作用:保证执行环境有权访问的所有变量和函数的有序访问. 理解:以函数为界限,每个函数都有自己的变量对象,自己界限的变量是可以访问的.也可以访问外围包围住这个函数里面的变量.但是大(外围)函数不能访问此函数里面的变量.函数执行完会被销毁. 每个函数有自己的执行环境,当执行流进入一个函数的时候,函数的环境就会被推入到一个环境栈中.而函数执行完后,栈就会将其环境弹出. 内部环境可以通过作用域链访问所有的外部环境,但外部环

【JS】JavaScript中的执行环境与作用域

JavaScript中的执行环境定义了变量或函数有权访问的数据(每个函数都有自己的执行环境),全局执行环境是最外围的执行环境,在浏览器中,全局执行环境就是window对象,所以所有的全局变量和函数都是作为window对象的属性和方法创建的.当某一个执行环境中所有代码执行完成后,该环境就被销毁,保存在其中的变量和函数也将被销毁,全局执行环境在关闭网页或浏览器时才被销毁. 当代码在一个环境中执行时,会创建变量对象的一个作用域链(保证对执行环境有权访问的变量和函数的有序访问),如果环境是函数,将其活动

高程(4):执行环境、作用域、上下文执行过程、垃圾收集、try...catch...

高程三 4.2.4.3 一.执行环境 1.全局执行环境是最外层的执行环境. 2.每个函数都有自己的执行环境,执行函数时,函数环境就会被推入一个当前环境栈中,执行完毕,栈将其环境弹出,把控制器返回给之前的执行环境. 二.作用域 1.每个执行环境,都对应一个自己的作用域. 2.每个执行环境,都能访问上一级的父级.父父级....作用域,称为 "作用域链" 3.执行代码时,会按照标识符沿着作用域链一级一级向上查找.如果找不到,则会报错 *标志符:执行时,使用到的变量或函数 三.上下文执行过程

JavaScript执行环境及作用域

执行环境(executin context)是JS中最为重要的一个概念.执行环境定义了变量或函数有权访问的其他数据决定了它们各自的行为.每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中.虽然我们编写的代码无法访问这个对象 ,但解析器在处理数据时会在后台使用它. 全局执行环境是最外围的一个执行环境.根据JS实现的宿主环境不同,表示执行环境的对象也不一样.在Web浏览器中,全局执行环境被认为是Window对象,因此所有全局变量和函

javascrip高级程序设计的学习笔记【作用域和执行环境】

javascript变量可以用来保存两种类型的值:基本类型和引用类型.基本类型包括以下5种:undefined.Null.string.Boolean.Number等, 引用类型是object:基本类型和引用类型都具有几下特点: 1.基本类型值在内存中占据固定大小的空间,因此基本类型的值被保存在栈内存中: 2.从一个变量向另一个变量复制基本类型的值,会创建这个值的副本. 3.引用类型的值是对象,保存在堆中: 4.包含引用类型值的变量实际并不是包含对象本身,而是一个指向该对象的指针: 5.从一个变

Javascript 函数及其执行环境和作用域

函数在javascript中可以说是一等公民,也是最有意思的事情,javascript函数其实也是一个对象,是Function类型的实例.因此声明一个函数首先可以使用 Function构造函数: var saySomething = new Function("something","console.log(something)"); saySomething("hello world!"); // 输出hello world! Function

深入理解javascript中执行环境(作用域)与作用域链

相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行环境分为全局执行环境和局部执行环境,其中局部执行环境我们又可以称之为函数执行环境.那么究竟什么使执行环境呢?通俗的说,执行环境即为代码执行时所处的环境.我们下来看一看如下代码,再进一步分析之. 1 2 3 4 5 6 7 8 9 10 11 <script><br>var name="zhuzhenwei"; functio