函数的底层运行机制

230 阅读3分钟

一、函数执行的步骤

  • 形成一个私有的执行上下文(AO),然后进栈执行
  • 初始化作用域链
  • 初始化THIS
  • 初始化ARGUMENTS
  • 形参赋值
  • 变量提升
  • 代码执行
  • 根据情况决定是否出栈释放

二、作用域

创建函数的时候就声明了它的作用域,在哪个上下文中创建的,那它作用域就是哪个上下文

三、作用域和上下文的关系

  • 是一个东西,都是函数执行形成的那个空间
  • FN的作用域【ECG】ECG 也是全局上下文
  • 从核心来说 都是栈内存
  • 函数自己执行形成的EC(FN) 上下文
  • 函数创建的时候声明了它的作用域

四、函数执行会形成一个"全新"的"私有"的执行上下文,然后进栈执行

一般情况下,函数执行完,形成的这个私有上下文会出栈释放 来优化内存空间

EC(FN) 私有执行上下文

AO(FN) 私有变量对象 [私有上下文声明的变量都存储在这里=>私有变量]

  • AO[active object]是VO的分支,函数私有上下文中[AO]
  • 私有变量(AO):
    • @1 形参变量
    • @2 函数体中声明过的变量

五、代码执行前的步骤

  • 初始作用域链:scope chain <EC(FN),EC(G)>
  • 初始化THIS
  • 初始ARGUMENTS--实参集合
  • 形参赋值
  • 变量提升

六、作用域链

<自己的上下文【执行产生的】,函数的作用域【创建时候声明的】> <EC(FN),EC(G)>

作用域链机制

1

  • 私有上下文中,代码执行阶段,遇到一个变量,我们首先看是否为自己上下文中的私有变量,
  • 如果是自己的【AO】,则接下来所有操作,都是操作自己的,和外界的变量没有直接的关系

2

  • 如果不是自己私有的变量,则按照作用域链,查找是否为其上级上下文中变量【上级上下文就是函数的作用域】
  • 如果找到了,则后期操作的都是上级上下文中的变量

3

  • 如果上级上下文也没有这个变量,则继续找其"上上级"上下文,一直到EC(G)全局上下文位置
  • 如果全局上下文中也没有
    • 获取变量值就是报错
    • 设置变量值就是给window设置的属性

练习题

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

3.png

console.log(a, b, c);//undefined undefined undefined
var a = 12, //等同于 var a,var b,var c
    b = 13,  
    c = 14;
var fn = function (c) {
    console.log(a, b, c);//undefined 13 10
    var a = b = c = 20;// c=20,b=20,var a=20
    console.log(a, b, c);//20 20 20
};
fn(10);
console.log(a, b, c); //12 20 14

3-1.png

如果全局上下文没有变量

获取:报错
赋值:为window变设置属性
/* 
/!*
 * EC(G)
 *   VO(G) / GO
 *     fn ---> 0x000 [[scope]]:EC(G) 
 *!/
var fn = function () {
    /!*
     * EC(FN)
     *   AO(FN)
     * 
     * 作用域链:<EC(FN),EC(G)>
     * 形参赋值:--
     * 变量提升:-- 
     *!/
    console.log(a);  //获取的话就是报错 Uncaught ReferenceError: a is not defined
    a = 100; //window.a=100
};
fn();
console.log(a, window.a); //100 100