1.函数作用域
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
</head>
<body >
<div id="div1" style="height: 100px;background: red;"></div>
</body>
</html>
<script type="text/javascript">
var n=99;
function outer() {
var n=0;
alert(n);
}
document.getElementById("div1").onclick=outer();
</script>
运行结果:
访问的n是outer函数内部的不是全局的n,作用域链原理,先访问自己作用域的,没有才顺着链向上一层层找,直至找到,否则报错,找不到变量。
2.计数器,记录点击次数
第一种方法:这是使用全局变量实现,全局变量常驻内存,一直保存着
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
</head>
<body>
<div id="div1" style="height: 100px;background: red;"></div>
<div id="div2" style="height: 100px;margin: 10px; background: green;"></div>
</body>
</html>
<script type="text/javascript">
var n = 99;
function outer() {
return ++n;
}
document.getElementById("div1").onclick = function() {
this.innerHTML = outer();
}
</script>
第二种方式推导:全局变量使用总害怕其他地方有冲突,一直常驻内存,占用资源等弊端。
第1步骤:在outer函数内加n变量
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
</head>
<body>
<div id="div1" style="height: 100px;background: red;"></div>
<div id="div2" style="height: 100px;margin: 10px; background: green;"></div>
</body>
</html>
<script type="text/javascript">
var n = 99;
function outer() {
n=0;
return ++n;
}
document.getElementById("div1").onclick = function() {
this.innerHTML = outer();
}
</script>
结果每次单击出现的结果都是一样的。因为每次单击调用outer函数时候,作用域中会重新创建一个n变量,而且n=0都要执行一次
第2步骤:如何让这个n保存在内存,且不重置值。考虑到作用域链,如果这个outer函数内部还有一个inner()函数,这个内部函数调用outer()的变量,那么JavaScript是不会回收这个变量n的。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
</head>
<body>
<div id="div1" style="height: 100px;background: red;"></div>
<div id="div2" style="height: 100px;margin: 10px; background: green;"></div>
</body>
</html>
<script type="text/javascript">
var n = 99;
function outer() {
n = 0;
function inner() {
return ++n;
}
}
document.getElementById("div1").onclick = function() {
this.innerHTML = outer();
}
</script>
但显示打印的是undefined。运行查看发现单击调用的是outer(),它的函数体内没有返回值,
第3步骤:考虑到js中函数也可像变量一样当参数和返回值,所以把inner()整体做返回值,不但保留的n变量在作用域链,同时返回自加值。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
</head>
<body>
<div id="div1" style="height: 100px;background: red;"></div>
<div id="div2" style="height: 100px;margin: 10px; background: green;"></div>
</body>
</html>
<script type="text/javascript">
var n = 99;
function outer() {
n = 0;
return function inner() {
return ++n;
}
}
document.getElementById("div1").onclick = function() {
this.innerHTML = outer();
}
</script>
但返回的是
函数体,所以要在返回前,把函数赋给某个变量,得到结果
第4步骤:添加变量
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
</head>
<body>
<div id="div1" style="height: 100px;background: red;"></div>
<div id="div2" style="height: 100px;margin: 10px; background: green;"></div>
</body>
</html>
<script type="text/javascript">
var n = 99;
function outer() {
var n = 0;
return function inner() {
return ++n;
}
}
var counter = outer(); //outer()函数执行返回的就是inner()函数
document.getElementById("div1").onclick = function() {
this.innerHTML = counter(); //所以执行counter()函数就是执行inner()函数,inner()函数有的作用域链,counter()函数也能同样保留。
}
</script>
在这里插入图片描述
这样外部函数counter通过调用inner访问到了outer里定义的n;
外部函数调用内部函数的变量,或者平辈函数调用另一个函数的变量。都称为闭包。
很类似:你同学要借你爸的车用,做法却是约你去郊游。
3.两个计数器,记录点击次数
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JavaScript闭包属性详解</title>
</head>
<body >
<div id="div1" style="height: 100px;background: red;"></div>
<div id="div2" style="height: 100px;margin: 10px; background: green;"></div>
</body>
</html>
<script type="text/javascript">
var n=99;
function outer() {
var n=0;
return function inner() {
return ++n;
}
}
var counter=outer();
document.getElementById("div1").onclick=function () {
this.innerHTML=counter();
}
var counter2=outer();
document.getElementById("div2").onclick=function () {
this.innerHTML=counter2();
}
</script>
counter()和counter2(),虽然都把outer()赋给他,但各做各的,counter()和counter2()各自在内存中引起建立两套不同的outer()和它内部的n和inner。
如何形象的理解这个内容呢?
我们可以以篮球投篮类比:
function 投篮(){
记分牌=0;
计分变量=function 投进(){
记分牌=记分牌+2;
}
return 计分变量;
}
单击div=function 投篮();