首先做一道练习题
let x = [12,23];
function fn(y){
y[0] = 100;
y = [100];
y[1] = 200;
console.log(y);
};
fn(x);
console.log(x);
答案及解析:
函数执行
函数执行的过程
- 创建一个全新的私有上下文 [进栈], 函数中的代码都是在这个私有上下文中执行的。
- JS中的栈结构:一个栈中有多个上下文,哪个上下文中的代码需要执行,则挪至顶端去执行。
- 代码执行时形成一个全新的EC(私有上下文)供私有的代码执行。
- 私有变量对象AO,它是VO的分支,在私有上下文中声明的变量都存储在这里。
- 进栈
- 把全局上下文放到栈底(压缩栈)
- 新进来的上下文放到栈的顶部
- 进栈后的操作
- 初始化作用域链(scopeChain):<自己所在的上下文,函数的作用域>
- 初始化THIS指向:eg:window (箭头函数没有这一步)
- 初始化实参集合:arguments(箭头函数没有这一步)
- 形参赋值:
- 变量提升:
- 代码执行:自上而下依句执行,变量提升时处理过的,代码执行时不再重复处理,直接跳过,计算机不做重复的事情
- 出栈:
- 上下文中的代码执行完毕,出栈(可能释放)。
- 一般情况下,为了优化内存空间,函数执行完毕,所形成的上下文,就会被释放出栈。
形参 & 实参
- 形参是变量,私有变量,存放到自己上下文中的私有变量对象中。
- 实参是具体的值。
- 函数定义设置的是形参,执行传递的是实参。
函数的作用域
[[scope]] :
- 函数的作用域主要看函数在哪儿创建的,
- 与函数在哪儿执行和函数的执行主体都无关。
- 创建函数的时候,就定义了函数作用域>当前函数创建所在的上下文=>[[scope]]:作用域。
- 函数执行的目的是想让之前存储在堆中的代码字符串执行 -> 代码执行就要有自己的执行环境。
作用域链的储存规则
[[scopeChain]]: <EC(fn),EC(G)>
- 即: <自己所在的上下文,自己函数的作用域(上级上下文)>,
- 函数在哪儿创建的,上级上下文就是谁。
- 从函数创建开始,作用域就已经指定好了。
- 当前函数在作用域(N)下创建的
- 那么函数执行形成的作用域(M)的上级作用域就是N,
- 和函数在哪儿执行的没有任何关系。
- 只和函数在哪儿创建的有关系。
作用域链查找机制
作用域链是一种机制:“变量查找机制”[scope-shain]。
- 在当前上下文执行代码的时候,如果遇到一个变量:
- 首先看是否为自己的私有变量(在私有变量对象中的是私有变量),
- 如果是,则操作的都是自己的变量,
- 如果不是私有的变量,则按照作用域链找上级上下文(也就是函数作用域)中是否有这个变量;
- 如果还不是,一直找到全局上下文EC(G) /GO 为止。
- 在查找过程中,找到谁的就操作谁的,
- 找不到则报错提示:
xxx is not defined 。
面试题练习
let x = 1;
function A(y){
let x = 2;
function B(z){
console.log(x + y + z);
};
return B;
}
let C = A(2);
C(3);
图形解析:
|