当javaScript从入门到提高前需要注意的细节:闭包部分

简介:

对于希望在javascript技术中提高的人群来说,闭包肯定时常是一个令人感觉神秘的技术。早先有人说javaScript中的闭包可能会引发javaScript内存管理的复杂度,也许会出现内存泄露,所以不建议用闭包。不过jQuery很好的证明了闭包非常好用,C#的Linq也证明的闭包技术的重要性,所以花一点点时间来理解下闭包还是很值得的,再说了,以下的内容不过就是一杯茶的时间而已。

先给出一个闭包的定义:在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。

以上定义中非常重要的是

闭包关联到:函数和变量

闭包锁定了:函数和变量的环境关系


 

常有人在说闭包前会给出类似以下案例,说是javaScript的特殊性,函数中访问全局变量,这个我一直不太明白,一个函数访问它所在环境中的全局变量很特殊很奇怪吗?

 
  1. var a = 10;  
  2. function fun1() {  
  3.     alert(a);  
  4. }  
  5. fun1(); 

而且就算有以下代码我也丝毫不感觉有任何问题

 
  1. var a = 10;  
  2. function fun2() {  
  3.     b = 100;// 全局变量  
  4. }  
  5. function fun1() {  
  6.     alert(a);  
  7.     alert(b);  
  8. }  
  9. fun2();  
  10. fun1(); 

真正值得你考量的是以下代码

 
  1. function fun1(x) {  
  2.     var a = x;  
  3.     function fun2() {  
  4.         return a;  
  5.     }  
  6.     return fun2;  
  7. }  
  8. alert(fun1(100)()); //100  
  9. alert(fun1(9)()); //9 

如果从结果来看,你也许同样不屑一顾,返回的值很明确的嘛。但是你仔细想想,问题就不简单了

 

函数fun1的结果是返回了一个fun2的函数。从代码来讲,fun1()是调用了fun1的执行,并且得到了fun2的函数,fun1()()就是说是执行fun2()了!如果你还是感觉正常的话,要么说明你已经理解闭包了,要么就是你忽略了一个重要的事实!

调用fun1()后,fun1所占用的内存应该已经释放了,fun1函数中的所有变量都将释放!!!对不?

 
  1. function sum(x, y) {  
  2.     var a = x, b = y;  
  3.     return a + b;  
  4. }  
  5. alert(sum(9, 5));//14 

上面的代码,当我完成sum(9,5)之后,sum函数肯定被释放了,a和b将不存在是不?如果你还是感觉不是很明晰的话,那么看下如下的分解动作吧。

 
  1. function fun1(x) {  
  2.     var a = x;  
  3.     function fun2() {  
  4.         return a;  
  5.     }  
  6.     return fun2;  
  7. }  
  8. var fun3 = fun1(10);  
  9. var fun4 = fun1(9);  
  10. alert(fun4()); //9  
  11. alert(fun3()); //10 

看看第一次的fun1将x的值赋值了10,第二次的fun1将x的值赋值为9,而且我们先执行了fun4,要求返还的值是9,再次执行fun3的得到的是10!!!说明什么呢?说明当fun2被创建的时候,它将它所需要用到的变量锁住 了,或者不这么夸张的说是记忆住了。

 

闭包就是函数在创建自己的时候,将需要用的变量锁住或说记忆住。

这里的函数创建不是指函数声明,而且指函数表达式被激活的时候,匿名函数表达式的激活有:call就是()调用,()分组,还有就是return的时候。

看看如下案例

 
  1. var dofun = [];  
  2.  
  3. for (var i = 0; i < 10; i++) {  
  4.     dofun[i] = function() {  
  5.         return i;  
  6.     }  
  7.  
  8. }  
  9.  
  10. for (var j = 0; j < 10; j++) {  
  11.     alert(dofun[j]()); //全部返还10  

很多人在网上用过这个案例来说明闭包的特性,我需要很严肃的声明两个问题

 

1 这个案例可以说明闭包

2 这个案例还有更神奇的特性说明

先说下闭包,因为当这个返回的匿名函数只有在

 
  1. dofun[j]() 

的时候才被激活,这个时候i的值是10,所以这个函数被返回了10,要解决这个问题,可以要求函数在i是各种值的时候被激活,怎么激活?return

 
  1. var dofun = [];  
  2.  
  3. for (var i = 0; i < 10; i++) {  
  4.     dofun[i] = (function(k) {  
  5.         return function() {  
  6.             return k;  
  7.         }  
  8.     }(i));  
  9.  
  10. }  
  11.  
  12. for (var j = 0; j < 10; j++) {  
  13.     alert(dofun[j]()); //全部返还10  

这个处理中很有意思的是,我需要一个k来帮忙,为什么呢?

那就是我刚才说的第2点,先看下如下很令人无语的代码

 
  1. var dofun = [];  
  2.  
  3. for (var i = 0; i < 10; i++) {  
  4.     dofun[i] = function() {  
  5.         return i;  
  6.     }  
  7.  
  8. }  
  9.  
  10. for (var i = 0; i < 10; i++) {  
  11.     alert(dofun[i]());  

咋一看,你估计会暴跳起来,这不是耍人嘛,结果我们已经知道了,都是10!!!!错!!!!!!弹出的分别是0 1 2 3 4 5 6 7 8 9!!!

 

不相信你就测试一下看看。你仔细看看代码不同在哪里?下面的for用了变量是i,这就是不同所在,也是闭包的关键所在!

函数是不是值得来锁定一个变量,是看该变量在调用这个函数的时候,是不是能在上下文作用域中找到这个变量,如果无法在调用时找到这个变量,内部函数就会锁住它,否则就不会锁住,至少表面上是这样的。

现在我们再把目光移到最初的那个案例

 
  1. function fun1(x) {  
  2.     a = x;  
  3.     function fun2() {  
  4.         return a;  
  5.     }  
  6.     return fun2;  
  7. }  
  8.  
  9.  
  10. var fun3 = fun1(10);  
  11. var fun4 = fun1(9);  
  12. alert(fun4()); //9  
  13. a = 999;  
  14. alert(fun3()); //999 

fun3()的结果是999的原因倒不是在乎现在a是全局变量,而是现在fun3在执行的时候能在上下文作用域中找到a了,全局变量不过是凑齐罢了。



本文转自shyleoking 51CTO博客,原文链接:http://blog.51cto.com/shyleoking/803076

相关文章
|
前端开发 机器人 API
前端大模型入门(一):用 js+langchain 构建基于 LLM 的应用
本文介绍了大语言模型(LLM)的HTTP API流式调用机制及其在前端的实现方法。通过流式调用,服务器可以逐步发送生成的文本内容,前端则实时处理并展示这些数据块,从而提升用户体验和实时性。文章详细讲解了如何使用`fetch`发起流式请求、处理响应流数据、逐步更新界面、处理中断和错误,以及优化用户交互。流式调用特别适用于聊天机器人、搜索建议等应用场景,能够显著减少用户的等待时间,增强交互性。
3748 2
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
|
10月前
|
前端开发 JavaScript Java
JavaScript闭包深入剖析:性能剖析与优化技巧
JavaScript 闭包是强大而灵活的特性,广泛应用于数据封装、函数柯里化和事件处理等场景。闭包通过保存外部作用域的变量,实现了私有变量和方法的创建,提升了代码的安全性和可维护性。然而,闭包也可能带来性能问题,如内存泄漏和执行效率下降。为优化闭包性能,建议采取以下策略:及时解除对不再使用的闭包变量的引用,减少闭包的创建次数,使用 WeakMap 管理弱引用,以及优化闭包结构以减少作用域链查找的开销。在实际开发中,无论是 Web 前端还是 Node.js 后端,这些优化措施都能显著提升程序的性能和稳定性。
241 70
|
8月前
|
存储 JavaScript 前端开发
|
10月前
|
自然语言处理 JavaScript 前端开发
当面试官再问我JS闭包时,我能答出来的都在这里了。
闭包(Closure)是前端面试中的高频考点,广泛应用于函数式编程中。它不仅指函数内部定义的函数,还涉及内存管理、作用域链和垃圾回收机制。闭包可以让函数访问其外部作用域的变量,但也可能引发内存泄漏等问题。通过合理使用闭包,可以实现模块化、高阶函数和回调函数等应用场景。然而,滥用闭包可能导致代码复杂度增加、调试困难以及潜在的性能问题。为了避免这些问题,开发时应谨慎处理闭包,避免不必要的嵌套,并及时清理不再使用的变量和监听器。
415 16
当面试官再问我JS闭包时,我能答出来的都在这里了。
|
9月前
|
JSON JavaScript 前端开发
菜鸟之路Day23一一JavaScript 入门
本文介绍了 JavaScript 的基础内容,包括 JS 的引入方式、基础语法、数据类型、运算符、类型转换、函数、对象(如 Array、String、自定义对象、JSON、BOM 和 DOM)、事件监听,以及 Vue 框架的初步使用。重点讲解了内部和外部脚本的引入、变量声明(var、let、const)、常见输出语句、数组与字符串的操作方法、DOM 操作及事件绑定,并通过实例展示了 Vue 的双向数据绑定和常用指令(v-bind、v-model、v-on、v-if、v-for 等)。
228 7
|
9月前
|
缓存 自然语言处理 JavaScript
JavaScript中闭包详解+举例,闭包的各种实践场景:高级技巧与实用指南
闭包是JavaScript中不可或缺的部分,它不仅可以增强代码的可维护性,还能在模块化、回调处理等场景中发挥巨大作用。然而,闭包的强大也意味着需要谨慎使用,避免潜在的性能问题和内存泄漏。通过对闭包原理的深入理解以及在实际项目中的灵活应用,你将能够更加高效地编写出简洁且功能强大的代码。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
JavaScript 前端开发
js 闭包的优点和缺点
【10月更文挑战第27天】JavaScript闭包是一把双刃剑,在合理使用的情况下,它可以带来很多好处,如实现数据封装、记忆功能和模块化等;但如果不注意其缺点,如内存泄漏、变量共享和性能开销等问题,可能会导致代码出现难以调试的错误和性能问题。因此,在使用闭包时,需要谨慎权衡其优缺点,根据具体的应用场景合理地运用闭包。
335 58
|
12月前
|
JavaScript 前端开发
【JavaScript】——JS基础入门常见操作(大量举例)
JS引入方式,JS基础语法,JS增删查改,JS函数,JS对象
|
监控 前端开发 JavaScript
React 静态网站生成工具 Next.js 入门指南
【10月更文挑战第20天】Next.js 是一个基于 React 的服务器端渲染框架,由 Vercel 开发。本文从基础概念出发,逐步探讨 Next.js 的常见问题、易错点及解决方法,并通过具体代码示例进行说明,帮助开发者快速构建高性能的 Web 应用。
584 10

热门文章

最新文章