前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入浅出JavaScript之闭包(Closure)

深入浅出JavaScript之闭包(Closure)

作者头像
牧云云
发布2022-03-11 10:16:09
3350
发布2022-03-11 10:16:09
举报
文章被收录于专栏:云瓣

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

闭包-无处不在

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

代码语言:javascript
复制
!function() {      
  var localData = "localData here";    
     document.addEventListener('click',    //处理点击事件时用到了外部局部变量,比如这里的localData       
        function(){              
           console.log(localData); 
    }); 
}(); 

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

代码语言:javascript
复制
!function() {      
  var localData = "localData here";      
  var url = "http://www.baidu.com/";      
  $.ajax({ 
     url : url,          
     success : function() {              
        // do sth...              
        console.log(localData); 
        } 
    }); 
}(); 

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

代码语言:javascript
复制
function outer() {   
  var localVal = 30;    
  return function(){      
    return localVal;    
  } 
} 
var func = outer();  
func(); // 30

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

闭包的概念

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

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

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

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

闭包的用途

这部分转自这篇博文

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

代码语言:javascript
复制
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,可以在函数外部对函数内部的局部变量进行操作。

闭包-封装
代码语言:javascript
复制
(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

代码语言:javascript
复制
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了

代码语言:javascript
复制
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的值传到匿名函数里面
} 
思考题

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

代码片段一。

代码语言:javascript
复制
  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());

代码片段二。

代码语言:javascript
复制
  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()());
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-10-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 闭包-无处不在
  • 闭包的概念
  • 闭包的用途
  • 闭包-封装
  • 常见错误之循环闭包
  • 思考题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档