一些概念
栈:先进后出的存储结构。
堆:完全二叉树,这里不讨论。
调用堆栈:暂存函数的栈,可进可出。
执行上下文:当函数放入到调用堆栈时由 JS 创建的环境。
闭包:当在另一个函数内创建一个函数时,它“记住”它在以后调用时创建的环境。
垃圾收集:当内存中的变量被自动删除时,因为它不再使用,引擎要处理掉它。
Call Stack
var myOtherVar = 10;
function a() {
console.log("myVar", myVar);
b();
}
function b() {
console.log("myOtherVar", myOtherVar);
c();
}
function c() {
console.log("Hello world!");
}
a();
var myVar = 5;
/*
myVar undefined
myOtherVar 10
Hello world!
*/
复制代码
函数只是声明了但没有执行,等到调用函数时候把函数放入到堆栈区,如果有函数的嵌套调用,就不断的入栈

词法与作用域
function a() {
var myOtherVar = "inside A";
b();
}
function b() {
var myVar = "inside B";
console.log("myOtherVar:", myOtherVar);
function c() {
console.log("myVar:", myVar);
}
c();
}
var myOtherVar = "global otherVar";
var myVar = "global myVar";
a();
复制代码

这张图显示的很明显了,对上述代码,放入函数和函数内的局部变量,形成了两条运行链:
-
a -> global
-
c -> b -> global
setTimeout
执行条件
function logMessage2() {
console.log("Message 2");
}
console.log("Message 1");
setTimeout(logMessage2, 1000);
console.log("Message 3");
复制代码
setTimeout包裹的函数会被放入CallBack Queue(回调队列)中,满足两个条件才能移入到Call Stack(调用堆栈)里面:
-
到达设定时间
-
Call Stack为空
值得一提的是,即使设置时间为0,即
setTimeOut(logMessage2, 0);,也会移入回调队列中,只有调用堆栈为空才会加入到里面

嵌套的setTimeout
function a() {
let x = 2;
let y = 3;
function b() {
setTimeout(() => {
console.log(x + y);
}, 5000);
return 'b函数';
}
setTimeout(() => {
console.log(x ** y);
}, 1000);
let res = b();
console.log(res);
return 'a函数';
}
console.log(a());
/*
b函数
a函数
8
5
*/
复制代码
从上述代码可以看出,setTimeout的执行是晚于函数的返回值的,这更验证了“Call Stack为空”这个条件
第二个问题:既然函数执行结束返回,变量为啥不清除,还能被调用?答:变量存储在堆中,函数执行过程是在栈中,互相不干预。

值得一提的是,装入到Call Queue的函数出队顺序是时间越短的先出队,上面两处setTimeout的时间设定交换下,会打印出:
/*
a函数
b函数
8
5
*/
复制代码
案例分析
见如下代码,分析词法环境和执行过程:
let x = 1;
let y = 'Y';
function a(name) {
function b() {
console.log(x);
return function(){
console.log(y);
}
}
let x = 3;
y = 'YES';
let f = b();
setTimeout(f,1000);
console.log('a函数里面:'+name);
return;
}
a('Hellen');
复制代码
1.Global
声明变量x、y、function a(),但不赋初值
执行x = 1;,入栈,紧接着出栈实现赋值
执行y = 'Y';,入栈,紧接着出栈实现赋值
执行a();,入栈
现在栈中只有
a()函数了,在Global环境下
x = 1
y = 'Y'
2.function a()
声明变量name、function b()、x、f,但不赋初值
执行name = Hellen;,入栈,紧接着出栈实现赋值,这一步是函数参数的赋值
执行x = 3;,入栈,紧接着出栈实现赋值
执行y = 'YES';,入栈,紧接着出栈实现赋值
执行b(),入栈
现在栈中有
a()、b(),在function a()环境下:
x = 3
f = 'undefined'另外修改了Global环境下
y = 'YES'
3.function b()
执行console.log(x);,由于此时使用最近的外层function a()环境下的x = 3,打印输出
返回匿名函数function()
4.function a()
执行let f = b();,给变量f赋值返回的匿名函数function()
执行setTimeout(f,1000);,将函数f()放入到回调队列中等待
执行console.log('a函数里面:'+name);,此时name = 'Hellen',打印输出
紧接着b()、a()会依次出栈,此时栈为空
5.function()
满足setTimeout执行条件(Call Stack为空、到达设定时间),调用f(),将f()放入堆栈中
f()出栈执行,打印y




















![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)