作用域链
核心原理
- VO中包含一个额外的属性(假设为 source),该属性指向创建该VO的函数本身
- 每个函数在创建时,会有一个隐藏属性
[[scope]],它指向创建该函数时的AO - 当访问一个变量时,会先查找自身VO中是否存在,如果不存在,则依次查找
[[scope]]属性。
某些浏览器会优化作用域链,函数的[[scope]]中仅保留需要用到的数据。
示例分析
js
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<script>
var g = 0;
function A() {
var a = 1;
function B() {
var b = 2;
var C = function () {
var c = 3;
console.log(c, b, a, g);
};
C();
}
B();
}
A();
</script>
</body>
</html>
说明:
蓝线:VO中包含一个额外的属性(假设为 source),该属性指向创建该VO的函数本身
红线:每个函数在创建时,会有一个隐藏属性[[scope]],它指向创建该函数时的AO
作用域链:当访问一个变量时,会先查找自身VO中是否存在,如果不存在,则依次查找[[scope]]属性(蓝红线交替 )

html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<script>
var count = 100;
function A() {
var count = 0;
return function () {
count++;
console.log(count);
};
}
var test = A();
test();
test();
test();
console.log(count);
</script>
</body>
</html>
执行至 22 行时,执行上下文栈中的情况:

执行至 24 行时,执行上下文栈中的情况:

执行至 25 行时,执行上下文栈中的情况:

执行 count++时,线找寻 test 执行上下文中的 VO 是否有 count,如果没有,则根据 source寻找到创建该 VO 的函数本身,然后通过[[scope]]属性寻找到下一个 VO,若有 count 则该 count++,否则再往下寻找。
闭包
重新理解闭包:
原理上理解:执行上下文已经销毁,但执行上下文中 VO 被保留下来,因为匿名函数中有一个[[scope]]属性还指向他。并没有被垃圾回收器回收。
表面上理解:内部函数使用外部函数的变量,内部函数被外部调用。