浏览器是如何创建并执行函数的呢?

136 阅读3分钟

函数在js中绝对是一个非常特殊的存在,那么函数的创建和执行在浏览器中是如何实现的呢?让我来简单记录一下
以下方代码为例描述浏览器都做了些什么:

var a = [10,20];
function fn(x){
    x[0] = 100;
    x = [20];
    x[1] = 30;
    console.log(x)
}
fn(a)
console.log(a)
  1. 开辟内存

    • 计算机在其内存中专门开辟一块儿内存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中
  2. 变量赋值

    • 创建一块儿堆内存(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关联到一起

  3. 代码执行

    • 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的关联
      • 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]
    • 出栈操作(普通函数)
  4. 最终结果

    [20,30]
    [100,20]