函数在js中绝对是一个非常特殊的存在,那么函数的创建和执行在浏览器中是如何实现的呢?让我来简单记录一下
以下方代码为例描述浏览器都做了些什么:
var a = [10,20];
function fn(x){
x[0] = 100;
x = [20];
x[1] = 30;
console.log(x)
}
fn(a)
console.log(a)
-
开辟内存
- 计算机在其内存中专门开辟一块儿内存ECStack(Execution Context Stack 执行环境栈)
- 再次开辟一块内存形成全局上下文EC(G),在EC(G)中会形成全局变量对象VO(G)「Variable Object Global」,创建全局变量a和fn并存储起来
- 将代码(var a ... console.log(a))全部存储到EC(G)中
- EC(G)执行进栈操作,进入到执行环境栈ECStack中
-
变量赋值
-
创建一块儿堆内存(0x001)并将数组
[10,20]
以 "0:10,1:20,length:2" 的形式存储 -
将a和16进制地址0x001关联到一起
-
函数是js中的引用数据类型,所以它的创建也应该遵守引用类型值创建的规律:创建一块儿堆内存(0x002)并以字符串的形式‘x[0] = 100;x = [20];x[1] = 30;console.log(x)’存储起来
-
将fn和16进制地址0x002关联到一起
-
-
代码执行
fn(a)
执行:形成一个全新的私有的上下文EC(...),这里称之为EC(fn)- 在EC(fn)中形成激活对象AO(Activation Object),其为当前上下文的私有变量(形参变量和当前上下文中声明的变量)
- 进栈操作,进入到执行环境栈ECStack中
- 初始化作用域链(scope-chain)
- <当前自己的上下文,上级上下文(函数定义的作用域)>
- 函数代码执行的时候遇到一个变量,首先看这个变量是否是自己的私有变量,如果是,那么执行代码,这个变量的操作和外界没有任何直接关系;如果不是自己的私有变量,那么会沿着作用域链向上(上级上下文)查找是否存在,如果存在进行代码操作,如果不存在继续上下查找,知道找到EC(G)位置----->作用域链的查找机制
- 这里的初始化为<EC(fn),EC(G)>
- 初始化this
- window
- 初始化arguments
- 形参赋值
- x ----> 0x001
- 变量提升
- 此处不存在变量提升
- 内部代码由上而下的执行
x[0] = 100;
这里的x的函数fn的私有变量,在形参赋值阶段x和0x001的堆内存已经做了关联,所以这里对x
进行的修改就是对0x001
堆内存的内容进行修改,此时0x001堆内存中的内容变为 ‘0:100,1:20,length:2’x = [20]
- 创建新的堆内存0x003并将数组
[20]
以 **'0:20,length:1'**的形式存储 - 将fn的私有变量
x和16进制地址0x003
进行关联,同时取消和0x001的关联
- 创建新的堆内存0x003并将数组
x[1] = 30
这里对x进行的修改就是对0x003
堆内存的内容进行修改,此时0x001堆内存中的内容变为 ‘0:20,1:30,length:2’console.log(x)
,这里的x是函数fn的私有变量,而在上一步中已经将x和0x003进行了关联,所以这里需要打印的是0x001的内容,所哟结果为[20,30]
- 函数外部代码执行
console.log(a)
,这里的a是全局变量关联的是16进制地址0x001,而在fn函数内代码x[0] = 100;
执行时0x001中的内容改为了‘0:100,1:20,length:2’
,所以这里打印的结果为[100,20]
- 出栈操作(普通函数)
-
最终结果
[20,30]
[100,20]