前言: 在想要去了解闭包之前, 有一个知识点是一定要学习的, 那就是作用域 (链) 和词法作用域, 这些可以帮助我们更好的理解闭包.
1. 作用域(链)
犀牛书上这样说: 一个变量的作用域 (scope) 是程序源代码中定义这个变量的区域, 全局变量拥有全局作用域, 即在 JavaScript 代码中任何地方都是有定义的. 然而在函数内部声明的变量只在函数体内有定义, 它们是局部变量, 作用域是局部性的. 函数参数也是局部变量, 它们只在函数体内有定义.
我自己的理解就是定义这个变量, 并可以在这里找到这个变量的区域, 来看两段代码:
- // 代码段 1
- var a = 7;
- function foo(){
- console.log(a)
- }
- foo() // 输出 7
- // 代码段 2
- function foo(){
- var b = 9;
- console.log(b);
- }
- foo()// 输出 9
在 foo 函数执行的时候, 两端代码分别输出了 a 变量和 b 变量, 那么 a 变量和 b 变量是哪里来的呢, 我们可以明确的看到变量 a 定义在全局中, 变量 b 定义在函数中, 也就是说: 我们在全局作用域中找到了 a 变量, 在局部作用域中找到了 b 变量.
同时, 我们在查找 a 变量的时候, 先在函数作用域中查找, 没有找到, 再去全局作用域中查找, 这样一个从里向外层查找的过程, 像是顺着一条链条从下往上查找变量, 这条链条, 我们就称之为作用域链.
如图就是:
image
2. 词法作用域
犀牛书上说: JavaScript 是基于词法作用域的语言: 通过阅读包含变量定义在内的数行源码就能知道变量的作用域.
换言之: 所谓的词法作用域就是在你写代码时将变量和块作用域写在哪里来决定, 也就是词法作用域是静态的作用域, 在你书写代码时就确定了.
image
A 为全局作用域, 有一个标识符: fn1
B 为 fn1 所创建的作用域, 有三个标识符: x,y,fn2
C 为 fn2 所创建的作用域, 有一个标识符: z
作用域是由期代码写在哪里决定的, 并且是逐级包含的.
在此强调, 词法作用域就是作用域是由书写代码时函数声明的位置来决定的. 编译阶段就能够知道全部标识符在哪里以及是如何声明的, 所以词法作用域是静态的作用域, 也就是词法作用域能够预测在执行代码的过程中如何查找标识符.
注 1:eval()和 with 可以通过其特殊性用来 "欺骗" 词法作用域, 不过正常情况下都不建议使用, 会产生性能问题.
注 2:ES6 中有了 let,const 就有了块级作用域, 后面会专门介绍.
参考(侵删):
深入理解闭包之前置知识 --- 作用域与词法作用域www.jianshu.com
图标
来源: http://www.jianshu.com/p/2ad784b2ab23