【3分钟带你学】JS闭包
一、什么是闭包?
var n=99;
function f1(){
console.log(n);
}
f1(); //99
JavaScript中有两种作用域,全局作用域和局部作用域,函数内部可以直接读取全局变量。函数 f1 可以读取全局变量 n。但是,在函数外部无法读取函数内部声明的变量。
function f1() {
var n = 99;
}
f1( );
console.log(n); //undefined
但是,有时我们却需要在函数外部访问函数内部的变量,正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部, 再定义一个函数。
function f1() {
var n = 99;
var f2 = function() {
console.log(n);
}
return f2;
}
var f = f1();
f();
上面代码中, 函数f2就在函数f1内部, 这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行, f2内部的局部变量,对f1就是不可见的。
这就是JavaScript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。既然f2可以读取f1的局部变量, 那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
闭包就是函数f2, 即能够读取其他函数内部变量的函数。
由于在JavaScript语言中, 只有函数内部的子函数才能读取内部变量, 因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点, 就是它可以“记住”诞生的环境, 比如f2记住了它诞生的环境f1, 所以从f2可以得到f1的内部变量。
在本质上, 闭包就是将函数内部和函数外部连接起来的一座桥梁;
二、垃圾回收机制(GC)及闭包
先看一个demo
function f1( ) {
var n = 99;
console.log(++n);
}
f1( ); //100
f1( ); //100
当我们在函数内部引入一个变量或函数时,系统都会开辟一块内存空间;
还会将这块内存的引用计数器进行初始化,初始化值为0;
如果外部有全局变量或程序引用了这块空间,则引用计数器会自动进行+1操作;
当函数执行完毕后,变量计数器重新归零,系统会运行垃圾回收机制,将函数运行产生的数据销毁;
如计数器不是 0 ,则不会清除数据;
这个过程就称之为 “JS的垃圾回收机制” ;
但是,如果将代码改为闭包形式:
function f1() {
var n = 99;
function f2(){
console.log(++n);
}
return f2;
}
var f = f1();
f(); //100
f(); //101
运行代码发现,函数调用一次,其变量 n 变化一次;因函数f1被调用时,返回的结果是f2函数体,也就是说,f2函数被当作值返回给f1的调用者,但是f2函数并没有在此时被调用执行,所以整个 f1 函数体,无法判断子函数f2会对其产生何种影响,无法判断变量n是否会被使用;即使f1函数被调用结束,整个f1函数始终保留在内存中不会被垃圾回收机制回收;
三、闭包的作用
闭包的最大用处有2个,
一个是可以读取函数内部的变量,
另一个就是让这些变量始终保持在内存中.
此外,还保护变量的安全,因为函数内的变量只能通过函数内部访问,其他途径不能访问即闭包可以使得它诞生环境一直存在;
注意, 外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包, 否则会造成网页的性能问题。
下附一个实际应用案例 tab切换
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style type="text/css">
* {
padding-bottom: 0px;
margin: 0px;
padding-left: 0px;
padding-right: 0px;
font-size: 12px;
padding-top: 0px;
}
BODY {
padding-left: 20px;
padding-top: 20px;
}
.wid240 {
width: 242px;
margin-bottom: 20px;
}
.wid180 {
width: 182px;
}
.tab {
border-bottom: #000 1px solid;
border-left: #000 1px solid;
border-top: #000 1px solid;
border-right: #000 1px solid;
}
.tab UL {
zoom: 1;
clear: both;
}
.tab UL:after {
display: block;
height: 0px;
visibility: hidden;
clear: both;
content: "";
}
.tab UL LI {
text-align: center;
line-height: 26px;
width: 60px;
display: inline;
background: #000;
float: left;
height: 26px;
color: #fff;
}
.tab UL LI.on {
background: #fff;
color: #000;
}
.tabList {
border-bottom: #000 1px solid;
border-left: #000 1px solid;
height: 150px;
border-top: #000 1px;
border-right: #000 1px solid;
}
.tabList .one {
padding-bottom: 10px;
padding-left: 10px;
padding-right: 10px;
display: none;
color: #ff0000;
padding-top: 10px;
}
.tabList .block {
display: block;
}
</style>
<meta name="GENERATOR" content="MSHTML 8.00.7600.16535">
</head>
<body>
<div class="wid240">
<div class="tab">
<ul>
<li id="one1" class="on">one1 </li>
<li id="one2">one2 </li>
<li id="one3">one3 </li>
<li id="one4">one4 </li>
</ul>
</div>
<div class="tabList">
<div id="cont_one_1" class="one block"> cont_one_1</div>
<div id="cont_one_2" class="one"> cont_one_2</div>
<div id="cont_one_3" class="one"> cont_one_3</div>
<div id="cont_one_4" class="one"> cont_one_4</div>
</div>
</div>
</div>
</body>
<script type="text/javascript">
//获取li标签
var lis = document.getElementsByTagName('li');
//绑定悬浮事件
for(var i=0;i<lis.length;i++) {
lis[i].onmouseover = (function(index){
return function() {
//切换div.one
var num = parseInt(index+1);
var div = document.getElementById('cont_one_'+num);
//判断
var divs = document.querySelectorAll('.one');
for(var i=0;i<divs.length;i++) {
divs[i].style.display = 'none';
}
div.style.display = 'block';
}
})(i);
}
</script>
</html>