闭包是很多语言都具备的特性,上篇《从抽象代数漫游函数式编程(1):闭包概念再Java/PHP/JS中的定义》

闭包的特性

闭包有三个特性:

  • 函数嵌套函数

  • 函数内部可以引用外部的参数和变量

  • 参数和变量不会被垃圾回收机制回收

在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等。

闭包(closure)是Javascript语言特色(函数式编程特色),很多高级应用都要依靠闭包实现。但是JavaScript的一个难点,因为JavaScript这个早产儿先天不足,不想强类型语言那么泾渭分明。引用《ECMAScript进化史(1):话说Web脚本语言王者JavaScript的加冕历史 》的段落:

Javascript其实(简化的)函数式编程+(简化的)面向对象编程,这是由Brendan Eich(函数式编程)与网景公司(面向对象编程)共同决定的。它是C语言和Self语言一夜情的怪胎。'它的优秀之处并非原创,它的原创之处并不优秀。'

总的来说,Brendan Eich的设计思路是这样的:

  1. 借鉴C语言的基本语法;

  2. 借鉴Java语言的数据类型和内存管理;

  3. 借鉴Scheme语言,将函数提升到"第一等公民"(first class)的地位;

  4. 借鉴Self语言,使用基于原型(prototype)的继承机制。

…………

原因一:javascript是一个函数编程语言,怪就怪在它也有this指针,说明这个函数编程语言也是面向对象的语言,说的具体点,javascript里的函数是一个高阶函数,编程语言里的高阶函数是可以作为对象传递的,同时javascript里的函数还有可以作为构造函数,这个构造函数可以创建实例化对象,结果导致方法执行时候this指针的指向会不断发生变化,很难控制。

原因二:javascript里的全局作用域对this指针有很大的影响,由上面java的例子我们看到,this指针只有在使用new操作符后才会生效,但是javascript里的this在没有进行new操作也会生效,这时候this往往会指向全局对象window。

  原因三:javascript里call和apply操作符可以随意改变this指向,这看起来很灵活,但是这种不合常理的做法破坏了我们理解this指针的本意,同时也让写代码时候很难理解this的真正指向

诠释JS闭包函数

在理解闭包以前.最好能先理解一下JavaScript的垃圾回收机制与作用域链的含义,推荐阅读《再谈JavaScript垃圾回收机制:分析与排查JS内存泄露情形》

javascript的垃圾回收原理

  • 引用计数(reference counting):机制就是跟踪一个值的引用次数,当声明一个变量并将一个引用类型赋值给该变量时该值引用次数加1,当这个变量指向其他一个时该值的引用次数便减一。当该值引用次数为0时就会被回收。该方式会引起内存泄漏的原因是它不能解决循环引用的问题: var a={};var b={};a.prop = b;b.prop = a;

  • 标记清除(mark and sweep):大部分浏览器以此方式进行垃圾回收,当变量进入执行环境(函数中声明变量)的时候,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”,在离开环境之后还有的变量则是需要被删除的变量。标记方式不定,可以是某个特殊位的反转或维护一个列表等。

    垃圾收集器给内存中的所有变量都加上标记,然后去掉环境中的变量以及被环境中的变量引用的变量的标记。在此之后再被加上的标记的变量即为需要回收的变量,因为环境中的变量已经无法访问到这些变量。

其实我们只需要记住:

  • 在javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收;

  • 如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

JavaScript作用域链

简单来说,,作用域链就是函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引而他内部的规则是:

  • 把函数自身的本地变量放在最前面,

  • 把自身的父级函数中的变量放在其次

  • 把再高一级函数中的变量放在更后面

  • ……以此类推直至全局对象为止

当函数中需要查询一个变量的值的时候,js解释器会去作用域链去查找。从最前面的本地变量中先找,如果没有找到对应的变量,则到下一级的链上找,一旦找到了变量,则不再继续。如果找到最后也没找到需要的变量,则解释器返回undefined

一般来说,一个函数在执行开始的时候,会给其中定义的变量划分内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了。对应的内存空间也就被回收了。下次再执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用。

但是如果这个函数内部又嵌套了另一个函数,而这个函数是有可能在外部被调用到的。并且这个内部函数又使用了外部函数的某些变量的话。这种内存回收机制就会出现问题:如果在外部函数返回后,又直接调用了内部函数,那么内部函数就无法读取到他所需要的外部函数中变量的值了。所以JavaScript解释器在遇到函数定义的时候,会自动把函数和他可能使用的变量(包括本地变量和父级和祖先级函数的变量(自由变量))一起保存起来。也就是构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不可能被调用以后(例如被删除了,或者没有了指针),才会销毁这个闭包,而没有任何一个闭包引用的变量才会被下一次内存回收启动时所回收

也就是说,有了闭包,嵌套的函数结构才可以运作,这也是符合我们的预期的.

在生活上,我们去看中共政办事,找A办事,你还先得找B门盖个章,B说,你先得找C盖个章,C说,这个东西不是我们的职权范围…… 踢皮球,这就是非闭包。闭包就是负责到底,你找到A部门,A部门接待的那个人负责到底,他/她去协调B部门和C部门。

在工程上,闭包就是项目经理,负责调度项目所需要的资源。老板、客户有什么事情,直接找项目经理即可,不用再去找其它的人。

闭包的定义及其优缺点概况

闭包 是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量。

闭包的缺点

一般函数执行完毕后,局部活动对象就被销毁,内存中仅仅保存全局作用域。但闭包的情况不同!

闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。

使用闭包的好处

那么使用闭包有什么好处呢?使用闭包的好处是:

  • 希望一个变量长期驻扎在内存中

  • 避免全局变量的污染

  • 私有成员的存在(设计私有的方法和变量。)

嵌套函数的闭包

function closure () {var a = 1;return function () {console.log(a++);};
}var fun = closure();
fun();// 1 执行后 a++,,然后a还在~
fun();// 2
fun = null;//a被回收!!

闭包会使变量始终保存在内存中,如果不当使用会增大内存消耗

代码演示JS闭包

talk is cheap ,show me code

一、全局变量的累加

var a = 1;
function abc(){a++;console.log(a);
}
abc();// 2
abc();// 3

二、局部变量

function abc(){var a = 1;a++;console.log(a);
}
abc();// 2
abc();// 2

那么怎么才能做到变量a既是局部变量又可以累加呢?

三、局部变量的累加

function outer () {var x = 10;//函数嵌套函数return function () {x++;alert(x);};
}//外部函数赋给变量y;
var y = outer();
//y函数调用一次,结果为11,相当于outer()();
y();
//y函数调用第二次,结果为12,实现了累加
y();

函数声明与函数表达式

在js中我们可以通过关键字function来声明一个函数:

function abc () {console.log(123);
}
abc();

我们也可以通过一个"()"来将这个声明变成一个表达式:

//然后通过()直接调用前面的表达式即可,因此函数可以不必写名字;
(function () {console.log(123);
})();

四、模块化代码,减少全局变量的污染

var abc = (function(){      //abc为外部匿名函数的返回值var a = 1;return function(){a++;console.log(a);}
})();
abc();    //2 ;调用一次abc函数,其实是调用里面内部函数的返回值
abc();    //3

五、私有成员的存在

var aaa = (function(){var a = 1;function bbb(){a++;console.log(a);}function ccc(){a++;alert(a);}return { b:bbb, c:ccc }           //json结构
})();
aaa.b();     //2
aaa.c();     //3

六.使用匿名函数实现累加

function box(){var age = 100;return function(){          //匿名函数age++;return age;};}
var b = box();
console.log(b());
console.log(b());    //即alert(box()());
console.log(b());
console.log(b);
b = null;  //解除引用,等待垃圾回收

七、在循环中直接找到对应元素的索引

 window.onload = function () {var aLi = document.getElementsByTagName('li')for(let i =0 ;i<aLi.length;i++){(function () {//TODO})(i)}
};

九.内存泄露问题

由于IEjs对象和DOM对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题,也就是无法销毁驻留在内存中的元素

function closure(){var oDiv = document.getElementById('oDiv');//oDiv用完之后一直驻留在内存中oDiv.onclick = function () {console.log('oDiv.innerHTML');//这里用oDiv导致内存泄露};
}
closure();
//最后应将oDiv解除引用来避免内存泄露
function closure(){var oDiv = document.getElementById('oDiv');var test = oDiv.innerHTML;oDiv.onclick = function () {alert(test);};oDiv = null;
}

扩展阅读:javascript学习总结(四)function函数部分

转载本站文章《从λ演算到函数式编程聊闭包(2):彻底理解JavaScript闭包规则》,
请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/js6/2015_1024_325.html

从λ演算到函数式编程聊闭包(2):彻底理解JavaScript闭包规则相关推荐

  1. 从λ演算到函数式编程聊闭包(1):闭包概念在Java/PHP/JS中形式

    什么是闭包 如果让谷哥找一下"闭包"这个词,会发现网上关于闭包的文章已经不计其数 维基百科上对闭包的解释就很经典: 在计算机科学中,闭包(Closure)是词法闭包(Lexical ...

  2. 让你分分钟理解 JavaScript 闭包

    原文:https://www.cnblogs.com/onepixel/p/5062456.html 让你分分钟理解 JavaScript 闭包 闭包,是 Javascript 比较重要的一个概念,对 ...

  3. 全面理解Javascript闭包和闭包的几种写法及用途--转载自https://www.cnblogs.com/yunfeifei/p/4019504.html...

    全面理解Javascript闭包和闭包的几种写法及用途 好久没有写博客了,过了一个十一长假都变懒了,今天总算是恢复状态了.好了,进入正题,今天来说一说javascript里面的闭包吧!本篇博客主要讲一 ...

  4. 深入理解JavaScript闭包(closure) 【收藏】

    深入理解JavaScript闭包(closure) 原文地址:http://www.felixwoo.com/archives/247  Felix Woo 最近在网上查阅了不少Javascript闭 ...

  5. (转)深入理解Javascript闭包(closure)

    深入理解Javascript闭包(closure) 一.什么是闭包?        "官方"的解释是:所谓"闭包",指的是一个拥有许多变量和绑定了这些变量的环境 ...

  6. JS函数式编程思维:柯里化、闭包

    偏函数(Partial Application): 探讨柯里化之前,我们先聊一聊很容易跟其混淆的另一个概念--偏函数(Partial Application).在维基百科中,对 Partial App ...

  7. 理解 JavaScript 闭包{转载}

    本文转载自:http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html 要成为高级 JavaScript 程序 ...

  8. 全面理解Javascript闭包和闭包的几种写法及用途【转】

    一.什么是闭包和闭包的几种写法和用法 1.什么是闭包 闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.闭包的特点: 1. ...

  9. 全面理解Javascript闭包和闭包的几种写法及用途

     一.什么是闭包和闭包的几种写法和用法 1.什么是闭包 闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.闭包的特点: 1. ...

最新文章

  1. 使用Dom4j操作XML数据
  2. oracle 并接去掉字符串,ORACLE删除字符-TRIM字符截取-substr查找字符-instr
  3. 大作完成了一部分,陆续往上放吧
  4. 微软总裁:比尔盖茨人生简介和名言
  5. python中定义类
  6. 二叉排序树的建立、先序/中序/后序遍历、查找
  7. mq补偿机制java代码_RocketMQ源码分析之消息消费机制-消费端消息负载均衡机制与重新分布 - Java 技术驿站-Java 技术驿站...
  8. 七个你可能不了解的CSS单位
  9. 如何系统的自学python-应该怎样系统的学习Python标准库?
  10. ReentrantReadWriteLock 可重入的读写锁
  11. Atitit 避税之道 如何降低企业与项目组成本 attilax总结
  12. Python语言程序设计----【第8周 程序设计方法学】之8.2 Python程序设计思维
  13. 学术论文撰写(逻辑清晰,浅显易懂,套用模板)
  14. [RL 13] VDN (201706, DeepMind)
  15. 良好的代码习惯(一)
  16. 修炼系列(八),你真的会写注释吗
  17. idea中的一些有趣的插件
  18. 内网渗透(四十六)之横向移动篇-使用系统漏洞ms17010横向移动
  19. 【无标题】IP地址段必须正好可以聚合成1个地址块
  20. fatal error: opencv2\core\core.hpp: No such file or directory

热门文章

  1. 使用OrgChart技术流程图(树状图)
  2. 距离大决战777天——目前的进度
  3. 用 PyInstaller 打包基于 PyQt 的程序遇到的坑
  4. 《祝福-张学友》歌词查看
  5. 第一次扁平化博客实战练习
  6. excel 常用公式
  7. 测试覆盖率是软件测试的重要组成部分?当然是,必须是啊!
  8. 绿色信贷对兴业银行竞争力的影响研究_kaic
  9. 将中国标准时间转换为年月日时分秒格式
  10. 中合国创杯2017年创客中国互联网+创新创业大赛项目初筛完成