理解闭包前先要理解两个概念:作用域和作用域链。
一、作用域分为:
-
全局作用域
-
函数作用域
var a = 100 function fn() { var a = 200 console.log('local: ' + a) } fn() // local: 200 console.log('global: ' + a) // glocal: 100
执行上面的代码可以看到,第一个a是定义在全局作用域里的,第二个a是定义在函数作用域里的。在全局作用域的变量a和在函数作用域变量a互不影响。
注意:没有块级作用域。
二、作用域链
var a = 100
function f1() {
var b = 200
function f2() {
var c = 300
console.log(a)
console.log(b)
console.log(c)
}
f2()
}
f1() // 100 200 300
执行以上代码,依次打印出a, b, c的值100,200,300。其中a是定义在全局作用域里,b是定义在函数f1作用域里,c是定义在函数f2作用域里。
当代码执行到函数f2里的console.log(a)
的时候,首先会在函数f2的作用域找,没有 --> 到父级作用域即函数f1的作用域里面找,也没有 --> 再到父级作用域即全局作用域里面找,有,打印。
同理当console.log(b)
的时候首先会在函数f2的作用域找,没有 --> 到父级作用域即函数f1的作用域里面找,有,打印。
这三个作用域组成的有序集合(f2 -- f1 -- window)就是作用域链。
注意:函数的父级作用域指的是函数定义时候的决定的,而不是执行的时候。即函数在什么地方定义,父级作用域就在哪里。
三、闭包
闭包有两种应用场景:
-
函数作为返回值
function fn() { var a = 100 return function() { console.log(a) } } var f1 = fn() var a = 200 f1() // 100
fn返回一个函数,赋值给f1,执行f1()即执行fn的返回函数。返回函数在定义时的父级作用域是fn,因此返回函数里的变量a会到函数fn的作用域去找,而与函数f1执行时定义的全局作用域里的变量a无关。
-
函数作为参数传递
function f1() { var a = 100 return function() { console.log(a) } } var fn = f1() function f2(f) { var a = 200 f() } f2(fn) // 100
函数f2接收一个函数作为变量传入,执行函数f即执行函数f1,剩下的代码执行过程和场景1同理。
总结:闭包的特性让我们可以在函数作用域之外,通过作用域链访问到函数作用域内的变量,但是无法对其进行修改。
网友评论