什么是执行上下文

当浏览器的解释器开始执行我们的js代码的时候,js代码运行所处的环境可以被认为是代码的执行上下文,执行上下文(简称-EC)是ECMA-262标准里的一个抽象概念,用于同可执行代码(executable code)概念进行区分。一般来讲,执行上下文可以在以下三种情况产生:

1. 全局上下文(globalContext)   2. function 内部 3. Eval code.

看个例子,包含全局和function内部上下文

  

紫色框内表示全局的执行上下文,同时内部会有3个不同的Function context, function context可以有多个,但是全局上下文只有一个,并且当解释器开始执行代码的时候就会创建全局上下文并进入。 我们可以创建任意多个Function context,声明一个方法并执行的时候会自动创建该function context,同时创建一块区域,在该区域内创建的变量或其他声明不直接被外部context所访问。

执行上下文堆栈

浏览器内部js解释器是按照单线程的方式实现,意味着内部只能同时在做一件事情,其他的调用都会在被称为执行堆栈的地方排队。看下面的图:

  

当浏览器加载js的时候,就会默认进入全局上下文,如果全局代码中开始执行function,则会创建一个新的execution context,并把该execution context push到栈顶。

如果在function里面又调用内部function,则会执行相同的操作,创建新的execution context,并push到栈顶。看例子:

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

  

foo会执行三次,每次执行会生成新的execution context,执行结束则自动出栈。

执行上下文的细节

现在我们知道伴随着function的调用,都会产生一个新的context,在解释器内部,大致分为两个阶段:

Stage I:创建阶段(function被调用,但是在开始执行任何代码之前)

  创建阶段大致做了以下几件事情:

  ①:生成变量对象。该阶段把所有的声明都以key-value的形式提取出来,包括 函数的形参(value为实参的值),function arguments,内部function名(value是对内部function的引用),function内的变量声明(value值统一为undefined)。

  ②:创建作用域链。作用域链包含该上下文中的变量对象和所有父上下文的变量对象,用于变量查找)

  ③:给this赋值。关于this的理解,参考《对javascript this的理解

Stage II:执行阶段(给变量赋值,逐步执行)

理解了以上两个执行阶段后,我们可以大体描绘一下执行上下文中有哪些东西,可以用一个带有3个属性的对象来表示:

executionContextObj = {
scopeChain: { /* variableObject + all parent execution context's variableObject */ },
variableObject: { /* function arguments / parameters, inner variable and function declarations */ },
this: {}
}

对象变量和活动对象 Variable/Activation Object[VO/AO]

executionContextObj在function每次被调用的时候创建,在上文提到的StageI阶段,解释器会对function进行扫描,包括function的arguments, 参数,内部变量声明和内部function声明,扫描的结果会被放到 我们称为 对象变量的对象中(variable object).

看例子:

var a = 10;

function test(x) {
var b = 20;
}; test(30);

以上代码对应的变量对象应该为:

VO(global context) = {
a: 10,
test: <reference to function>
} VO(test function context) = {
x:30,
b:20
}

具体分为两种,

全局上下文中的对象变量

首先,我们要给全局对象一个明确的定义:

全局对象(Global object) 是在进入任何执行上下文之前就已经创建了的对象;
这个对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。

全局对象初始创建阶段将Math、String、Date、parseInt作为自身属性,等属性初始化,同样也可以有额外创建的其它对象作为属性(其可以指向到全局对象自身)。例如,在DOM中,全局对象的window属性就可以引用全局对象自身(当然,并不是所有的具体实现都是这样):

global = {
Math: <...>,
String: <...>
...
...
window: global //引用自身
};

当访问全局对象的属性时通常会忽略掉前缀,这是因为全局对象是不能通过名称直接访问的。不过我们依然可以通过全局上下文的this来访问全局对象,同样也可以递归引用自身。例如,DOM中的window。综上所述,代码可以简写为:

String(10); // 就是global.String(10);

// 带有前缀
window.a = 10; // === global.window.a = 10 === global.a = 10;
this.b = 20; // global.b = 20;

因此,回到全局上下文中的变量对象——在这里,变量对象就是全局对象自己:

VO(globalContext) === global;

非常有必要要理解上述结论,基于这个原理,在全局上下文中声明的对应,我们才可以间接通过全局对象的属性来访问它(例如,事先不知道变量名称)。

var a = new String('test');

alert(a); // 直接访问,在VO(globalContext)里找到:"test"

alert(window['a']); // 间接通过global访问:global === VO(globalContext): "test"
alert(a === this.a); // true var aKey = 'a';
alert(window[aKey]); // 间接通过动态属性名称访问:"test"

 函数上下文中的变量对象

在函数执行上下文中,VO是不能直接访问的,此时由活动对象(activation object,缩写为AO)扮演VO的角色。

VO(functionContext) === AO;

活动对象是在进入函数上下文时刻被创建的,它通过函数的arguments属性初始化。arguments属性的值是Arguments对象:

AO = {
arguments: <ArgO>
};

Arguments对象是活动对象的一个属性,它包括如下属性:

  1. callee — 指向当前函数的引用
  2. length — 真正传递的参数个数
  3. properties-indexes (字符串类型的整数) 属性的值就是函数的参数值(按参数列表从左到右排列)。 properties-indexes内部元素的个数等于arguments.length. properties-indexes 的值和实际传递进来的参数之间是共享的。

例如:

function foo(x, y, z) {

  // 声明的函数参数数量arguments (x, y, z)
alert(foo.length); // 3 // 真正传进来的参数个数(only x, y)
alert(arguments.length); // 2 // 参数的callee是函数自身
alert(arguments.callee === foo); // true // 参数共享 alert(x === arguments[0]); // true
alert(x); // 10 arguments[0] = 20;
alert(x); // 20 x = 30;
alert(arguments[0]); // 30 // 不过,没有传进来的参数z,和参数的第3个索引值是不共享的 z = 40;
alert(arguments[2]); // undefined arguments[2] = 50;
alert(z); // 40 } foo(10, 20);

 综上,来看一个比较综合的例子:

function foo(i) {
var a = 'hello';
var b = function privateB() { };
function c() { }
} foo(22);

foo被调用,StageI阶段:

foo executionContextObj = {
scopeChain: { ... },
varaible object :{
arguments:{
length:1,
callee : foo,//对调用foo的引用
0:22
}
i : 22,
c : <reference function()>,
a : undefined,
b : undefined
},
this : global }

Stage II :

fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
callee:foo
length: 1
},
i: 22,
c: pointer to function c()
a: 'hello',
b: pointer to function privateB()
},
this: global
}

关于javascript中的 执行上下文和对象变量的更多相关文章

  1. 【进阶1-1期】理解JavaScript 中的执行上下文和执行栈(转)

    这是我在公众号(高级前端进阶)看到的文章,现在做笔记 https://mp.weixin.qq.com/s/tNl5B4uGdMkJ2bNdbbo82g 阅读笔记 执行上下文是当前 JavaScrip ...

  2. JavaScript 中的执行上下文和执行栈

    JavaScript - 原理系列 ​ 在日常开发中,每当我们接手一个现有项目后,我们总喜欢先去看看别人写的代码.每当我们看到别人写出很酷的代码的时候,我们总会感慨!写出这么优美而又简洁的代码的兄弟到 ...

  3. JavaScript 中的执行上下文和调用栈是什么?

    http://zcfy.cc/article/what-is-the-execution-context-amp-stack-in-javascript-by-david-shariff-4007.h ...

  4. JavaScript进阶之执行上下文和执行栈

    js引擎的执行过程 执行上下文和执行栈属于js引擎的执行过程的预编译阶段. 执行上下文(Execution Context) 执行上下文是当前 JavaScript 代码被解析和执行时所在环境的抽象概 ...

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

    JavaScript中的执行环境定义了变量或函数有权访问的数据(每个函数都有自己的执行环境),全局执行环境是最外围的执行环境,在浏览器中,全局执行环境就是window对象,所以所有的全局变量和函数都是 ...

  6. 前端学习 第二弹: JavaScript中的一些函数与对象(1)

    前端学习 第二弹: JavaScript中的一些函数与对象(1) 1.apply与call函数 每个函数都包含两个非继承而来的方法:apply()和call(). 他们的用途相同,都是在特定的作用域中 ...

  7. javascript中var let const三种变量声明方式

    javascript中var let const三种变量声明方式 1.var  ①var表示声明了一个变量,并且可以同时初始化该变量. ②使用var语句声明的变量的作用域是当前执行位置的上下文:一个函 ...

  8. ES6新特性:Javascript中的Map和WeakMap对象

    Map对象 Map对象是一种有对应 键/值 对的对象, JS的Object也是 键/值 对的对象 : ES6中Map相对于Object对象有几个区别: 1:Object对象有原型, 也就是说他有默认的 ...

  9. JavaScript中的两种全局对象

    这里总结的东西特别适合先学习c/c++, Java这类标准语言再学JS的童鞋们看,因为JS在程序执行之前就会初始化一个全局对象,这个全局对象到底是什么是跟JS程序运行环境有关的. 根据JavaScri ...

随机推荐

  1. hadoop 异常及处理总结-01(小马哥-原创)

    试验环境: 本地:MyEclipse 集群:Vmware 11+ 6台 Centos 6.5 Hadoop版本: 2.4.0(配置为自动HA) 试验背景: 在正常测试MapReduce(下简称MR)程 ...

  2. Hadoop学习笔记1---简介 优点 架构分析

    一.Hadoop简介 Hadoop最早起源于Nutch.Nutch是一个开源的网络搜索引擎,由Doug Cutting于2002年创建.Nutch的设计目标是构建一个大型的全网搜索引擎,包括网页抓取. ...

  3. windows分屏

    一.准备 主机.显示屏A.显示屏B.DVI连接线2根 二.操作步骤 1.使用DVI连接线将显示屏A连接到主机上,开机进入windows系统(演示用的是win 7)(若已连接,请跳到第2步.基本上这一步 ...

  4. C++11之sizeof

    [C++11之sizeof] 在标准C++,sizeof可以作用在对象以及类别上.但是不能够做以下的事: 这会传回OtherType的大小.C++03并不允许这样做,所以会引发编译错误.C++11将会 ...

  5. Telnet连接Win7系统被拒绝的原因及解决方法

    有时要与计算机进行远程连接,会用到telnet.win7中telnet默认是没有开启的,所以这时连接会连接失败,其失败提示如下: 正在连接192.168.100.103...无法打开到主机的连接. 在 ...

  6. Winfrom子窗体刷新父窗体

    本人比较懒,直接从网上转载了一篇比较合适的文章,只是文章格式有点乱,地址是 http://aspnet.blog.163.com/blog/static/17515510920121126104433 ...

  7. [GUI]界面开发类库-Ribbon风格 [转]

    [GUI]界面开发类库 如果我们不十分清楚需要什么样的界面风格及如何实现,请按以下两个步骤操作: (1)       搞清楚这种风格叫什么名字 (2)       查现有的比较著名的GUI库是否已有相 ...

  8. chart.js接口开发:X轴步长和Labels旋转角

    一. 当初为什么选择chart.js 当初项目使用库是Zepto,Zepto能支持的chart处理库太少.也是为了使得项目比较轻量化,所以选择了chart.js. 但是最后的显示结果实在太差,放弃了c ...

  9. PageValidate 类

    转载:http://www.cnblogs.com/sufei/archive/2010/01/14/1648028.html using System.Text.RegularExpressions ...

  10. Nutch 教程

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html 内部邀请码:C8E245J (不写邀请码,没有现金送) 国 ...