详谈一下闭包是什么

闭包(closure)是JavaScript语言的特色,很多高级应用都要依靠闭包实现。作为一个初学者,还是用最通俗易懂的语言解释一下闭包吧。

在讲闭包之前,我们先复习一下

变量的作用域

变量的作用域两种:全局变量和局部变量。
在Javascript语言中函数内部可以直接读取全局变量
先看个小例子。
例1:

1
2
3
4
5
6
7
8
9
<script>
var a = "我是全局变量";
function fun(){
var b = "我是局部变量";
console.log(b);
console.log(a);
}
fun();
</script>

毫无疑问,打印出来的就是
我是全局变量
我是局部变量
但是,如果我要这样写呢?
例2:

1
2
3
4
function fun(){
var b = "我是局部变量";
}
console.log(b);

结果输出: b is not defined 让你大眼一看,你也肯定会说,这个不是胡扯嘛,b明明就是局部变量,怎么能在函数体外调用呢!结果不出错才奇怪呢! 可我就想调用函数体内的变量,怎么办? 别急,既然你能想出这个问题,那么就肯定有办法解决。这不 “闭包”这个大神就给你支招了,只要你用了“闭包”,你就能让fun 函数以外的函数 来 使用 b 的值 。

闭包的概念

在程序语言中,所谓闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。这些外部执行域的非持久型变量神奇地保留他们在闭包最初定义(或创建)时的值。
大白话的将: 我们可以用一个函数 去访问 另外一个函数的内部变量的方式就是闭包。
为了让自己更明白,继续来举个例子,
例3:

1
2
3
4
5
6
7
8
function outFun() {
var num = 9; // num 是outFun函数内 的一个变量
function inFun() {
var key = 10;
console.log(num); // 外部函数outFun的变量num可以被内部函数inFun所使用
}
console.log(key); // 这样是不可以的,key是内部函数inFun的变量,内部函数的变量不可以被外部函数所使用
}

上面的例子中 我们知道num 这个变量只可以供内部函数使用,其他的不能随便使用。
可我想让外部的函数也使用num变量肿么办呢? 别急,看我给变变型。
例4:

1
2
3
4
5
6
7
8
9
10
11
12
function outFun(){
var num = 9;
function inFun(){
console.log(num);
}
return inFun; // 让outFun函数的结果 返回 inFun这个函数体 这也是闭包的核心语句
}
// 立即执行outFun这个函数,把返回的结果赋给fun
var fun = outFun(); // 这个点要多注意了
// outFun(); 返回的是 function inFun(){console.log(num);}
// 也就是说 var fun = function inFun(){console.log(num);}
fun();

输出结果就为9

所以外部函数 fun使用了 另一个函数outFun里面的变量num 这个创建的方法我们称之为闭包。
我们来看一道测试题,检验一下我们到底理解闭包了没.

思考题

例5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function outerFun() // 外部函数
{
var a=0; // 清空
function innerFun() // 内部函数
{
a++;
alert(a);
}
return innerFun; // 注意这里 核心
}
var obj=outerFun();
obj();
obj();
var obj2= outerFun();
obj2();
obj2();

返回的结果是 1 2 1 2
提示:在这里深深体会这句话,这些外部执行域的非持久型变量神奇地保留他们在闭包最初定义(或创建)时的值。

总结:

其实闭包的核心机制就是return一个内部函数。
再重复一遍: 如何让外部函数a访问另一个函数b内部的变量,直接访问,是行不通的。我们采用曲线救国的方法:让一个内部函数c可以使用b函数的变量,然后把这个内部函数c 给return出来,赋给外部函数a。再让外部函数a调用内部函数c。简单来讲就是让外部函数访问另一个函数的内部函数。
其中例4我们可以改成这个样子,

1
2
3
4
5
6
7
8
function outFun(){
var num = 9;
return function(){
console.log(num);
}
}
var fun = outFun();
fun();

闭包的优缺点 :

优点:不产生全局变量,实现属性私有化。
缺点:闭包中的数据会常驻内存,在不用的时候要删掉否则会导致内存溢出。
闭包的用法:闭包的函数传参。
例6,

1
2
3
4
5
6
7
function fun(x){
return function(){
console.log(x);
}
}
var obj = fun(4);
obj();

输出结果为 4

1
2
3
4
5
6
7
8
function fun(x){
return function(y){
console.log(x+y);
}
}
var obj = fun(4);
obj();
obj(5);

输出结果为 NaN 9
来个小例子,点击不同的按钮,让图片向左右不同的方向滑动

闭包传参示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
div{
width: 100px;
height: 100px;
background-color: pink;
position: absolute;
}
</style>
<script>
window.onload = function(){
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var box = document.getElementById("box")
function move(speed){
return function(){
box.style.left = box.offsetLeft + speed +"px";
}
}
btn1.onclick = move(5);
btn2.onclick = move(-5);
}
</script>
</head>
<body>
<button id="btn1">向右</button>
<button id="btn2">向左</button>
<div id="box"></div>
</body>
</html>

好啦,闭包的基础知识暂时讲到这里,以后还会深入总结闭包知识点的,敬请期待吧!

文章目录