javascript 闭包
闭包的理解:
简单来说,闭包就是在另一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对),而这些变量(键值对)是不会随着上一级函数的执行完成而被销毁。
常用的闭包实现方法:
1.函数闭包
(function(){ //函数闭包 })()
2. try catch
try{ }catch(e){ //catch闭包 }
3.with 对象闭包
with(obj){ //对象闭包 }
闭包的常用三种实现方法:
找一个经典例子来演示闭包的实现方法。
<!doctype html> <title>javascript闭包</title> <meta charset="utf-8"/> <script type="text/javascript"> window.onload = function(){ var lists = document.getElementsByTagName('li'); for(var i = 0,len = lists.length; i < len; i++){ lists[i].onclick = function(){ alert('点击的是:' + i); } } } </script> <ul> <li id="list1">测试列表</li> <li id="list2">测试列表</li> <li id="list3">测试列表</li> <li id="list4">测试列表</li> <li id="list5">测试列表</li> </ul>
本来是想点依次点击li弹出0 1 2 3 4,可是弹出的却是4 4 4 4 4。
注:onclick绑定的函数function(){alert('点击的是:'+i)}的作用域为对应的li对象,而函数里面的i的作用域为window,每次循环window.i的值都被重新赋值,因此,循环完后,i已经是4。所以点击哪个都是4。
解决方法:
1. 使用函数闭包
<!doctype html> <title>javascript闭包</title> <meta charset="utf-8"/> <script type="text/javascript"> window.onload = function(){ var lists = document.getElementsByTagName('li'); for(var i = 0,len = lists.length; i < len; i++){ lists[i].onclick = (function(){ var num = i; return function(){ alert('点击的是:' + num); } })() } } </script> <ul> <li id="list1">测试列表</li> <li id="list2">测试列表</li> <li id="list3">测试列表</li> <li id="list4">测试列表</li> <li id="list5">测试列表</li> </ul>
或者
<!doctype html> <title>javascript闭包</title> <meta charset="utf-8"/> <script type="text/javascript"> window.onload = function(){ var lists = document.getElementsByTagName('li'); for(var i = 0,len = lists.length; i < len; i++){ lists[i].onclick = (function(i){ return function(){ alert('点击的是:' + i); } })(i) } } </script> <ul> <li id="list1">测试列表</li> <li id="list2">测试列表</li> <li id="list3">测试列表</li> <li id="list4">测试列表</li> <li id="list5">测试列表</li> </ul>
还可以用块级作用域来实现,将let 定义的变量具备块级作用域。在for循环中,每一次循环都会形成一个新的块级作用域,所以i不会被改变
<!doctype html> <title>javascript闭包</title> <meta charset="utf-8"/> <script type="text/javascript"> window.onload = function(){ var lists = document.getElementsByTagName('li'); for(let i = 0,len = lists.length; i < len; i++){ lists[i].onclick = function(){ alert('点击的是:' + i); } } } </script> <ul> <li id="list1">测试列表</li> <li id="list2">测试列表</li> <li id="list3">测试列表</li> <li id="list4">测试列表</li> <li id="list5">测试列表</li> </ul>
2.使用try...catch语句构造的异常闭包
<!doctype html> <title>javascript闭包</title> <meta charset="utf-8"/> <script type="text/javascript"> window.onload = function(){ var lists = document.getElementsByTagName('li'); for(var i =0,len = lists.length; i < len; i++){ try{ throw i;//自定义抛出异常 }catch(e){ lists[e].onclick = function(){ alert('点击的是:' + e); } } } } </script> <ul> <li id="list1">测试列表</li> <li id="list2">测试列表</li> <li id="list3">测试列表</li> <li id="list4">测试列表</li> <li id="list5">测试列表</li> </ul>
3.使用with语句造成的对象闭包
<!doctype html> <title>javascript闭包</title> <meta charset="utf-8"/> <script type="text/javascript"> window.onload = function(){ var lists = document.getElementsByTagName('li'); for(var i =0,len = lists.length; i < len; i++){ var obj = {value: i} with(obj){ lists[value].onclick = function(){ alert('点击的是:' + value) } } } } </script> <ul> <li id="list1">测试列表</li> <li id="list2">测试列表</li> <li id="list3">测试列表</li> <li id="list4">测试列表</li> <li id="list5">测试列表</li> </ul>
几个经典有用的实例:
1.自加
<!doctype html> <title>javascript闭包</title> <meta charset="utf-8"/> <script type="text/javascript"> /** * 自加 */ addSelf = (function(){ var i = 0; return function(){ return i++; } })(); alert(addSelf()) //0 alert(addSelf()) //1 alert(addSelf()) //2 </script>
2.阶乘
<!doctype html> <title>javascript闭包</title> <meta charset="utf-8"/> <script type="text/javascript"> /** * 阶乘 */ var factorial = function(n){ if(n < 1){ alert('invalid arguments'); return 0; } if(n == 1){ return 1; }else{ return n*arguments.callee(n-1); } } alert(factorial(3)) //6 alert(factorial(4)) //24 </script>
3.设置/获取 属性值
<!doctype html> <title>javascript闭包</title> <meta charset="utf-8"/> <script type="text/javascript"> function User(properties){ var self = this; for(var i in properties){ (function(){ var property = properties[i]; self['get' + i] = function(){ return property; } self['set' +i] = function(value){ property = value; } })(); } } //测试代码 var user = new User({ name:'Bob', age: '27' }); alert(user.getname()) //Bob alert(user.getage()) // 27 user.setname('Mike'); alert(user.getname()) //Mike alert(user.getage()) // 27 user.setage(22); alert(user.getname()) //Mike alert(user.getage()) // 22 </script>
实际开发中闭包的应用
1.隐藏数据,只提供API
// 闭包隐藏数据,只提供API function createCache() { const data = {} //闭包中的数据,不被外界访问 return { set(key,value){ data[key] = value }, get(key){ return data[key] } } } const c = createCache() c.set('a',100) c.get('a')