- 写了挺久的也没写多少。
- 所以说其实闭包是很简单的啊233。
正文
作用域
什么是作用域
作用域按词义理解可以理解为变量的作用范围,也就是在哪里才能够访问到这个变量。
function foo() {
function bar() {
var b = 2
}
console.log(b) // b is not defined
}
以上代码中,函数foo就创建了一个作用域,foo内部的函数bar也创建了一个作用域。这时如果在foo内访问bar内的变量b,则会报错:b未被定义。这是因为bar的作用域确定了变量b只在bar的函数体内可以被访问。
function foo() {
var a = 1
function bar() {
console.log(a) // 1
}
}
而如果在foo内定义变量a并在bar内调用的话则会正常访问并输出。这是因为foo和bar形成了嵌套的关系,而在作用域中查找变量时会首先从自身作用域开始寻找,如果没有找到则会继续跳转到父级作用域中继续查找,直到得到该变量或查找到全局作用域为止。而这种作用域层层嵌套的结构就被称为作用域链。
函数作用域和块级作用域
在ES6出现之前,JavaScript中没有块级作用域的概念,只有函数才能创建作用域,也就是函数作用域。
函数作用域如上文所示,只要创建了一个函数就会创建一个函数作用域,外界无法访问作用域内部的变量。而块级作用域则由一对花括号创建:
var foo = true
if (foo) {
var a = 10
}
console.log(a) // 10
以上代码看似为a创建了一个单独的作用域,然而因为JavaScript中没有块级作用域,导致在花括号外依然能够访问到a。这很令人困惑,而且可能会导致一些意想不到的问题。好在ES6解决了这个问题,使用let关键字来声明变量,就可以将变量绑定到块级作用域中。
var foo = true
if (foo) {
let a = 10
}
console.log(a) // a is not defined
词法作用域和动态作用域
简单来说,词法作用域取决于代码书写的位置,而动态作用域取决于代码运行的位置。JavaScript中采用的是词法作用域,记住这一点对理解闭包很有帮助。
闭包
什么是闭包?
var a = 10
function foo() {
console.log('a') // 10
}
这就是一个闭包。
当然这样是难以理解的,这里将它稍微修改一下:
function foo() {
var a = 10
function bar() {
console.log(a)
}
return bar
}
var a = 20
var baz = foo()
baz() // 10
首先按照错误的思路来理解这段代码:foo执行时声明一个函数bar并返回,这里将它赋值到变量baz上。foo执行结束后其内部作用域理应被销毁。这时再执行函数baz时需要访问变量a,于是就在当前作用域中找到a为20并输出。
然而实际上foo函数在执行结束后,由于bar函数内部存在对foo内变量a的引用,使得foo的内部作用域不会被销毁而是随着函数bar一同被保留了下来。当bar函数在外部被调用时,访问的也并非是它被调用时所在的作用域,而是它被定义时所在的作用域,也就是词法作用域。
回到这部分第一句话,什么时候会产生闭包?
当一个函数拥有其自身作用域以外的变量的引用,这时就产生了闭包。
闭包基于词法作用域产生,只要掌握了作用域,也就理解了闭包。而实际编程中闭包也总是在不经意间被创建,我们只需要看到写完的代码,能知道 “啊,这是闭包” 就够了。
结语
推荐《你不知道的JavaScript(上卷)》
网友评论