函数的底层运行机制

1,900 阅读3分钟

平时在做项目的时候,除了完成业务逻辑代码的编写,我们也需要清楚的了解这一段js代码,在计算机底层是如何存储并执行的,不了解js的解释执行始末,真正遇到问题的时候,也只能一脸懵而已~

执行代码前的准备工作

当我们通过浏览器去加载一个页面的时候,首先会从内存条中分配出两块内存来,栈内存(ECStack)堆内存(Heap)

栈内存的作用:

  1. 供代码执行
  2. 存放声明的变量
  3. 存放原始值类型的值

堆内存的作用:

  1. 存放对象数据类型值

1.png

一开始加载页面的时候,会在堆内存中开辟一块空间,地址我们假设为:0x000

2.png

那么这块开辟的空间对象是做什么用的呢?不着急,我们继续往下看

3.png

最开始,如果要执行全局下的代码,形成一个全局的执行上下文EC(G),然后进栈执行

4.png

在每一个执行上下文中都有一个变量对象,用来存放当前上下文中存放的那些变量

5.png

而在VO(G)全局变量对象中我们会默认存储一个非常熟悉的变量:window,指向之前在堆内存中开辟的地址为0x000的那块空间

6.png

那块空间我们叫做全局对象GO(global object),里面存放的是像setTimeoutsetIntervalalert等内置的函数

7.png

一切准备工作就绪,接下来就要通过一段代码看执行过程了

底层执行机制

先上代码!

var x = [12, 13]
function fn(y) {
  y[0] = 100
  y = [100]
  y[1] = 200
  console.log(y)
}
fn(x)
console.log(x)

在执行代码前需要进行变量提升,把带varfunction关键字的提前声明或定义,带var的只声明,function声明并定义,这段代码中只有xfn可以提升

这里需要注意的是因为xfn是在全局上下文中定义,则会直接存储到GO中(只有在全局上下文中定义的才会如此)

8.png

创建函数时,会执行以下步骤:

  1. 会在堆内存中开辟一块空间,有16进制地址
  2. 存储一些内容到空间中,包括作用域scope(在哪个上下文中创建的,作用域就是谁)以及函数体代码当作字符串存储起来
  3. 把堆内存地址关联给对应的变量

9.png

接下来执行第一行代码:var x = [12, 13]

在赋值的时候,先创建值,原始值直接存储到栈内存中,对象值需要在堆内存

10.png

后面经过定义的函数,因为在提升阶段已经定义过,所以不用管,接着往下走来到fn(x)

11.png

这里执行fn,顺着存储的地址,找到0x001,执行里面的函数体,在执行函数的时候,产生一个全新的私有的上下文EC(?),上下文中有一个私有变量对象AO(?),用来存储当前上下文中声明的私有变量

12.png

在函数中,声明了一个形参y,并且将x地址赋值给了形参

13.png

接下来执行函数体内代码

y[0] = 100

14.png

y = [100],此时为私有变量y开辟一个新的堆空间0x003

15.png

y[1] = [200],这时修改的就是私有变量y新赋值的堆地址对象

16.png

最后打印console.log(y)console.log(x),得到的结果

[ 100, 200 ]
[ 100, 13 ]

结束语

如果该文对您有帮助的话,请点个赞哦😯

文中若有错误,欢迎指正;若您有补充,欢迎留言。