执行上下文(Execution Context)

JavaScript代码执行的过程,包括编译和执行两个阶段,编译就是通过词法分析,构建抽象抽象语法树,并编译成机器识别的指令,在JavaScript代码编译阶段,作用域规则就已经确定了;在代码执行阶段,或者函数一旦调用,便会创建执行上下文(Execution Context),也叫执行环境

在ECMA-262中有如下一段定义

当控制器转入 ECMA 脚本的可执行代码时,控制器会进入一个执行环境。当前活动的多个执行环境在逻辑上形成一个栈结构。该逻辑栈的最顶层的执行环境称为当前运行的执行环境。任何时候,当控制器从当前运行的执行环境相关的可执行代码转入与该执行环境无关的可执行代码时,会创建一个新的执行环境。新建的这个执行环境会推入栈中,成为当前运行的执行环境.

这也是一个抽象的概念,在一段JavaScript代码中,会创建多个执行上下文,执行上下文定义了变量或函数有权访问的其他数据, ,通过阅读规范及相关文档,了解到执行上下文(简称EC)主要包括三个点,用伪代码表示如下:

EC = {

this: // 绑定this指向为当前执行上下文, 如果函数属于全局函数,则this指向window

scopeChain: [] // 创建当前执行环境的作用域链,

VO: {} // 当前环境的变量对象(Variable Object),每个环境都有一个与之关联的变量对象

}

看下面这一段代码:

var a = 1;

function foo() {

var b = 2;

function bar() {

console.log(b)

}

bar()

console.log(a);

}

foo()

1.执行这段代码,首先会创建全局上下文globleEC,并推入执行上下文栈中;

2.当调用foo()时便会创建foo的上下文fooEC,并推入执行上下文栈中;

3.当调用bar()时便会创建bar的上下文barEC,并推入执行上下文栈中;

4.当bar函数执行完,barEC便会从执行上下文栈中弹出;

5.当foo函数执行完,fooEC便会从执行上下文栈中弹出;

6.在浏览器窗口关闭后,全局上下文globleEC便会从执行上下文栈中弹出;

总结: 栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文

再举一个例子结合浏览器开发者工具来看看到底什么执行上线文

function foo() {

bar()

console.log('foo')

}

function bar() {

baz()

console.log('bar')

}

function baz() {

debugger // 打断点观察执行上下文栈中的情况

}

可以看到当前baz正在执行,所以栈顶是baz的执行上下文,而栈底永远都是Global上下文

继续执行,baz函数执行完成后,从执行上下文栈顶弹出,继续执行bar函数内后面的代码,bar函数执行完,bar的执行上下文从栈中弹出;然后执行foo函数后面的代码,foo函数执行完后,从执行上下文从栈中弹出;最后全局上下文从执行上下文从栈中弹出,清空执行上下文从栈。

变量对象(Variable Object):

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

当浏览器第一次加载js脚本程序的时候, 默认进入全局执行环境, 此次的全局环境变量对象为window, 在代码中可以访问。

如果环境是函数, 则将此活动对象做为当前上下文的变量对象(VO = AO), 此时变量对象是不可通过代码来访问的,下面主要对活动对象进行讲解。

活动对象(Activation Object)

1.初始化活动对象(下文缩写为AO)

当函数一调用,立刻创建当前上下文的活动对象, 并将活动对象作为变量对象,通过arguments属性初始化,值为arguments对象(传入的实参集合,与形参无关,形参做为局部环境的局部变量被定义)

AO = {

arguments:

};

arguments对象有以下属性:

length: 真正传递参数的个数;

callee: 指向当前函数的引用,也就是被调用的函数;

'类index': 字符串类型的整数, 值就是arguments对象中对象下标的值,arguments对象应和数组加以区别, 它就是arguments对象,只是能和数组具有相同的length属性,和可以通过下标来访问值

function show (a, b, c) {

// 通过Object.prototype.toString.call()精准判断类型, 证明arguments不同于数组类型

var arr = [1, 2, 3];

console.log(Object.prototype.toString.call(arr)); // [object Array]

console.log(Object.prototype.toString.call(arguments)); // [object Arguments]

console.log(arguments.length) // 2 传递进来实参的个数

console.log(arguments.callee === show) // true 就是被调用的函数show自身

//参数共享

console.log(a === arguments[0]) // true

a = 15;

console.log(arguments[0]) // 15

arguments[0] = 25;

console.log(a) // 25;

但是,对于没有传进来的参数c, 和arguments的第三个索引是不共享的

c = 25;

console.log(arguments[2]) // undefined

argument[2] = 35;

console.log(c) // 25

}

show(10, 20);

接着往下走,这才是关键的地方,执行环境的代码被分成两个阶段来处理:

进入执行环境

执行函数的代码

2.进入执行环境

函数如果被调用, 进入执行环境(上下文),并立即创建活动对象, 通过arguments属性初始化, 与此同时会扫描执行环境中的所有形参、所有函数声明、所有变量声明, 添加到活动对象(AO)中, 并确定this的值,然后会开始执行代码。

在进入执行环境这个阶段:

所有形参声明:

形参名称作为活动对象属性被创建, 如果传递实参, 值就为实参值, 如果没有传递参数, 值就为undefined

所有函数声明:

函数名称作为活动对象的属性被创建,值是一个指针在内存中, 指向这个函数,如果变量对象已经存在相同名称的属性, 则完全替换。

所有变量声明:

所有变量名称作为活动对象的属性被创建, 值为undefined,但是和函数声明不同的是, 如果变量名称跟已经存在的属性(形式参数和函数)相同、则不会覆盖

function foo(a, b) {

var c = 10;

function d() {

console.log('d');

}

var e = function () {

console.log('e');

};

(function f() {})

if (true) {

var g = 20;

} else {

var h = 30;

}

}

foo(10);

此时在进入foo函数执行上下文时,foo的活动对象fooAO为:

fooAO = {

arguments: {

0: 10,

length: 1

},

a: 10,

b: undefined,

c: undefined,

d: //指向d函数的指针,

e: undefined,

g: undefined,

h: undefined // 虽然else中的代码永远不会执行,但是h仍然是活动对象中的属性

}

这个例子做如下几点说明:

1.关于函数,只会创建函数声明作为活动对象的属性, 而f函数作为函数表达式并不会出现在活动对象(AO)中

2.e虽然值是一个函数, 但是作为变量属性被活动对象创建

3.代码执行阶段

在进入执行上下文阶段,活动对象拥有了属性,但是很多属性值为undefined, 到代码执行阶段就开始为这些属性赋值了

还是上面的代码例子, 此时活动对象如下:

fooAO = {

arguments: {

0: 10,

length: 1

},

a: 10,

b: undefined,

c: 10, // 赋值为undefined

d: //指向d函数的指针,

e: // 指向e函数的指针

g: 20,

h: undefined // 声明h变量,但是没有赋值

}

变量对象包括:{ arguments对象+函数形参+内部变量+函数声明(但不包含表达式) }

这时这个活动对象, 即作为当前执行环境的变量对象会被推到此执行环境作用域链的最前端(作用域链本篇不做介绍,会在下一篇文章中单独讲解作用域和作用域链), 假定执行环境为一个对象,则整个执行环境可以访问到的属性如下:

伪代码如下:

fooExecutionContext = {

scopeChain: [], //fooAO +所有父执行环境的活动对象,

fooAO: {

arguments: {

0: 10,

length: 1

},

a: 10,

b: undefined,

c: 10, // 赋值为undefined

d: //指向d函数的指针,

e: // 指向e函数的指针

g: 20,

h: undefined

},

this: 当前执行环境的上下文指针

}

补充:

下面的例子为了说明一下变量声明的顺序及变量同名不会影响函数声明

console.log(foo); // foo的函数体

var foo = 10;

console.log(foo) // 10

function foo() {};

foo = 20;

console.log(foo); // 20

在代码执行之前, 就会读取函数声明,变量声明的顺序在函数声明和形参声明之后, 整个流程如下:

1. 进入执行环境阶段:

1. var VO = {}

2. VO[foo] = 'foo函数指针'

3. 扫描到var foo = 10,

// 但是foo做为function已经声明,所以变量声明不会影响同名的函数声明,如果代码中没有foo函数声明的话,则foo为undefined

代码执行阶段:

1. VO[foo] = 10;

2. VO[foo] = 20;

解析代码完成。

以上简单总结了下对执行上下文和变量对象的理解,主要在于记录总结一下学习成果,目前文章的水平实在不敢谈分享。如有理解不对的地方还请各位大神多多指教,想了解更深可以去查看本文最后主要参考资料的链接,都是经典啊,相信看完也就理解了。

本文主要参考资料:

java函数ao活动对象_JavaScript中的执行上下文和变量对象相关推荐

  1. java函数ao活动对象_[AO] AO全面介绍

    活动对象:一.同步函数被调用时,它会将服务执行完成,然后才返回到其调用处. 异步函数则会在函数调用中提交一个请求,然后马上返回到调用处,但该请求会迟些才会完成.在请求完成之前,调用者可以继续执行其它的 ...

  2. java函数ao活动对象_JS之预编译和执行顺序(全局和函数)

    预编译的两种情况 全局: 1.全局 直接是script标签中的代码,不包括函数执行 执行前: 1.首先生成一个GO(global object)对象,看不到,但是可以模拟出来用来分析 2.分析变量声明 ...

  3. 2.JS执行上下文和变量对象

    文章目录 执行上下文栈 函数上下文 变量对象 执行过程 进入执行上下文 代码执行 两个例子 1 2   JS 是单线程语言,因此执行顺序是顺序执行,不过 JS 引擎在执行 JS 代码的时候并不是逐行执 ...

  4. java函数ao活动对象_Java程序设计10-11试卷A0105答案

    物理与电信工程学院2010-2011学年第(一)学期期末考试 <Java程序设计>试卷(A) 一.选择题(在每小题的四个备选答案中,选出一个正确答案,并将正确答案的序号填在题干前的括号内. ...

  5. 串讲-解释篇:作用域,作用域链,执行环境,变量对象,活动对象,闭包

    这篇接:理论篇:作用域,作用域链,执行环境,变量对象,活动对象,闭包 看例子: function compare(value1, value2) {if (value1 < value2) {r ...

  6. JavaScript学习系列之执行上下文与变量对象篇

    一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 在上一篇文章中讲解了JavaScript内存模型,其中有提到执行上下文与变量对象的概念.对于JavaScript开发者来说,理解执行上下文与变 ...

  7. 一、 函数调用栈,执行上下文及变量对象

    前言 为什么会有这篇文章? 在书籍或博客上,我们经常会看到「作用域链」.「闭包」.「变量提升」等概念,说明一个问题 -- 它们很重要. 但很多时候,对于这些概念,看的时候觉得自己已经明白了,可过不了多 ...

  8. # JavaScript中的执行上下文和队列(栈)的关系?

    原文:What is the Execution Context & Stack in JavaScript? git地址:JavaScript中的执行上下文和队列(栈)的关系? 导读:以前总 ...

  9. JavaScript——执行环境、变量对象、作用域链

    前言 这几天在看<javascript高级程序设计>,看到执行环境和作用域链的时候,就有些模糊了.书中还是讲的不够具体.通过上网查资料,特来总结,以备回顾和修正. 目录: EC(执行环境或 ...

最新文章

  1. ubuntu 一个好系统
  2. 运送超级计算机 蓝书368
  3. sonar java_修复Sonar中常见的Java安全代码冲突
  4. 通过案例学调优之--和 LOG BUFFER 相关的主要 Latch
  5. mac电脑上的效率工具:alfred 4
  6. .netcore 如何获取系统中所有session_C#/.NET/.NET Core定时任务调度组件有哪些?
  7. AWS和阿里云对比研究二—阿里云
  8. 热血江湖单机版不显示服务器,热血江湖单机版
  9. 音量控制 单片机c 语言,(封贴)请大神代写音响音量控制程序(有偿代写)单片机加TDA7313控制...
  10. 打开chm文件提示“已取消到该网页的导航”的解决方案
  11. 切片器可以设置日期格式?_如何分秒必争--浅淡时间切片器
  12. 分享一款国产并口PSRAM存储芯片EMI164NA16LM
  13. Three.js地球开发—6.三维球面上某点进行贴图标注
  14. 电池极耳尺寸视觉检测系统
  15. 数字 IC 技能拓展(22)原码、反码、补码、移码的区别与联系
  16. 计算机原理与应用作业,计算机原理与应用(杨刚)-中国大学mooc-题库零氪
  17. 【星辰傀儡线·命运环·卷二 尘埃】 4 蓝月
  18. 淘宝老店新开有什么优势 淘宝老店弊端有哪些
  19. 自动化打包平台系列(一):自动化打平台建设概览
  20. 概率图与随机过程:概率统计基本概念与人工智能应用之间的桥梁

热门文章

  1. android单片机蓝牙小车,手把手教你做蓝牙小车
  2. amd linux 性能,10年内AMD处理器有多大改进?Linux下皓龙和霄龙每瓦性能对决
  3. 企业信息管理计算机考什么,考信息系统运行管理员要学什么?
  4. getservbyname php,php中getservbyport与getservbyname函数用法实例
  5. Linux脚本让我选择文件,linux – 用于选择文件和打印文件大小的Awk脚本
  6. body curl 设置post_深入说说postman发送post请求
  7. 一、Go语言环境搭建
  8. 四十三、Linux磁盘管理和Shell编程
  9. 四十七、Tableau地理可视化
  10. Map和hashmap