本文共 3695 字,大约阅读时间需要 12 分钟。
目录
定义
引子
闭包的用途
一个常见错误
定义
闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境
引子
首先看一个例子
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
数组中对应的字符串。
本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1357820,如需转载请自行联系原作者