前言
为什么我们需要理解并且掌握闭包,且不说大道理,也且不说你要不要成为JavaScript高手 那你要不要面试
找工作对吧??扎心
再者,对于任何一个前端或者JavaScript开发者来说,理解闭包可以看做是另一种意义上的重生。闭包是纯函数
编程语言的一个特性,因为他 大大简化复杂的操作,所以很容易在一些JavaScript库以及其他高级代码中找到闭包
的使用。
一言以蔽之,闭包,你就得掌握。
你所理解的闭包?
==> 官方一点:函数形成一个私有作用域,保护里面的私有变量不受外界的干扰,这种保护机制称为'闭包'
==> 通俗一点:形成一个不销毁的私有作用域(私有栈内存)才是闭包
常见的闭包种类
==>闭包:柯理化函数(高大上的名词)
function fn(){
return function()
}
}
var f = fn();
==>闭包:惰性函数
var utils = (function(){
return {
}
})();
代码处处有闭包
在经典的for循环中使用闭包
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
执行上述for循环,大家都知道输出6,因为在这个作用域中,我们只有一个i,所有的回调函数都是在这个for循环
结束以后才执行的。但是根据作用域工作原理,尽管循环中五个函数是在各个迭代中分别定义,但是他们都被封闭在
共享的作用域中 所以言归正传,我们需要使用闭包,在每一个循环中每一个迭代都让他产生一个闭包作用域。所以我
们代码修改如下:
for (var i=1; i<=5; i++) { (function() {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
})();
}
闭包项目实战应用
==>在做项目中为了保证JS的性能(堆栈内存的性能优化),应采取减少闭包的使用(不销毁的堆栈内存是消耗性能的)
==>闭包具有保护作用:保护私有变量不是受外界的污染,在做项目的时候可以将自己的代码封装在闭包中让全局变量
变为私有变量,以防止和别人之间相互冲突造成全局变量污染
(function(){
var n = 12;
function(){
}
//...
})();
==>闭包具有'保存'作用:形成不销毁的栈内存,我们需要调用的时候,值是保存在栈内存,方便调用
小案例讲解(选项卡)
先简单写一丢丢样式
<style>
* {
margin: 0;
padding: 0;
list-style: none;
}
.box {
width: 400px;
margin: auto;
}
.tabbox {
width: 100%;
height: 50px;
display: flex;
}
.tab {
flex: 1;
text-align: center;
line-height: 50px;
cursor: pointer;
}
.tab.current {
background: saddlebrown;
color: aqua;
}
.body {
width: 400px;
height: 400px;
font-size: 50px;
text-align: center;
border: 1px dashed skyblue;
display:none;
}
.body.current {
display: block;
}
</style>
</head>
<body>
<div class="box">
<ul class="tabbox">
<li class="tab current">1</li>
<li class="tab">2</li>
<li class="tab">3</li>
</ul>
<div class="body current">1</div>
<div class="body">2</div>
<div class="body">3</div>
</div>
</body>
</html>
JS代码实现
//获取要操作的元素
let tabs = document.getElementsByClassName('tab'),
bodys = document.getElementsByClassName('body');
// 先清除所有current类名,然后在点击的元素上加上,然后封装成函数
function clearClass() {
for( var i=0;i<tabs.length;i++){
tabs[i].className='tab';
bodys[i].className='body'
}
}
//之前都是采用this的属性来操作当前的元素 现在采用闭包的方式
//闭包的方式有很多种下面给大家 说两种方式:
闭包方法一
直接给循环体套一个自执行函数,形成一个私有作用域
for( var i=0;i<tabs.length;i++){
(function(n){
tabs[n].onclick = function(){
//点击tabs 让当前元素有current类名,其他元素没有
clearClass();
this.className='tab current';
// 让body跟着改变,让对应索引的body添加current类名,其他的移除即可
bodys[n].className='body current'
}
})(i)
}
闭包方法二
思想 就是把for循环体的东西分离
function fn(n){
return function(){
//点击tabs 让当前元素有current类名,其他元素没有
// 先清除所有current类名,然后在点击的元素上加上,随之封装函数
clearClass();
this.className='tab current';
// 让body跟着改变,让对应索引的body添加current类名,其他的移除即可
bodys[n].className='body current'
}
}
for( var i=0;i<tabs.length;i++){
tabs[i].onclick = fn(i)
}