一段超级简单的js代码了解执行上下文,执行栈,变量对象和作用域链

542 阅读4分钟

代码奉上

var a = 'hello'

function test() {
    console.log(a)
}

test()

解析过程

全局代码

创建阶段

创建全局执行上下文

全局执行上下文 = {
    
}

预处理阶段

创建全局的变量对象,我们给变量对象取个名字叫做VO(G)

变量对象里面有未赋值的变量,函数,this对象

全局执行上下文 = {
    VO(G):{
        a: undefined,
        test: function,
        this: window
    }
}

并且会创建函数:

1.会创建一个堆内存,里面存储函数代码字符串

2.会初始化当前函数的作用域scope = 所在上下文的变量对象

比如说test函数是处于全局上下文里面的,所以test函数的scope就指向了全局上下文的变量对象VO(G)

test函数.[scope] = VO(G)

所以,此刻全局执行上下文如下:

全局执行上下文 = {
    VO(G):{
        a: undefined,
        this: window,
        test: function,
        test[[scope]]: VO(G)
    }
}


创建完毕了全局的变量对象之后,我们的全局代码预处理完毕。

此刻将全局的执行上下文放入执行栈里面,激活该全局上下文,然后就可以开始执行全局代码了!😁

执行阶段

开始执行我们的全局代码:

走到了这一行代码:var a = 'hello'

给变量a赋值hello

此时的全局执行上下文的变量对象是:

VO(G):{
    a: 'hello',
    test: function,
    this: window
}

继续走代码

然后走到了这一行代码:test(),调用我们的函数。

注意!注意!调用函数要搞事情了!🐑

test函数

创建阶段

在调用了函数之后,执行我们的函数代码之前

我们像上面全局代码一样,会先创建一个函数执行上下文

函数执行上下文 = {
    
}

预处理阶段

然后初始化函数的变量对象,取了名VO(test)

1.arguments(就是函数传入的参数,此函数没有参数传入!)

2.变量(此函数里面没有自己的变量!)

3.this对象(因为是window调用的这个函数,所以这个函数的this指向的是window对象!)

以及会初始化作用域链[scopeChain],当前作用域(当前变量对象VO(test)) -> 当前函数的[scope]属性

还记得上面说的,当前函数的[scope]属性是全局执行上下文的变量对象VO(G)嘛!

函数执行上下文 = {
    VO(test):{
        arguments:[],
        this: window
    },
    [scope]:VO(G),
    [scopeChain]: VO(test) => VO(G)
}

函数的变量对象创建完毕,将函数执行上下文推入执行栈中

此时,该函数执行上下文在执行栈的顶部,激活该函数执行上下文。

执行阶段

开始执行函数内部的代码:console.log(a)

遇到变量a,开始在函数的作用域链里面查找a的值

首先在内部的作用域里面也就是变量对象里面查找a,找不到

再去[[scope]]属性里面查找,也就是全局执行上下文的变量对象里面查找,找到了,a='hello'

所以打印:hello

回收阶段

此时,函数已经执行完毕,函数执行上下文推出执行栈

函数维护的栈内存被回收,栈内存里面存储的值也被回收了,所以如果栈内存里面存储了一些堆内存地址,那么对应的堆内存也会被回收(前提是堆内存地址没有其他的引用了)

此时,全局执行上下文又处于执行栈栈顶,激活全局执行上下文,全局代码继续执行

回收全局代码

全局代码执行完毕,关闭浏览器/tab页,全局执行上下文出栈,回收。

HAPPY ENDING! 🎉

知识点合集

关于回收机制

典型的堆内存的回收机制有如下:

标记法

每隔一段时间就对所有的内存空间地址进行一次检测,

检测到该内存空间地址如果没有被引用,那么浏览器就回收。

计数法

如果该内存空间地址被引用了一次,那么就数字 + 1,

如果没有引用(比如设置了null),那么数字 - 1。

当该数字为0的时候,浏览器就立即将该内存回收!

栈内存的作用是什么?

1.存储基本的数据类型的值,比如说var a = 100

2.存储引用数据类型的指针,也就是堆内存地址,比如说const obj = { name:"rose" },obj对象的内存地址就是存在栈内存里面的!

3.给代码提供运行环境,比如说,函数每执行一次,就会生成一个新的栈内存!比如:

function test(){
    ...
}

test() // 生成一个新的栈内存
test() // 生成一个新的栈内存
test() // 生成一个新的栈内存

资料

www.lagou.com/lgeduarticl…