闭包

JavaScript 使用词法作用域,作用域由源码的位置决定,而不是由运行时的位置决定(动态作用域)。

var value = 1 // A

function foo() {
  console.log(value)
}

function bar() {
  var value = 2 // B
  foo()
}

bar()

闭包名字来源于,函数的词法作用域包住了自由变量(不是函数的参数或内部变量)。

在实践中,因为闭包,内部函数在外部函数之外调用时,仍然可以访问外部函数内部的变量。内部函数在外部函数之外调用有多种方式

function makeAdder(x) {
  // 通过闭包访问 x
  return function (y) {
    return x + y
  }
}

// 创建两个闭包
var add5 = makeAdder(5)
var add10 = makeAdder(10)

console.log(add5(2))  // 7
console.log(add10(2)) // 12

JavaScript 没有访问修饰符,可以借助闭包保护私有数据

var makeCounter = function() {
  var _count = 0 // 私有数据,外界不能直接访问
  function changeBy(val) {
    _count += val
  }
  // 下面三个函数共享 `_count`
  return {
    increment: function() {
      changeBy(1)
    },
    decrement: function() {
      changeBy(-1)
    },
    get value() {
      return _count
    }
  }
}

var counter1 = makeCounter()
counter1.increment()
console.log(counter1.value) // 1

var counter2 = makeCounter()
console.log(counter2.value) // 0

在循环语句当中留意闭包

var arr = []
for (var i = 0; i < 3; i++) {
  arr.push(function () {
    return i
  })
}
console.log(arr[0]()) // A

arr 中所有的函数实际共享 i,返回值一样。当运行到 A 行时 i3,所以打印结果为 3。解决思路是不让这些函数共享 i

方法一

var arr = []
for (var i = 0; i < 3; i++) {
  (function () {
    var j = i
    arr.push(function () {
      return j
    })
  })()
}
console.log(arr[0]()) // ?

方法二

var arr = []
for (let i = 0; i < 3; i++) {
  arr.push(function () {
    return i
  })
}
console.log(arr[0]()) // A

使用 let 声明变量 i,每次循环重新绑定,函数不会共享 i

参考