全面剖析函数的底层处理机制

  • EC(Execution Context )
  • AO(Active Object)
  • SCOPE
  • SCOPE-CHAIN

    1. var x = [12, 23];
    2. function fn(y) {
    3. y[0] = 100;
    4. y = [100];
    5. y[1] = 200;
    6. console.log(y);
    7. }
    8. fn(x);
    9. console.log(x);

    EC(G)全局执行上下文,要进栈ECStack执行。
    数组也是一个对象,特殊的对象,所以也是引用类型。要开辟一个堆内存。
    函数也是引用类型,函数的创建也要开辟一个堆内存。
    创建函数的步骤:

  • 开辟堆内存(16进制)

  • 把函数体内部的代码,当做“字符串”存储到堆中【当然也有健值对,函数也是对象】
  • 把地址存到栈中,供变量(函数名)调用,fn也相当于一个变量
  • 创建函数的时候,就声明了函数的“作用域”,是当前函数创建时所处的上下文,不是执行时候的上下文
  • 函数只创建不执行,没有作用,因为只是一堆字符串

函数作用域用[[scope]]表示,创建fn的时候所处上下文是EC(G),就是fn的函数作用域。
image.png
fn(x) 执行函数,x是实参,看下当前上下文下有没有x(全局变量),如果当前上下文没有x变量,就看看window(GO)下有没有x变量,如果GO里面也没有,就x id not undefined。

函数执行

  • 目的:是把之前创建函数时候,在函数体中存储的代码字符串去执行
  • 形成一个全新的私有上下文
    • 存储当前私有上下文中,声明的私有变量的空间AO(Active Obejct),AO就是VO,AO是VO的一种
    • 上下文执行
  • 代码执行前需要做如下工作
    • 初始化作用域链
    • 初始化this指向
    • 初始化arguments
    • 形参复制
    • 变量提升
  • 代码支持下
  • 一般情况下,函数执行完成后,为了优化栈内存,会把形成的私有上下文,出栈释放掉“GC浏览器垃圾回收机制”

作用域链 scope-chain:<当前上下文,函数的作用域(函数fn创建时候所处的上下文)>
形参是私有变量,y = AAAFFF000

作用域链:遇到一个变量y,看下是不是自己的私有变量。怎么看是不是自己的私有变量?首先看自己私有上下文下EC(FN)有没有这个变量,如果这个变量y不是自己私有的,就按照作用域链往上级上下文,也就是函数作用域去找。
去过上级上下文有没有,如果没有,就一直往上找,找到全局有没有。如果全局有,就是全局下的。
如果全局没有,有2种情况:如果是设置变量值,相当于给window设置属性值;如果是获取值,报错。

什么是私有变量?形参是私有变量,当前上细问中声明过的变量是私有变量。
y[0] = 100; y是私有变量,y[0]是成员访问,通过地址访问到堆,把12改成100.
y = [100];等号赋值,[100]是个数组,引用类型,开辟堆内存BBBFFF000,这个地址是在私有上下文下创建的。
y[1] = 200;y是私有变量,y[1]是成员访问,找到地址BBBFFF000,加了一个值。

fn(x)执行完,出栈释放,释放的是当前的执行上下文也就是EC(FN),当然EC(FN)里的AO(FN)和开辟的堆内存也都会被释放,只剩下全局上下文。

函数执行,会形成一个私有的上下文,保护里面的私有变量不受外界干扰,我们把函数执行这种保护机制,称之为“闭包”。
2.png