要了解闭包这个概念,首先需要弄清作用域是什么.
作用域和自由变量
所谓万丈高楼平地起,盘龙卧虎高山齐,在进入闭包的学习之前,我们先要去了解一下作用域的概念,只能说真正理解作用域才能更好地学习闭包,通俗来讲,作用域就是在定义的变量在某个范围内是可使用的一个区域。
简单地画了个图,上述的框框我们可以看出,其实作用域就是某个变量可使用的合法范围,比如变量a,作为全局作用域,是公开使用的,不论是函数外部还是fn1,2,3都可以使用,就好比公厕这种地方是面向全社会人员的。我们再看看fn3,假如fn2,fn1或者函数外部需要使用或者修改a3这个变量,是无法使用的。
在这里顺便讲一下自由变量,自由变量就是一个变量在当前作用域没有定义,但被使用了,我们再来看看这张图
这个代码段的执行顺序是,先调用fn1()、fn2()、fn3() ,当执行fn3()的时候,返a + a1 + a2 + a3,因为在fn3()的作用域没有a2,a1,a所以要往上一层fn2寻找,fn2没有a1,a所以再往上一层fn1找,fn1没有a,所以最后要到全局作用域才能找到a。因为a2,a1,a不在fn3的作用域范围,所以它们可称为自由变量
再啰嗦多一句,如果全局作用域都没找到的变量,则会报错——xx is not defined
闭包
上述的讲解同学们应该可以了解到了作用域到底是神马东西了,那么闭包这玩意,其实就是作用域的特殊情况,它有两种表现:
1.函数作为参数传递
2.函数作为返回值返回
我们先看看两个代码片段
// 函数作为返回值
function fun (){
const a = 100;
return function(){
console.log(a);//100
}
}
const fn = fun();
const a = 200;
fn();
// 函数作为参数被传递
function print(fn){
const a = 200;
fn();
}
const a = 100;
function fun (){
console.log(a);//100
}
print(fun);
解释一下控制台的打印结果,为什么是100呢,上面的内容已经讲解到,自由变量往往不存在当前的作用域范围中,所以需要一层一层地往上寻找,console.log(a)里面的变量a在函数中未被定义,所以需要往上一层寻找自己被定义和赋值的地方。
闭包的应用
我们先看一个代码片段
// 闭包隐藏数据,只提供API
function Cache(){
// 闭包中的数据被隐藏,不被外界访问
const data = {};
return{
set: function(key,val){
data[key] = val
},
get: function(key){
return data[key]
}
}
}
const t = Cache()
t.set('money',100)
console.log(t.get('money'));//100
上述代码中,我们简单做了一个Cache工具,在函数返回值返回两个函数,set用于传值,get用于取值,最后在控制台中打印出100。那我们能不能直接修改data的值?
// 闭包隐藏数据,只提供API
function Cache(){
// 闭包中的数据被隐藏,不被外界访问
const data = {};
return{
set: function(key,val){
data[key] = val
},
get: function(key){
return data[key]
}
}
}
data.b = 200 //控制台会报错
相信同学们已经知道为什么data.b = 200不可行吧,原因就是作用域问题,data是在Cache函数的作用域中,作用域外面并不能获取到它的值。所以这里函数主要的功能是只提供API,那么闭包中的data数据则由它自己来管理,外界无法修改。