目录
定义
引子
闭包的用途
一个常见错误
定义
闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境
引子
首先看一个例子
1
2
3
4
5
6
7
8
9
|
function
makeFunc() {
var
name =
"Mozilla"
;
function
displayName() {
alert(name);
}
return
displayName;
}
var
myFunc = makeFunc();
myFunc();
|
这段代码看起来别扭却能正常运行。通常,函数中的局部变量仅在函数的执行期间可用。一旦 makeFunc()
执行过后,我们会很合理的认为 name 变量将不再可用。不过,既然代码运行的没问题,显然不是我们想象的那样。在我们的例子中,myFunc
是一个闭包,由 displayName
函数和闭包创建时存在的 "Mozilla" 字符串形成。
这就是闭包,我们在返回函数的时候,也将函数的环境一并返回了。
闭包的用途
-
响应事件而执行的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<
html
>
<
head
>
<
script
type
=
"text/javascript"
>
function makeSizer(size){
return function(){
document.body.style.fontSize=size+"px";
}
}
var size12= makeSizer(12);
var size14= makeSizer(14);
var size16= makeSizer(16);
window.onload=function(){
document.getElementById("a12").onclick=size12;
document.getElementById("a14").onclick=size14;
document.getElementById("a16"). name="a12" id="a12">12</
a
>
<
a
name
=
"a12"
id
=
"a14"
>14</
a
>
<
a
name
=
"a12"
id
=
"a16"
>16</
a
>
</
body
>
</
html
>
|
2. 模拟似有方法
诸如 Java 在内的一些语言支持将方法声明为私有的,既它们只能被同一个类中的其它方法所调用。
对此,JavaScript 并不提供原生的支持,但是可以使用闭包模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<script type=
"text/javascript"
>
function
MyObject(){
var
name=
""
;
return
{
getName:
function
(){
return
name;
},
setName:
function
(str){
name=str;
}
}
}
var
mo = MyObject();
mo.setName(
"Mo1"
);
alert(mo.getName());
//Mo1
alert(mo.name);
//undefined
</script>
|
在上面的例子中,name作为私有属性,getName,setName为公有方法。
一个常见的错误
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
|
<
html
>
<
head
>
</
head
>
<
body
>
<
p
id
=
"help"
>Helpful notes will appear here</
p
>
<
p
>E-mail: <
input
type
=
"text"
id
=
"email"
name
=
"email"
></
p
>
<
p
>Name: <
input
type
=
"text"
id
=
"name"
name
=
"name"
></
p
>
<
p
>Age: <
input
type
=
"text"
id
=
"age"
name
=
"age"
></
p
>
<
script
type
=
"text/javascript"
>
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i <
helpText.length
; i++) {
var
item
=
helpText
[i];
document.getElementById(item.id)
.onfocus
=
function
() {
showHelp(item.help);//item.help 为最后一个对象的help
}
}
}
setupHelp();
</script>
</
body
>
</
html
>
|
该问题的原因在于赋给 onfocus
的函数是闭包;它们由函数定义和记录自 setupHelp
函数作用域的环境构成。一共创建了三个闭包,但是它们都共享同一个环境。在 onfocus
的回调被执行时,循环早已经完成,且此时 item
变量(由所有三个闭包所共享)已经指向了 helpText
列表中的最后一项。
修正
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
|
<
html
>
<
head
>
</
head
>
<
body
>
<
p
id
=
"help"
>Helpful notes will appear here</
p
>
<
p
>E-mail: <
input
type
=
"text"
id
=
"email"
name
=
"email"
></
p
>
<
p
>Name: <
input
type
=
"text"
id
=
"name"
name
=
"name"
></
p
>
<
p
>Age: <
input
type
=
"text"
id
=
"age"
name
=
"age"
></
p
>
<
script
type
=
"text/javascript"
>
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function makeHelpCallback(help){
return function(){
showHelp(help);
}
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i <
helpText.length
; i++) {
var
item
=
helpText
[i];
document.getElementById(item.id)
.onfocus
=
makeHelpCallback
(item.help);
}
}
setupHelp();
</script>
</
body
>
</
html
>
|
这段代码可以如我们所期望的那样工作。所有的回调不再共享同一个环境, makeHelpCallback
函数为每一个回调创建一个新的环境。在这些环境中,help
指向 helpText
数组中对应的字符串。
参考:https://developer.mozilla.org/zh-CN/docs/JavaScript/Guide/Closures
本文出自 “简单” 博客,请务必保留此出处http://dba10g.blog.51cto.com/764602/1357820