闭包和作用域链

闭包是指有权访问另一个函数作用域中的变量的函数。(《JavaScript高级程序设计》7.2闭包)

所有的JavaScript函数都是闭包:它们都是对象,它们都关联到作用域链。和其他大多数现代编程语言一样,JavaScript也采用词法作用域(lexical scoping),也就是说,函数的执行依赖于变量作用域, 这个作用域是在函数定义时决定的,而不是函数调用时决定的。(《JavaScript权威指南》8.6闭包)

当JavaScript需要查找变量的x的值的时候,它会从作用域链中的第一个对象开始查找, 如果这个对象没有名为x的属性,就会继续查找链上下一个对象,最后到全局作用域,如果全都没有找到就会抛出一个引用错误(ReferenceError)异常。

示例

1
2
3
4
5
6
7
8
9
10
function closure() {
var count = 0;
return function(){
return count += 1
}
}
var f = closure();
console.log(f()) // 1
console.log(f()) // 2
console.log(count) // error: count is not defined

closure函数返回了一个匿名函数,将其赋值给f,多次调用f,发现closure函数内的count累计增加。

当我们想打印全局作用域中的count时,报错了。由于作用域是在函数定义时决定而不是函数调用时决定的,f return的变量是closure函数作用域内的count,而不是全局作用域的count。

总结

  • 作用:
    • 延长局部变量的生命周期
    • 让函数外部能操作内部的局部变量
  • 闭包的应用场景:
    • 模块化
    • 循环遍历加监听
    • 柯里化
    • JS框架(jQuery)大量使用了闭包
  • 缺点:
    • 变量占用内存的时间可能会过长
    • 可能导致内存泄露
    • 解决:及时释放 : f = null; //让内部函数对象成为垃圾对象

想要通过更多实例来理解闭包,可以看大佬的这篇文章理解js闭包