JavaScript 闭包学习笔记

2017-02-21  本文已影响7人  ltaoo

什么是闭包?直接上代码:

var name = "global"
function wrapper() {
  var name = "local"
  function echo() {
    4、-------------------------
    console.log(name)
  }
  // 2、-------------------------
  return echo
}
// 1、---------------------------
var foo = wrapper()
// 3、---------------------------
foo() // 打印 local,这就是闭包

详细解读

之前在学习闭包时,简单记忆为:

函数能够 记住 定义时作用域中的变量。

比如这里的name="local",但是一直没有深入了解为什么能够访问。在了解了作用域链后,答案很明显,因为函数对象的[[scope]]属性。

像之前一样对作用域链的变化进行详细分析。

1、调用 wrapper() 前的作用域链:

// 调用 wrapper() 前的作用域链
scopes = {
  0: {
    name: "global",
    wrapper: {
      name: "wrapper",
      [[scope]]: {
        0: {...}
      }
    },
    foo: undefined,
    // 以及全局变量与方法
  }
}

2、调用wrapper函数时的作用域链,将warpper的变量对象加入到自身的[[scope]]对应的socpes中,就是这样的:

// 调用 wrapper 函数时
scopes = {
  1: {
    name: "local",
    echo: {
      name: "echo",
      [[scope]]: {
        1: {...},
        0: {...}
      }
    }
  },
  0: {
    name: "global",
    wrapper: {
      name: "wrapper",
      [[scope]]: {
        0: {...}
      }
    },
    foo: undefined,
    // 以及全局变量与方法
  }
}

重点在于echo对象的[[scope]]保存了两个变量对象,然后将这个echo对象返回。

3、调用foo函数前的作用域链:

scopes = {
  0: {
    name: "global",
    wrapper: {
      name: "wrapper",
      [[scope]]: {
        0: {...}
      }
    },
    foo: {
      name: "foo",
      [[scope]]: {
        1: {...},
        0: {...}
      }
    },
    // 以及全局变量与方法
  }
}

这里和 1、---- 处不同在于fooundefined变成了对象,并且这个对象就是3、----处的echo对象。

4、调用 foo 函数时的作用域链:

scopes = {
  2: {
    this: Window
  },
  1: {
    name: "local",
    echo: {
      name: "echo",
      [[scope]]: {
        1: {...},
        0: {...}
      }
    }
  },
  0: {
    name: "global",
    wrapper: {
      name: "wrapper",
      [[scope]]: {
        0: {...}
      }
    },
    foo: {
      name: "foo",
      [[scope]]: {
        1: {...},
        0: {...}
      }
    },
    // 以及全局变量与方法
  }
}

echo函数内会寻找name变量,在作用域链的第一个变量对象中没有找到(2对应的对象)就往下继续找,在1中找到了,并且是local,所以最终打印出local

同样以一张图片来更为直观的了解:

作用域链变化

echo函数内的console.log(name)查找name时首先在自身的变量对象中寻找,没有找到后往下继续寻找,找到了local后就返回,所以不会再继续往下找到global。最终打印local

随处都是闭包

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
—— 《你不知道的 JavaScript 》(上卷)

按照上面的定义,只要声明了函数,函数是能够记住当时的作用域链的,所以只要声明了函数,就产生了闭包?

function foo() { 
  var a = 2;
  function bar() { 
    console.log( a ); // 2
  }
  bar(); 
}
foo();

bar记住了定义时的作用域链,在这作用域链顶端的变量对象有a: 2的键值对,在其他任何地方调用bar都能够访问到a变量。

但是更严格来说,函数在当前词法作用域之外执行,才算闭包。

参考

上一篇下一篇

猜你喜欢

热点阅读