闭包(closure)是掌握Javascript从人门到深入一个非常重要的门槛,它是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。下面写下我的学习笔记~

闭包-无处不在

在前端编程中,使用闭包是非常常见的,我们经常有意无意,直接或间接用到了闭包。闭包可以使传递数据更加灵活(比如处理一些点击事件)

1
2
3
4
5
6
7
!function() {     
  var localData = "localData here";   
     document.addEventListener('click',    //处理点击事件时用到了外部局部变量,比如这里的localData      
        function(){             
           console.log(localData);
    });
}();

又比如下面这个例子:(是不是很亲切~~)

1
2
3
4
5
6
7
8
9
10
11
!function() {     
  var localData = "localData here";     
  var url = "http://www.baidu.com/";     
  $.ajax({
     url : url,         
     success : function() {             
        // do sth...             
        console.log(localData);
        }
    });
}(); 

再来看一个例子~~这种情况就是我们通常所说的闭包

1
2
3
4
5
6
7
8
function outer() {  
  var localVal = 30;   
  return function(){     
    return localVal;   
  }
}
var func = outer(); 
func(); // 30

这个例子中调用outer()返回匿名函数function(),这个匿名函数中可以访问outer()的局部变量localVal,在outer()调用结束后,再次调用func()的时候,仍然能访问到outer()的局部变量localVal

闭包的概念

闭包,不同于一般的函数,它允许一个函数在立即词法作用域外调用时,仍可访问非本地变量。 --维基百科

闭包就是能够读取其他函数内部变量的函数。 --阮一峰

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

闭包的用途

这部分转自这篇博文

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

1
2
3
4
5
6
7
8
9
10
11
12
function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

闭包-封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(function() {  
   var _userId = 23492;  
   var _typeId = 'item';   
   var export = {};
     
   function converter(userId) {         
     return +userId;
   }
    export.getUserId = function() {        
       return converter(_userId);    
   }
   export.getTypeId = function() {         
      return _typeId;
   }        
   window.export = export;   //通过此方式输出
}());
 
  export.getUserId(); // 23492
  export.getTypeId();  // item
  export._userId;    // undefined 
  export._typeId;    // undefined      
  export.converter; // undefined

利用闭包的特性能让我们封装一些复杂的函数逻辑,在这个例子中调用export上的方法(getUserId,getTypeId)间接访问函数里私有变量,但是直接调用export._userId是没法拿到_userId的。这也是Node里面常用到特性吧~

常见错误之循环闭包

下面这个案例,我们添加3个div,值分别为aaa,bbb,ccc,我们想实现的是点击aaa输出1,点击bbb输出2,点击ccc输出3

1
2
3
4
5
6
7
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div><div id=div3>ccc</div>"
for (var i = 1; i < 4; i++) {     
  document.getElementById('div' + i).        
    addEventListener('click', function() {        
    alert(i); // all are 4!
    }); 
}

结果点击aaa,bbb还是ccc都是alert(4)~~

产生这样的问题在于这个i的值在初始化完成的时候就已经是4了

要达到我们想要的点击aaa输出1,点击bbb输出2,点击ccc输出3,要用到闭包的技巧,在每次循环的时候,用立即执行的匿名函数把它包装起来,这样子做的话,每次alert(i)的值就取自闭包环境中的i,这个i来自每次循环的赋值i就能输出1,2,3了

1
2
3
4
5
6
7
8
9
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div>" + "<div id=div3>ccc</div>"
for (var i = 1; i < 4; i++) {
  !function(i){ //②再用这个参数i,到getElementById()中引用    
    document.getElementById('div' + i).      
      addEventListener('click', function() {        
      alert(i); // 1,2,3
     }); 
  }(i);  //①把遍历的1,2,3的值传到匿名函数里面

思考题

如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。(来自阮老师)这题目总结得真秒~~

代码片段一。

1
2
3
4
5
6
7
8
9
10
var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc : function(){
    return function(){
      return this.name;
    };
  }
};
alert(object.getNameFunc()());

代码片段二。

1
2
3
4
5
6
7
8
9
10
11
var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc : function(){
    var that = this;
    return function(){
      return that.name;
    };
  }
};
alert(object.getNameFunc()());

【转】理解JavaScript之闭包的更多相关文章

  1. 深入理解JavaScript的闭包特性如何给循环中的对象添加事件

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  2. 深入理解javascript的闭包

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域 ...

  3. 如何给循环中的对象添加事件--深入理解JavaScript的闭包特性

    初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...

  4. 理解Javascript 的闭包(closure)

    要理解闭包的概念先从变量的作用域说去 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之 ...

  5. 深入理解JavaScript的闭包特性 如何给循环中的对象添加事件(转载)

    原文参考:http://blog.csdn.net/gaoshanwudi/article/details/7355794 初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数 ...

  6. 理解javascript的闭包,原型,和匿名函数及IIFE

    理解javascript的闭包,原型,和匿名函数(自己总结) 一 .>关于闭包 理解闭包 需要的知识1.变量的作用域 例1: var n =99; //建立函数外的全局变量 function r ...

  7. 简单理解javascript的闭包

    看过网上关于javascript的闭包的概念和分析,看完之后都是一头雾水,完全不懂,零度我本来就对于概念性的东西很烦躁,没办法,硬着头皮翻阅了很多的资料,总算理清了一点头绪,现在分享给大家,错误之处还 ...

  8. 理解 Javascript 的闭包

    什么是闭包 闭包是什么?闭包是Closure,这是静态语言所不具有的一个新特性.但是闭包也不是什么复杂到不可理解的东西,简而言之,闭包就是: 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会 ...

  9. 理解JavaScript的闭包

    在JS这块,免不了被问什么是闭包. 从一个常见的循环问题说起. 有一个ul列表, 里面有5个li标签,我希望点击每个li标签的时候,弹出每个li标签对应的索引值(第一个弹出0,第二个弹出1...). ...

随机推荐

  1. 小白日记48:kali渗透测试之Web渗透-XSS(二)-漏洞利用-键盘记录器,xsser

    XSS 原则上:只要XSS漏洞存在,可以编写任何功能的js脚本 [反射型漏洞利用] 键盘记录器:被记录下的数据会发送到攻击者指定的URL地址上 服务器:kali 客户端 启动apache2服务:ser ...

  2. python3.x爬取美团信息

    在之前的文章中,笔者有提到,我们要在实践中去学习python,笔者有天就想着要不要爬点东西呢,跃跃欲试的节奏啊,想来想去,想到美团了,那么首先笔 者想给自己确定一个目标,就是我要爬什么样的数据,我要爬 ...

  3. python获取每颗cpu使用率

    以下是关于python来获取服务器每颗CPU使用率的使用脚本. #!/usr/bin/env python # -*- coding: utf-8 -*- import re,time def _re ...

  4. NSTimer定时器的用法

    #import "ViewController.h" @interface ViewController () { NSTimer *countDownTimer; int cou ...

  5. 安装 vsftpd

    (1)安装vsftpdsudo apt-get install vsftpd (2)配置sudo vi /etc/vsftpd.conf #anonymous_enable=YESlocal_enab ...

  6. Java Script基础(七) HTML DOM模型

    一.HTML DOM. HTML DOM的特性和方法是专门针对HTML的,HTML中的每个节点都是一个对象,通过访问属性和方法的方式,让一些DOM操作更加简便,在HTML DOM中有专门用来处理白哦个 ...

  7. 在Firefox中通过AJAX跨域访问Web资源---

    一.解决在firefox中无法跨域访问的问题 AJAX从本质上讲就是命名用XMLHttpRequest组件来向服务端发送HTTP请求,请接收相应信息.至于成功接收到响应信息后的操作,就和普通的Web客 ...

  8. 新浪SAE部署django博客

    步骤: 第一步:注册新浪SAE账号(即新浪微博),下载TortoiseSVN 第二步:部署代码 使用SAE来部署代码,SAE提供的是PAAS层的云服务,即不是给你一个虚拟主机而是直接上传应用.进入SA ...

  9. Svn 的 Update 与Maven 的update project 作用有什么区别

    1.svn update只是更新项目的SVN仓库.2.maven update project则是进行一系列的操作(跟自己的配置有关,可能包含一下操作):更新SVN.更新项目关联.编译项目.构建发布等 ...

  10. ionic中input框禁止输入问题

    其实这个问题在之后沥青思路之后觉得还是挺好实现的,没有思路的时候真是找不到头绪~ 功能的描述为:当输入框中没有内容时,允许用户编辑:当其中有内容时不允许用户编辑,只有当用户点击编辑按钮后,才可允许编辑 ...