深入浅出执行上下文、词法环境、变量环境

执行上下文的概念

执行上下文:javascript 代码解析和执行时所在的环境。

执行上下文的类型

执行上下文分为三种类型:

1.全局执行上下文

  • js代码开始运行后。首先进入全局执行上下文环境中,不在任何函数中的js代码都会在全局执行上下稳重
  • 一个js程序中只存在一个全局执行上下文。创建时会压人栈底,只有当程序结束时才会弹出
  • 全局执行上下文会做两件事。1.创建全局对象,2.将this指向这个全局对象
  • 浏览器环境中全局对象是window, 在node环境中全局对象是global

2.函数执行上下文

  • 函数每次调用都会产生一个新的函数执行上下文,每个函数都拥有自己的执行上下文,但是只有调用的时候才会被创建
  • 函数执行上下文的生命周期分为两个阶段。创建和执行

3.Eval执行上下文

  • eval函数执行时产生的执行上下文。

执行上下文栈

执行上下文栈是一个后进先出的数据结构,
具体执行流程如下

  • 首先创建全局执行上下文, 压入栈底
  • 每当调用一个函数时,创建函数的函数执行上下文。并且压入栈顶
  • 当函数执行完成后,会从执行上下文栈中弹出,js引擎继续执栈顶的函数。

如以下函数执行时的执行栈变化为:

function fun1(){
    console.log('func1')
    fun2()
}
function fun2(){
    console.log('func2')
}
fun1()  
/*
*                     fun2
*           fun1      fun1       fun1   
* global => global => global => global => global
*/

执行上下文生命周期

变量对象VO和活动对象AO

在讲生命周期钱。我们必须了解讲个对象,变量对象VO和活动对象AO

变量对象VO:

  • 用来存储执行上下文中可以被访问。但不能被delete的函数标识。
  • 包括:arguments 对象,形参实参键值对,函数声明,变量声明和this
    活动对象AO:
  • 他可以被理解为当函数被激活调用时创建的对象。用来访问VO对象中存储的那些个标识。
    全局上下文中变量对象:
  • 就是全局对象
  • 初始化时:初始化一系列原始属性:Math,String,Date,Window等
  • 在浏览器中window对象引用全局对象,全局环境中this也引用自身

创建阶段

此阶段执行上下文会执行以下操作

  • 创建作用域链
  • 通过变量对象VO创建活动AO,
    • 首先创建arguments对象
    • 创建形参实参的键值对
    • 创建函数声明 (经典面试题:为什么函数声明提前?)
    • 创建变量声明 (经典面试题:为什么变量声明提升?)
  • 创建this

执行阶段

  • 变量赋值。函数引用,执行其他代码逻辑
  • 当执行完毕后。执行上下文出栈,等待垃圾回收机制回收

举例说明

function a(name, age){
    var a = 1
    function c(){}
    var d = funciton (){}
    (function e(){})
    var f = function g(){}
}
a('John')
// 如上代码。
//在创建预编译阶段生成的AO对象如下
AO = {
    arguments:{
        0: 'John',
        1: undefined,
        length: 2
    },
    name: 'John',
    age: undefined,
    c: reference to function c(){},
    a: undefined,
    d: undefined,
    f: undefined,
}
// 函数表达式 e,不在AO中 
// 函数g不在AO中

// 函数执行时AO对象如下
AO = {
    arguments:{
        0: 'John',
        1: undefined,
        length: 2
    },
    name: 'John',
    age: undefined,
    c: reference to function c(){},
    a: 1,
    d: reference to FunctionExpression "d",
    f: reference to FunctionExpression "f",
}

函数声明提前

从AO对象的创建过程我们就可以发现,AO对象想是先扫描函数体内的函数声明才去扫描变量声明。所以这也就是为啥会有声明提前。

变量提升

AO对象创建时已经将函数内部的变量提前扫描声明。是指在函数执行的过程中开始依次赋值。

词法环境和变量环境

在ES6中提出词法环境和变量环境两个概念。主要是执行上下文创建过程。

词法环境(LexicalEnvironment)

词法环境是一种包含 标识符 => 变量 隐射关系的一种结构。

在词法环境中有两个组成部分:

  • 环境记录(EnvironmentRecord): 储存变量和函数声明的实际位置
  • 对外部环境的引用(Outer):当前可以访问的外部词法环境

词法环境分为两种类型:

  • 全局环境: 全局执行上下文,他没有外部环境的引用,拥有一个全局对象window和关联的方法和属性eg: Math,String,Date等。还有用户定义的全局变量,并将this指向全局对象。
  • 函数环境: 用户在函数定义的变量将储存在环境记录中。对外部环境的引用可以是全局环境,也可以是包含内部函数的外部函数环境。环境记录中包含。用户声明的变量。函数。还有arguments对象。

举例词法环境在伪代码中如下:

GlobalExectionContent = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      // 剩余标识符
    },
    Outer: null,
  }
}

FunctionExectionContent = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      // 剩余标识符
    },
    Outer: [Global or outer function environment reference],
  }
}

变量环境(VariableEnvironment)

变量环境也是一个词法环境。他具有词法环境中所有的属性
在ES6中,LexicalEnvironment和VariableEnvironment 的区别在于前者用于存储函数声明和变量let 和 const 绑定,而后者仅用于存储变量 var 绑定。

用以下代码举例:

let a = 20;  
const b = 30;  
var c;

function add(e, f) {  
 var g = 20;  
 function c(){}
 return e + f + g;  
}

c = add(20, 30);

在预编译阶段。生成的词法环境和变量环境如下

GlobalExectionContent = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      a: <uninitialied>,
      b: <uninitialied>,
      add: <func>
      // 剩余标识符
    },
    Outer: null,
  },

  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Object",
      c: undefined,
      // 剩余标识符
    },
    Outer: null,
  }
}

FunctionExectionContent = {
  LexicalEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      arguments: {
        0: 20,
        1: 30,
        length: 2,
      },
      e: 20,
      f: 30,
      c: reference to function c(){}
      // 剩余标识符
    },
    Outer: GlobalLexicalEnvironment,
  },
  VariableEnvironment: {
    EnvironmentRecord: {
      Type: "Declarative",
      g: undefined,
      // 剩余标识符
    },
    Outer: GlobalLexicalEnvironment,
  }
}

我们发现使用let和const声明的变量在词法环境创建时是未赋值初始值。而使用var定义的变量在变量环境创建时赋值为undefined。这也就是为什么const、let声明的变量在声明钱调用会报错,而var声明的变量不会。

代码执行阶段

此阶段的执行流程就是函数执行时的流程。给变量赋值,和执行其他逻辑代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容