平时在做项目的时候,除了完成业务逻辑代码的编写,我们也需要清楚的了解这一段js代码,在计算机底层是如何存储并执行的,不了解js的解释执行始末,真正遇到问题的时候,也只能一脸懵而已~
执行代码前的准备工作
当我们通过浏览器去加载一个页面的时候,首先会从内存条中分配出两块内存来,栈内存(ECStack)和堆内存(Heap)
栈内存的作用:
- 供代码执行
- 存放声明的变量
- 存放原始值类型的值
堆内存的作用:
- 存放对象数据类型值
一开始加载页面的时候,会在堆内存中开辟一块空间,地址我们假设为:0x000
那么这块开辟的空间对象是做什么用的呢?不着急,我们继续往下看
最开始,如果要执行全局下的代码,形成一个全局的执行上下文EC(G),然后进栈执行
在每一个执行上下文中都有一个变量对象,用来存放当前上下文中存放的那些变量
而在VO(G)全局变量对象中我们会默认存储一个非常熟悉的变量:window,指向之前在堆内存中开辟的地址为0x000的那块空间
那块空间我们叫做全局对象GO(global object),里面存放的是像setTimeout、setInterval、alert等内置的函数
一切准备工作就绪,接下来就要通过一段代码看执行过程了
底层执行机制
先上代码!
var x = [12, 13]
function fn(y) {
y[0] = 100
y = [100]
y[1] = 200
console.log(y)
}
fn(x)
console.log(x)
在执行代码前需要进行变量提升,把带var和function关键字的提前声明或定义,带var的只声明,function声明并定义,这段代码中只有x和fn可以提升
这里需要注意的是因为
x、fn是在全局上下文中定义,则会直接存储到GO中(只有在全局上下文中定义的才会如此)
创建函数时,会执行以下步骤:
- 会在堆内存中开辟一块空间,有16进制地址
- 存储一些内容到空间中,包括作用域
scope(在哪个上下文中创建的,作用域就是谁)以及函数体代码当作字符串存储起来 - 把堆内存地址关联给对应的变量
接下来执行第一行代码:var x = [12, 13]
在赋值的时候,先创建值,原始值直接存储到栈内存中,对象值需要在堆内存中
后面经过定义的函数,因为在提升阶段已经定义过,所以不用管,接着往下走来到fn(x)
这里执行fn,顺着存储的地址,找到0x001,执行里面的函数体,在执行函数的时候,产生一个全新的私有的上下文EC(?),上下文中有一个私有变量对象AO(?),用来存储当前上下文中声明的私有变量
在函数中,声明了一个形参y,并且将x地址赋值给了形参
接下来执行函数体内代码
y[0] = 100
y = [100],此时为私有变量y开辟一个新的堆空间0x003
y[1] = [200],这时修改的就是私有变量y新赋值的堆地址对象
最后打印console.log(y)和console.log(x),得到的结果
[ 100, 200 ]
[ 100, 13 ]
结束语
如果该文对您有帮助的话,请点个赞哦😯
文中若有错误,欢迎指正;若您有补充,欢迎留言。