【转】理解JavaScript之闭包

闭包(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()());

时间: 2024-12-10 09:29:12

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

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

初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript的闭包特性. 有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4. <!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> &

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

深入理解JavaScript的闭包特性如何给循环中的对象添加事件 初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript的闭包特性. 有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4. 1.  <!DOCTYPE HTML> 2.  <html> 3.  <head&g

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

初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript的闭包特性. 有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4. 1.  <!DOCTYPE HTML> 2.  <html> 3.  <head> 4.  <meta charset="utf

深入理解javascript之闭包

理解闭包 闭包是静态语言不具有的一个特性,闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在.闭包就是函数的堆栈在函数返回后并不释放,可以理解为这些函数并不在栈上分配而是在堆上分配. 这里需要讲一下堆栈,堆与栈都是编程语言用来在RAM中存放数据的地方.栈的优势就是存取速度比堆要快,仅次于直接位于CPU中的寄存器,但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性.堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,垃圾收集器会自动地收走这些不再使用的数据,但是

理解Javascript的闭包

前言:还是一篇入门文章.Javascript中有几个非常重要的语言特性——对象.原型继承.闭包.其中闭包 对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性.本文将以例子入手来介绍Javascript闭包的语言特性,并结合一点 ECMAScript语言规范来使读者可以更深入的理解闭包. 注:本文是入门文章,例子素材整理于网络,如果你是高手,欢迎针对文章提出技术性建议和意见.本文讨论的是Javascript,不想做语言对比,如果您对Javascript天生不适,请自行绕道. 什么是闭

【深入理解javascript】闭包

1.作用域 "javascript没有块级作用域".所谓"块",就是大括号"{}"中间的语句.例如if语句: 再比如for语句: 所以,我们在编写代码的时候,不要在"块"里面声明变量,要在代码的一开始就声明好了.以避免发生歧义.如: 在声明变量时,全局代码要在代码前端声明,函数中要在函数体一开始就声明好.除了这两个地方,其他地方都不要出现变量声明. javascript除了全局作用域之外,只有函数可以创建作用域. 作用域最大的

深入理解javascript闭包

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function

深入理解JavaScript闭包(closure)

最近在网上查阅了不少Javascript闭包(closure)相关的资料,写的大多是非常的学术和专业.对于初学者来说别说理解闭包了,就连文字叙述都很难看懂.撰写此文的目的就是用最通俗的文字揭开Javascript闭包的真实面目. 一.什么是闭包? “官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话通俗的来说就是:JavaScript中所有的function都是一个

javascript中闭包的工作原理

一.什么是闭包? 官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话通俗的来说就是:JavaScript中所有的function都是一个闭包.不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”.看下面这段代码: 1 2 3 4 5 6 7 function a() { var i = 0; function b() { ale