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

深入浅出JavaScript之闭包(Closure)

作者头像
牧云云
发布于 2022-03-11 02:16:09
发布于 2022-03-11 02:16:09
35400
代码可运行
举报
文章被收录于专栏:云瓣云瓣
运行总次数:0
代码可运行

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

闭包-无处不在

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

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

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function outer() {   
  var localVal = 30;    
  return function(){      
    return localVal;    
  } 
} 
var func = outer();  
func(); // 30

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

闭包的概念

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

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

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

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

闭包的用途

这部分转自这篇博文

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
(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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());

代码片段二。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
JavaScript:string
JavaScript 的字符串是不可变的(immutable),String 类定义的方法都不能改变字符串的内容,返回的是全新的字符串,而不是修改原始字符串。
奋飛
2019/08/15
7320
JavaScript正则表达式下——相关方法
http://www.cnblogs.com/dolphinX/p/3486136.html
bear_fish
2018/09/19
4560
JavaScript正则表达式下——相关方法
JavaScript正则表达式
什么是正则表达式? 正则表达式也叫做匹配模式(Pattern),它由一组具有特定含义的字符串组成,通常用于匹配和替换文本。 在JavaScript脚本中,利用正则表达式可以很容易的实现文本字符串的检测、替换等功能。 正则表达式是字符串,它定义了一个用来搜索匹配字符串的模式。定义模式:/表达式/ JavaScript脚本语言中引入正则表达式主要作用: 验证字符串格式 查找字符串 替换文本 创建方式: 1、采用RegExp对象的显式构造函数构造 var regObj = ne
汤高
2018/01/11
2.6K0
JavaScript正则表达式
JavaScript正则表达式入门知识详细介绍
正则表达式,在各种语言(JS、Java、Php等)里面都是很常见的,而且语法都有相似之处。作为新手,第一次接触正则,可能一脸茫然,这是什么东西,语法这么奇怪。其实它的语法是有迹可循的,而且基本是规定的语法模式,只要掌握它的语法,你也可以写出属于你自己的正则表达式。
Javanx
2019/09/04
7620
JavaScript正则表达式入门知识详细介绍
Js中String对象
创建一个字符串可以通过字面量的方式,通过字面量创建的字符串变量在调用方法的时候能够自动转化为临时的包装对象,从而能够调用其构造函数的原型中的方法,也可以利用String对象生成字符串对象,此外在ES6标准还定义了模板字面量用以生成字符串的方式。
WindRunnerMax
2020/09/18
8.1K0
js正则表达式语法大全_JavaScript正则
​ 返回一个数组 [匹配内容,index:匹配的起始位置,input:要匹配的字符串, group:undefined]
全栈程序员站长
2022/11/08
3.8K0
JavaScript 正则表达式入门教程
正则表达式是描述一组字符串特征的模式,用来匹配特定的字符串 主要分三个部分:基本语法、RegExp对象的方法、JS中支持正则表达式的String对象方法 一、基本语法 在JS中,正则表达式为对象,用如下两种方式定义: 直接量法: /pattern/attributes;创建RegExp对象法:new RegExp(pattern,attributes); var reg=/hi/i;//字面量 var reg=new RegExp('hi','i');//构造函数(参数1:最简单的正则匹配字母hi;参数2:
小古哥
2018/03/08
1.4K0
「思维导图学前端 」初中级前端值得收藏的正则表达式知识点扫盲
本文是思维导图学前端系列第二篇,主题是正则表达式。首先还是想说下我的出发点,之所以自己画一遍思维导图,是因为我整理的思维导图里加入了自己的理解,更容易记忆。之前也看过很多别人整理的思维导图,虽然有点拨之用,但是要想吸收个二三分营养却也是很难。所以,建议本系列的读者在阅读文章之后,在时间允许的情况下,可以考虑自行整理知识点,便于更好地理解和吸收。
程序员白彬
2020/07/16
4940
JavaScript 正则表达式全面总结
正则表达式是用于匹配字符串中字符组合的模式。正则表达式的模式规则是由一个字符序列组成的。包括所有字母和数字在内,大多数的字符都是直接按照直接量描述待匹配的字符。除此之外,正则表达式还有其他特殊语义的字符,这些字符不按照特殊含义进行匹配。
全栈程序员站长
2022/09/07
1.1K0
正则表达式-JavaScript
正则表达式-JavaScript 什么是正则表达式 正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象。 这些模式被用于 RegExp 的 exec 和 test 方法, 以及 String 的 match、replace、search 和 split 方法。 正则表达式存在于大部分的编程语言,就算是在写shell时也会不经意的用到正则。 比如大家最喜欢的rm -rf ./*,这里边的*就是正则的通配符,匹配任意字符。 在JavaScr
贾顺名
2018/04/12
1.3K0
正则表达式-JavaScript
javascript 操作dom
Node往往被翻译为节点,在一个对象(可以简单的理解为是HTML页面中),一个属性name="aaa"可以是一个节点,一个< div id="aaa">……< /div>也可以是一个节点,在< body>……< /body>中的,也是一个大大的节点。下面是一些有关Node的属性和方法,并没有包含部分不兼容IE和FF内容的讲解。
jack.yang
2025/04/05
640
JavaScript 包含某个字符串
JavaScript 判断子串方法。 String 对象方法 indexOf() var str = "123"; console.log(str.indexOf("3") != -1); // true console.log(str.search("3") != -1); // true console.log(str.match(reg));// true 方法返回指定字符串首次出现的位置,如果未找到,则返回 -1 。 方法用来检索字符串中指定的子串,或检索与正则表达式相配置的字符串,如果未找到配置
zucchiniy
2019/10/30
1.4K0
JavaScript 学习-21.正则表达式 RegExp 对象
前言 RegExp:是正则表达式(regular expression)的简写。RegExp 对象用于规定在文本中检索的内容。 创建 RexExp 对象 创建正则表达式有两种方式: 第一种:使用字面量创建 RegExp 对象的语法: var p = /pattern/attributes; 第二种:使用 new 创建RegExp对象的语法: var p = new RegExp(pattern, attributes); 参数释义: 1.参数pattern是一个字符串,指定了正则表达式的模式或其他正则表达式
上海-悠悠
2022/05/25
5720
前端架构师之12_JavaScript正则表达式
正则表达式(Regular Expression,简称regexp)是一种描述字符串结构的语法规则。
张哥编程
2024/12/13
1270
JavaScript 28个常用字符串方法及使用技巧
今天再来看一些JavaScript基础知识,基础太重要了。还清楚的记得,今年春招的时候,某大厂面试官狠狠的嘲讽我 JavaScript 的API都记不住🤣太尴尬了,主要还是用的太少了,所以平时还是要多用多积累。今天我们就来看看JavaScript中有哪些常用的字符串方法!文章内容较多,建议先收藏再学习! 1. 获取字符串长度 JavaScript中的字符串有一个length属性,该属性可以用来获取字符串的长度: const str = 'hello'; str.length // 输出结果:5 复
玖柒的小窝
2021/09/24
3K0
JavaScript 28个常用字符串方法及使用技巧
正则表达式学习笔记
正则表达式 1. 使用正则 创建正则表达式有两种方式,一种是以字面量方式创建,另一种是使用RegExp构造函数来创建。 var expression = / pattern / flags; var expression = new RegExp( pattern / flags ); var expression = new RegExp( patternStr, flags ); // example var regexp = /regexp/g; var regexp = new RegExp('r
糊糊糊糊糊了
2018/05/09
1.4K0
代码之美,正则之道
导语 “如果罗列计算机软件领域的伟大发明,我相信绝对不会超过二十项,在这个名单当中,当然应该包括分组交换网络,Web,Lisp,哈希算法,UNIX,编译技术,关系模型,面向对象,XML这些大名鼎鼎的家伙,而正则表达式也绝对不应该被漏掉。”-- Jeffrey Friedl《精通正则表达式》序言 从1956年至今,正则表达式活跃了半个多世纪,其热度依然不减,可见技术半衰期之长,因此,学习正则,不但重要,且受益漫长。 本文涉及 js、php、java、python、bash 等语言,共计 1.2w 字,适
腾讯技术工程官方号
2019/07/31
1.3K0
代码之美,正则之道
正则表达式理论篇
学习正则表达式的你们,有没有发现,一开始总是记不住语法。嗯,加深大家的印象的同时,我也是来找同道中人的。
陈大鱼头
2021/10/09
1.3K0
正则表达式理论篇
1、正则表达式
前端往往有大量的表单数据校验的工作,采用正则表达式会使得数据校验的工作量大大减轻,如邮箱验证,手机号码,等等。比起用字符串的函数来判断简单,易用。
Daotin
2022/11/28
5800
分享 18 个JS 字符串操作相关的方法
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。如果要检索的字符串值没有出现,则该方法返回 -1。
前端达人
2021/12/08
8110
相关推荐
JavaScript:string
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档