js-闭包/执行上下文/作用域链

js的执行,由执行栈控制。执行栈存储执行上下文,每次执行栈顶那个。

函数执行前,创建执行上下文,并压入执行栈。
函数执行完毕后,它的执行上下文从栈中弹出,控制流程到达栈顶的执行上下文。

执行上下文主要包括3部分:this、AO、scopeChain。

一个js文件执行流程:

  • 执行全局代码时,会创建全局上下文globalContext,并入栈。
    // 全局上下文
    globalContext = {
        VO: [ 全局变量,函数...];
        scopeChain: VO;
        this: window;
    }
    
  • 遇到函数声明,fn.[[ scope ]] = 父.scopeChain。
  • 遇到函数执行,创建函数执行上下文,并入栈。
    • 函数执行上下文初始化
      1. scopeChain = fn.[[ scope ]]
      2. 通过VO构造出自己的AO(arguments、函数声明、变量声明)
      3. scopeChain = [ AO, [[ scope ]] ]
    • 随着函数执行,AO里变量值变化。
    • 执行完,出栈,销毁执行上下文,断开对变量的引用,流程交给栈顶。

闭包:一个可以访问外部作用域的内部函数。
即使外部作用域已经执行结束,内部函数还活着。主要包括两种情况:

  1. 异步任务
    • setTimeout setInterval
    • 事件 onXXX
    • ajax回调
    • 其他异步API(H5 Geolocation/WebSockets/requestAnimationFrame())
  2. 内部函数被返回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);

闭包的应用

还没咋理解。后面补。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容