js的执行,由执行栈控制。执行栈存储执行上下文,每次执行栈顶那个。
函数执行前,创建执行上下文,并压入执行栈。
函数执行完毕后,它的执行上下文从栈中弹出,控制流程到达栈顶的执行上下文。
执行上下文主要包括3部分:this、AO、scopeChain。
一个js文件执行流程:
- 执行全局代码时,会创建全局上下文globalContext,并入栈。
// 全局上下文 globalContext = { VO: [ 全局变量,函数...]; scopeChain: VO; this: window; }
- 遇到函数声明,fn.[[ scope ]] = 父.scopeChain。
- 遇到函数执行,创建函数执行上下文,并入栈。
- 函数执行上下文初始化
- scopeChain = fn.[[ scope ]]
- 通过VO构造出自己的AO(arguments、函数声明、变量声明)
- scopeChain = [ AO, [[ scope ]] ]
- 随着函数执行,AO里变量值变化。
- 执行完,出栈,销毁执行上下文,断开对变量的引用,流程交给栈顶。
- 函数执行上下文初始化
闭包:一个可以访问外部作用域的内部函数。
即使外部作用域已经执行结束,内部函数还活着。主要包括两种情况:
- 异步任务
- setTimeout setInterval
- 事件 onXXX
- ajax回调
- 其他异步API(H5 Geolocation/WebSockets/requestAnimationFrame())
- 内部函数被返回or被引用
so 闭包会导致 函数具有私有状态。
内存泄漏
由于闭包含有对外部变量的引用,当使用完,却不断开引用时,会导致其他地方也能通过闭包拿到变量,可能造成内存泄漏。js垃圾回收机制,当一个变量不再被任何地方引用时,便为可回收,可销毁。所以,闭包使用完,要设为null;或让其他地方无法再用到。
题
for (var i=0; i<5; i++) {
setTimeout(function() {
console.log(i);
}, 1000)
}
console.log(i);
以上代码,输出啥~
正确答案是:5->5,5,5,5,5
for循环,共执行5次。每次都将一个1s后打印i的任务放到微任务队列里。当i=5时,跳出循环,打印i的值,此时i为5。1s后,5个微任务一起执行,打印5次i的值,此时这里的i会拿到外面的i的引用,即为5。
如果想输出 5->0,1,2,3,4 该怎么改造代码?
// 立即执行函数
for (var i=0; i<5; i++) {
(function(j) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}
console.log(i);
// setTimeout API
for (var i=0; i<5; i++) {
setTimeout(function(j) {
console.log(j);
}, 1000, i)
}
console.log(i);
// 传参
var output = function (i) {
setTimeout(function() {
console.log(i);
},1000)
}
for(var i=0; i<5; i++) {
output(i);
}
console.log(i);
// let
for(let i=0; i<5; i++) {
setTimeout(function() {
console.log(i);
}, 1000)
}
// 但是这里会报错,因为i只在循环内部
console.log(i);
如果想输出 0->1->2->3->4->5 咋改?
// 立即执行函数+时间间隔
for (var i=0; i<5; i++) {
(function(j) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}
setTimeout(function() {
console.log(i);
}, 1000*i);
// Promise
const task = [];
const output = (i) => new Promise((resolve) => {
setTimeout(() => {
console.log(i);
resolve();
}, 1000 * i)
})
for (var i=0; i<5; i++) {
tasks.push(output(i));
}
Promise.all(tasks).then(() => {
setTimeout(() => {
console.log(i);
}, 1000);
})
// ES7 async/await
const sleep = (timeoutMS) => new Promise((resolve) => {
setTimeout(resolve, timeoutMS);
})
(async () => {
for (var i=0; i<5; i++) {
if (i>0) await sleep(1000);
console.log(i);
}
})();
await sleep(1000);
console.log(i);
闭包的应用
还没咋理解。后面补。