: 闭包

每当我们运行代码的时候,代码就会生成执行上下文(可以理解为执行环境)

JavaScript执行环境主要分为三种:

  • 全局环境
  • 函数环境
  • Eval环境

JavaScript用栈处理多个执行上下文

img

作用域链

创建执行上下文分两个阶段

  • 创建阶段

    • 作用域链
      • 包含当前变量对象+所有父级变量对象
    • 变量对象(参数、变量函数声明)
    • this
  • 执行阶段

    • 变量赋值、函数引用等
复制代码
function books(){
  var book = "书包里面的书本"
  return function(){
    console.log(book);
  }
}
var bag = books();
bag()

上面代码的上下文创建顺序

全局执行上下文 = {作用域链:{全局变量对象}, {变量对象: books, bag}}

books执行上下文 = {作用域链:{books变量对象+全局变量对象},{变量对象:book}}

匿名函数执行上下文 = {作用域链:{匿名函数遍历对象+books变量对象+全局变量对象},{变量对象:}}

一道题:

复制代码
for(var i = 0;i<5;i++){
  setTimeout(function(){
    console.log(i++);
  },4000)
}
console.log(i);//556789

解析:

顺序是这样的,1. 循环走完 设置完一堆定时器 定时器里的函数并没有执行;2. 执行第6条的log输出第一个5;3. 定时器时间到,开始执行,此时i从5开始计算 输出 5,6,7,8,9;

闭包的应用-数据私有

没有使用闭包

复制代码
let i = 0;
function fn(){
  i++
  console.log(i);
}
//另外一个程序
i= 1000

fn()//1001

我们希望i这个变量是私有的,脱离全局上下文环境

复制代码
function count(){
  let i = 0;
  function fn(){
    i++
    console.log(i);
  }
  return fn
}
i = 1000
const fun = count()
fun()//1

这样子i就不在全局上下文环境中了,不会被全局上下文中的代码修改掉。

闭包内存泄露

复制代码
function count(){
  let i = 0;
  function fn(){
    i++
    console.log(i);
  }
  return fn
}
const fun = count()
  1. fun是一个全局变量,代码执行完毕不会立即销毁
  2. fun使用count函数
  3. count函数使用fn函数
  4. fn函数里面有用到i
  5. i被引用就不会被回收,所以一直存在

此时:闭包引起了内存泄漏

不是所有的内存泄露都要手动回收的

比如react里面很多闭包不能回收的