JavaScript学习日记 : Day9 - 执行环境(Execution Context)

执行环境就是当前Javascript代码被执行时所在的环境,Javascript在运行任何代码都是在执行环境运行,在执行环境的创建阶段以下三个都会被建立:

  1. 变数对象(variable)
  2. 作用域链(scope chain)
  3. this

1.执行环境类型

  1. 全域执行环境 : 不在任何函数内的代码,全都位於全域执行环境。他做了两件事,一是创建全域对象,在浏览器中就是window对象,二是将this指向这个全局对象,一个程序中,只能存在一个全域执行环境。

  2. 函数执行环境 : 每次呼叫函数时,都会为该函数创建一个新的执行环境。每个函数都有独立的执行环境,但是只有函数被调用时才会创建。

2. 执行环境生命周期

创建阶段 -> 执行阶段 -> 回收阶段

  • 创建阶段
    函数被调用,在运行内部代码之前,会做以下三件事 :
  1. 创建变数对象 : 首先初始化函数的参数arguments,提升函数声明(声明表达式)和变数声明(变数声明提前仅限var声明)
  2. 创建作用域链 : 作用域链是在变数对象後创建的,主要用来解析变数。JavaScript会从代码的最内层开始,内层没有就往上一层找,直到找到变数。
  3. 确定this指向。
  • 执行阶段
    创建後,就开始执行代码,会完成赋值(创建阶段有创建变数了)

  • 回收阶段
    函数调用完毕後,等待垃圾回收器回收执行环境。

用一个例子来说明:

let a = 10
function out() {
    let b = 20;
    function inner() {
        let c = 30;
        console.log(a+b+c);
    }
    inner();
}
out();

上面的代码会历经底下过程:

  1. 当代码开始执行,创建全域环境。
  2. 开始执行全域环境的代码,进行赋值、函数调用等操作。执行到out()时,out()函数创建自己的执行环境。
  3. 开始执行out()函数内的代码,进行赋值、函数调用,执行到inner()时,inner()创建自己的执行环境。
  4. 开始执行inner()函数内的代码,进行赋值、函数调用,因为里面没有可以生成其他执行环境的需求,所以inner()执行完毕後,inner执行环境回收。
  5. 回到out()执行环境,继续执行剩余的代码,此例子没有代码,所以out()执行环境也回收。
  6. 回到全域执行环境,直到浏览器关闭,全域执行环境回收。

其中每个执行环境都是依序进入call stack中,执行完毕回收後就会离开call stack。

2.1 变数对象(Variable Object,VO)

变数对象(VO)是一个类似容器的对象,与作用域、作用域链息息相关。

变数对象的创建过程三个规则 :

  1. 建立argument对象,建立该对象下的属性与属性值。
  2. 检查执行环境中的函数声明,也就是用function关键字声明的函数,在变数对象中以函数名建立一个属性,属性值指向该函数所在记忆体地址的引用。如果该属性之前已经存在,该属性将被新的引用所覆盖。
  3. 检查执行环境中的变数声明,每找到一个变数,就在变数对象中以变数名建立一个属性,属性值为undefined。如果该变数名的属性已经存在,为了防止同名的函数被修改为undefined,则会跳过,原属性值不会被修改。

关於第三点举个例子:

function test() {
    function inner() {
        console.log("inner");
    }
    var inner = 123

    console.log(inner)
}

test() // 123

按照第三点的规则,inner属性名称已经声明过了,所以底下var所声明的是会跳过的,那为什麽执行结果最後还是123呢?因为这三条规则只有在变数对象的创建时适用,而赋值123是发生在执行阶段,所以结果自然是123,这种现象很让人头疼,其实也是因为var声明的变数允许重复命名所导致的,若使用let来声明就可以避免这种状况(上面的例子改为let,则会出现'func' has already been declared)。

执行环境还没进入执行阶段时,变数对象中的属性都不能被访问。但是进入执行阶段,激活为活动对象就可以被访问,然後开始执行执行阶段的操作。

3. 作用域链

定义 : 多个执行环境(作用域)的变数对象串联起来组成的链表就是作用域链。

作用域可以比拟为蒸笼,最底下一层为全局AO,里面的蒸汽(变数与函数的可见性),可以渗透整个蒸笼,底层之上的其他蒸笼层的蒸气只能往上,也就是只能影响上面的蒸笼层。

作用域的顶端一定是当前作用域(local scope)对应的变数对象,底端一定是全局作用域对应的变数对象(全局VO)。

根据以上的了解,查找变数与函数时JS引擎会从离它最近的作用域开始查找,也就是离它最近的变数对象(VO)开始查找,找不到就往上一层VO直到全域找到为止。


<<:  树选手3号:XGboost [python实例]

>>:  [Day 08 - CSS Architecture] CSS设计模式,摆脱义大利面代码

Laravel 实战经验分享 - Day30 回头看看,以及未来要干嘛

到了铁人赛最後一天,其实自己一开始就从没设想如果完成了铁人赛会有什麽样的心情,只想着就算没梗没题材,...

第三十一天:铁人赛的参与心得:选择比努力更重要-选择的初衷

回想之前在某家公司,人资在公司新人训送给大家的一句话:「选择比努力还重要」 当时的我一直无法体会这句...

【Day16】数据展示元件 - Table

元件介绍 Table 顾名思义就是一个表格元件,用来整齐的显示行列数据。 参考设计 & 属性...

全端入门Day05_何谓全端之後端首篇

今天要来介绍後端,所谓的後端简单来说就是负责资料的部分,因为有关於资料都会是他们处理,而要让资料显示...

Day02:先生先生,请问你装了什麽进去?

Java,我相信如果去查维基百科,「物件导向」这4个字一定会在叙述的第一行。 换言之,在Java的世...