JavaScript作用域(一)

来源:互联网 发布:淘宝包含哪些部门 编辑:程序博客网 时间:2024/06/05 12:49

文章来源:http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/

  明明全局变量定义一个值,为什么函数里不能用?java里函数完全可以使用全局变量值,这里有点糊涂了?

  且提示值显示undefine呢! 蒙了,哪么这篇文章将回答这个问题?

What is the Execution Context?

    当代码在JavaScript着运行时,它的执行环境非常重要,主要有下面3种代码: 

     1、Global code –代码第一次执行默认的环境。

     2、Function code –每次执行流进入函数体时的环境

    3、Eval code – 在eval 函数里面被执行的文本,

      这篇文章目的在于理解作用域,让我们想想execution context 关键字,正在解析当前代码的作用域,

例子,包含了全局和函数/本地 context 解析代码 两个。

     

     我们有1个 global context 由紫线边框表示 且有3个不同 function contexts 由绿,蓝,橘边界表示,只有一个global context, 程序里任何其它的context 能访问global context .
     你能有许多个 function contexts, 且调用每一个函数 时,建立一个新的context, 也就是建立一个私有的作用域,当前函数作用域的外面不能直接访问当前函数里面声明的任何东西 。上例, 一个fuction能访问声明在当前context外面的一个变量,但是一个外面的context不能访问里面的变量/函数, Why does this happen? How exactly is this code evaluated?

Execution Context Stack

浏览器解释JavaScript是作为一个单线程实现的。浏览器在一个时间里仅仅一个事情执行,其它的动作和事件在所谓的 Execution Stack 排队. 下面的图是单一线程堆栈的抽象视图。    

    正如我们已经知道,一个浏览器什么时候第一次装载你的脚本,首先默认进入the global execution context . 如果, 在你的global 代码,你调用一个函数,你程序的顺序流进入调用函数里面,建立一个新的 execution context 和把那个 context压入到 execution 堆栈顶部。

    如果在当前函数里你调用另一个函数,相同的事情发生。代码执行流 进入内部函数里面,  建立 一个新的 execution context ,并压入到一个已经存在堆栈的顶部.浏览器将总是执行当前 execution context ,它存在堆栈顶部,且一旦函数完成执行当前的 execution context, 它将从堆栈中弹出,返回对下一个context对象进行控制。 

   下面的例子显示一个递归函数和一个程序的执行堆栈, 这个代码简单的调用自己3次, i从0开始自增。每次函数 foo 被调用, 一个新的 execution context被建立. 一旦一个 context 已经完成了执行, 从堆栈中弹出且控制返回到下面的context,一直执行到再一次到达global context .

(function foo(i) {    if (i === 3) {        return;    }    else {        foo(++i);    }}(0));

 

关于execution stack有5点需要记住:

  • 单线程.
  • 异步执行.
  • 全局context
  • 无限个 function contexts.
  • 调用每一个函数建立一个新 execution context, even a call to itself
  详细解释Execution Context 
  在JavaScript 解释器里面, 对 an execution context每一次调用有2步
      1、建立阶段
         1》建立作用域链.
         2》建立函数参数,本地函数,函数里的变量。
         3》确定this值。
     2、Activation / 代码执行阶段
       1》赋值,
       2》 对一个函数引用
       3》解释 /执行代码
一个对象有3个属性,来表示每一个execution context的概念。
executionContextObj = {
  'scopeChain': { /* variableObject + all parent execution context's variableObject */ },
   'variableObject': { /* function arguments / parameters, inner variable and function declarations */ },
   'this': {}
}
   Activation / Variable   Object [AO/VO]
   当函数调用时建立了executionContextObj,但实际上是函数执行之前。这是建立的1步,这儿,解释器建立executionContextObj,通过扫描函数的参数,本地函数声明和本地变量的声明。这个扫描的结果变成在executionContextObj里面的variableObject。

下面是浏览器解释器解析code的步骤:
      1、查找调用函数的一些代码
      2、在执行一个函数的代码之前,建立execution context.
      3、进入execution context.建立阶段:
              3.1 初始化作用域链
             3.2 建立变量对象

                   3.2.1 建立参数对象,检查参数的context, 初始化名字和值且建里一个引用复制 
                   3.2.2 扫描函数声明的context:
                           3.2.2.1 对每一个发现的函数,建立变量对象( variable object)的一个属性 ,即该函数的函数名。
                         3.2.2.2 如果一个函数名字已经存在,引用指针值将被重写
                3.2.3 扫描变量声明的context:
                          3.2.3.1  对于发现的每一个变量,建立一个属性in the variable object ,也就是变量名字,且undefined.作为初始化值。
                          3.2.3.2  如果变量名字已经存在 variable object里, 什么也不做且继续扫描。
               3.2.4  确定 在 context里面的" this" 值。
        3.3 Activation / Code Execution Stage
    • 运行 / 解释函数代码在 context 里 且给变量赋值,代码是一行一行的执行。
例如:
function foo(i) {    var a = 'hello';    var b = function privateB() {    };    function c() {    }}foo(22);
在调用foo(22)上, 建立阶段 creation stage ,主要是execution context.建立阶段如下 
fooExecutionContext = {    scopeChain: { ... },    variableObject: {        arguments: {// 建立参数对象            0: 22,            length: 1        },        i: 22,//参数变量        c: pointer to function c()//扫描函数声明的context:        a: undefined,// 扫描变量声明的context:        b: undefined // 扫描变量声明的context:    },    this: { ... }}
正如你看见的, the creation stage 处理变量属性名字的定义为undefined,并没有对他们赋值, with the exception of formal arguments / parameters. 一旦 creation stage 已经完成, 下面的执行进入函数和代码的 execution stage 看起来像这样。   
fooExecutionContext = {    scopeChain: { ... },    variableObject: {        arguments: {            0: 22,            length: 1        },        i: 22,        c: pointer to function c()        a: 'hello',        b: pointer to function privateB()    },    this: { ... }}
  一个变量的提升
  你已经发现许多在线资料定义JavaScript里的术语提升 hoisting ,解释变量和函数的声明是提升到他们函数作用域的顶部,然而,没有一个人详细的解释发生理由,现在你可以用刚学的知识解释如何建立一个 activation object
 下面例子再一次解释:
(function() {    console.log(typeof foo); // function pointer    console.log(typeof bar); // undefined    var foo = 'hello',        bar = function() {            return 'world';        };    function foo() {        return 'hello';    }}());
  • 为什么我们在什么foo之前能访问 foo ?
    • 如果我们观察建立阶段 creation stage, 我们知道多个变量已经在 activation / code execution stagez 之前建立,因此foo作为执行的函数首次已经定义在 activation object
  • Foo是声明两次,为什么foo显示的 function 而不是 undefined  string?
    • 即使 foo 是声明两次, 我们从 creation stage 已经知道,activation object 函数建立在变量之前, 且如果属性名字已经在 activation object里面,我们简单忽略后面的声明.
    • 因此, 在 activation object上,一个对象 function foo() 的引用是第一次建立, 当解释器得到 var foo时,我们已经看属性名字foo 存在,所以代码什么也没做,然后继续。
  • 为什么 bar undefined?
    • bar 是一个函数赋值的一个变量, 我么知道所有变量是在creation stage 建立的,但是他们的初始化值是 undefined.





         



  




    原创粉丝点击