先看一下这段代码:

  1. function bar() {
  2. console.log(myName)
  3. }
  4. function foo() {
  5. var myName = "极客邦"
  6. bar()
  7. }
  8. var myName = "极客时间"
  9. foo()

按调用栈所示,相当然以为本栈没有,则向上级栈寻找,但是打印出来的是: 极客时间
image.png
其实:
image.png
把这个查找的链条就称为作用域链,JavaScript 执行过程中,其作用域链是由词法作用域决定的。

1. 词法作用域

词法作用域就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能够预测代码在执行过程中如何查找标识符。
image.png
整个词法作用域链的顺序是:foo 函数作用域—>bar 函数作用域—>main 函数作用域—> 全局作用域

  1. function bar() {
  2. console.log(myName)
  3. }
  4. function foo() {
  5. var myName = "极客邦"
  6. bar()
  7. }
  8. var myName = "极客时间"
  9. foo()

根据词法作用域,foo 和 bar 的上级作用域都是全局作用域,所以如果 foo 或者 bar 函数使用了一个它们没有定义的变量,那么它们会到全局作用域去查找。也就是说,词法作用域是代码阶段就决定好的,和函数是怎么调用的没有关系。

2. 闭包

  1. function foo() {
  2. var myName = "极客时间"
  3. let test1 = 1
  4. const test2 = 2
  5. var innerBar = {
  6. getName:function(){
  7. console.log(test1)
  8. return myName
  9. },
  10. setName:function(newName){
  11. myName = newName
  12. }
  13. }
  14. return innerBar
  15. }
  16. var bar = foo()
  17. bar.setName("极客邦")
  18. bar.getName()
  19. console.log(bar.getName())

image.png
image.png

定义:

在 JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。比如外部函数是 foo,那么这些变量的集合就称为 foo 函数的闭包。

使用:

image.png
image.png

回收

通常,如果引用闭包的函数是一个全局变量,那么闭包会一直存在直到页面关闭;但如果这个闭包以后不再使用的话,就会造成内存泄漏
通常,如果引用闭包的函数是一个全局变量,那么闭包会一直存在直到页面关闭;但如果这个闭包以后不再使用的话,就会造成内存泄漏