文章导航

JavaScript 闭包

2018-8-23 00:22| 作者: admin| 查看: 1612| 评论: 1|来自: 蚂蚁部落

闭包是JavaScript中重要的概念之一,英文名称是closure。

虽然使用JavaScript工作多年,但是依然不理解为何closure要翻译成闭包。

绝大多数情况,编程概念名称的翻译通常和它的实际作用相关联,唯独闭包是一个例外,至今也不理解"闭包"这个词与它的实际使用有任何语义上的关联,由此学习过程中走了很多弯路,其实我们完全可以放弃"闭包"这个概念,因为它本质上就是作用域和作用域链的应用,不知道为何非要取名"闭包"。

一.闭包的特点:

(1).函数中嵌套函数。

(2).嵌套的函数可以使用外层函数作用中的变量和参数。

(3).参数和变量不会被垃圾回收机制回收。

通常情况下,函数中声明的局部变量,会在函数执行完毕后被销毁,看如下代码:

[JavaScript] 纯文本查看 复制代码运行代码
function a(){
  var webName="蚂蚁部落";
  console.log(webName);
}
a()

当函数执行完毕之后,函数内部声明的变量就不会再占用内存。

但是闭包就可以使局部变量驻留在内存中,这样就不用使用全局变量,避免出现全局污染情况。

看如下代码实例:

[JavaScript] 纯文本查看 复制代码运行代码
function func() { 
  var num = 1; 
  return function(){
    console.log(num++)
  }; 
}     
var foo=func(); 
foo();//1
foo();//2  
fun = null;//垃圾回收

num变量在fun执行完毕之后并没有被销毁,这是因为全局变量(全局变量会在程序执行完毕销毁)foo存储func返回的匿名函数,而此返回的匿名函数会用到变量num。

二.使用闭包的优点:

(1).局部变量长期驻扎在内存中(其实所有的特点都是由长期驻扎实现的)。

(2).避免全局变量的污染。

(3).私有成员的存在。

下面分别通过代码实例做一下介绍。

三.作用域和作用域链方向理解:

如果看到上面关于闭包解释的理论有点云里雾里,那么完全可以放弃闭包这个概念,因为闭包的实质就是对作用域和作用域链运用的一种方式之一。

关于作用域和作用域链更多内容可以参阅JavaScript 作用域和作用域链详解一章节。

[JavaScript] 纯文本查看 复制代码运行代码
function func() { 
  var num = 1; 
  return function(){
    console.log(num++)
  }; 
}     
var foo=func(); 
foo();//1
foo();//2  
fun = null;//垃圾回收

对以上代码分析如下:

首先说明一点,作用域和作用域链在变量或者函数声明的那一刻就已经被确定。

(1).var foo=func(),func函数返回一个函数对象的引用,将此引用赋值给变量foo,也就是说foo指向的依然是func返回的函数对象,并非克隆了一个新的函数,因为函数对象是引用类型数据,具体参阅JavaScript 引用类型和值类型详解一章节。

(2).foo(),调用此函数,由于作用域和作用域链在声明的那一刻就确定了,所以num变量可以被函数使用。

(3).foo(),foo是全局作用域,它引用着函数对象,而函数内部又在使用num变量,所以num在上次函数执行完毕并不会被销毁,再次执行实现了累加效果。

四.闭包概念总结:

闭包就这么简单,就是一个作用域和作用域链的一种使用方式而已,它实现了函数执行完毕后,变量依然存在于内存没有被销毁的一种现象。

五.代码实例:

[JavaScript] 纯文本查看 复制代码运行代码
var num=1;
function func(){
  num++;
  console.log(num);
}
func();//2
func();//3

由于是全局变量自然就会实现累加效果。

[JavaScript] 纯文本查看 复制代码运行代码
function func(){
  var num=1;
  num++;
  console.log(num);
}
func();//2
func();//2

由于是局部变量,执行一次就会销毁一次,所以不会有累加效果。

下面用闭包实现局部变量累加效果,代码如下:

[JavaScript] 纯文本查看 复制代码运行代码
function func(){
  var num=1;
  return function(){
    num++;
    console.log(num)
  }
}
var foo=func();
foo();//2
foo();//3

代码实现了局部变量累加。又由于num是局部变量,所以不会对函数作用域外的同名变量产生污染。

再来看一个闭包的实际应用代码:

[HTML] 纯文本查看 复制代码运行代码
<!DOCTYPE html>
<html>
<head>
<meta charset=" utf-8">
<meta name="author" content="http://www.softwhy.com/" />
<title>蚂蚁部落</title>
<style>
ul li{
  width:300px;
  list-style-type:none;
  font-size:12px;
  line-height:30px;
  height:30px;
  cursor:pointer;
}
ul li span{
  float:right
}
</style>
<script>
window.onload=function(){
  var obox=document.getElementById("box");
  var lis=obox.getElementsByTagName("li");
  var odiv=document.getElementById("show");
  for(var index=0;index<lis.length;index++){
    lis[index].onclick=(function(index){
      return function(){
        odiv.innerHTML=index;
      } 
    })(index)
  }
}
</script>
</head> 
<body> 
<ul id="box">
  <li>蚂蚁部落一</li>
  <li>蚂蚁部落二</li>
  <li>蚂蚁部落三</li>
  <li>蚂蚁部落四</li>
  <li>蚂蚁部落五</li>
</ul>
<div id="show"></div>
</body> 
</html>

点击li元素可以获取它在集合中的位置。

没必要将闭包理解的过于复杂,它描述了函数执行完毕内存释放后,依然内存驻留的一个现象,只要把握这个核心概念,闭包就不难理解了。

4

鲜花
1

握手

雷人

路过

鸡蛋

刚表态过的朋友 (5 人)

发表评论

最新评论

引用 梅长苏 2018-8-30 19:43
这是关于闭包的最好文章,确实闭包就是作用域链的应用,死扣闭包这个概念可能会爆炸

查看全部评论(1)

返回顶部