前端一段关于函数执行上下文的分析

这里有段函数

function print(fn) {
  const a = 200;
  fn();
}

const a = 100;
function fn() {
  console.log(a);
}

print(fn); // 100

对此执行顺序的分析如下

// 执行上下文栈
ECSTACKS = [
  globalContext, // 全局执行上下文
];
// 初始化全局执行上下文
globalContext = {
  // Variable Object
  VO: {
    arguments: {
      length: 0,
    },
    a: 100,
  },
  scope: [VO],
};

执行print函数,向 执行上下文栈 顶部压入 print函数执行上下文

ECSTACKS = [
  printContext, // print函数执行上下文
  globalContext, // 全局执行上下文
];
// 复制父级作用域变量对象到 print函数的内置属性[[scope]]中
print.[[scope]] = [globalContext.VO];
// 初始化print函数的执行上下文,将内置属性[[scope]]作为上下文的作用域链,
// 生成活动对象,添加形参、函数声明、变量声明,
// 将活动对象压入作用域链的顶部
printContext = {
  // Activation Object
  AO: {
    arguments: {
      0: 1,
      length: 1,
    },
    fn: `refer to function fn`,
    a: 200,
  },
  scope: [AO, globalContext.VO],
};
// 执行fn函数,向执行上下文栈压入fn函数执行上下文
ECSTACKS = [
  fnContext, // fn函数执行上下文
  printContext, // print函数执行上下文
  globalContext, // 全局执行上下文
];
// 复制父作用域(这里是全局作用域)的变量对象到内置属性[[scope]]中
fn.[[scope]] = [globalContext.VO];
// 初始化fn函数的执行上下文,将内置属性[[scope]]作为上下文的作用域链,
// 生成活动对象,添加形参、函数声明、变量声明,
// 将活动对象压入作用域链的顶部
fnContext = {
  AO: {
    arguments: {
      length: 0,
    },
  },
  scope: [AO, globalContext.VO],
};
// 执行函数fn,在fn函数执行上下文的活动对象AO中寻找a变量,未找到则通过作用域链,
// 寻找父级作用域的变量对象VO,找到a变量输出其值
// 100

执行完fn函数后,fn的函数执行上下文上下文栈 中弹出 ECSTACKS.shift()

ECSTACKS = [
  printContext, // print函数执行上下文
  globalContext, // 全局执行上下文
];

fn执行完后print也就执行完毕,print函数执行上下文上下文栈 中弹出 ECSTACKS.shift()

ECSTACKS = [
  globalContext, // 全局执行上下文
];

也就说明了js采用的是**静态作用域**,函数的作用域在定义的时候就确定了。

posted @ 2023-02-22 11:05  脆皮鸡  阅读(171)  评论(0)    收藏  举报