闭包是你家庭中的第三者你在享受着第三者给你带来的便利时,而你的家庭也随时触发前所未有的危机(直男癌患者的观点);
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见的方式,就是在
一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。

  1. //通过闭包可以返回局部变量
  2. function box() {
  3. var user = 'Lee';
  4. return function () { //通过匿名函数返回 box()局部变量
  5. return user;
  6. };
  7. }
  8. alert(box()()); //通过box()()来直接调用匿名函数返回值
  9. var b = box();
  10. alert(b()); //另一种调用匿名函数返回值

使用闭包有一个优点,也是它的缺点:就是可以把局部变量驻留在内存中,可以避免使
用全局变量。(全局变量污染导致应用程序不可预测性,每个模块都可调用必将引来灾难,
所以推荐使用私有的,封装的局部变量)。

  1. //通过全局变量来累加
  2. var age = 100; //全局变量
  3. function box() {
  4. age ++; //模块级可以调用全局变量,进行累加
  5. }
  6. box(); //执行函数,累加了
  7. alert(age); //输出全局变量
  8. //通过局部变量无法实现累加
  9. function box() {
  10. var age = 100;
  11. age ++; //累加
  12. return age;
  13. }
  14. alert(box()); //
  15. alert(box()); //101,无法实现,因为又被初始化了
  16. //通过闭包可以实现局部变量的累加
  17. function box() {
  18. var age = 100;
  19. return function () {
  20. age ++;
  21. return age;
  22. }
  23. }
  24. var b = box(); //获得函数
  25. alert(b()); //调用匿名函数
  26. alert(b()); //第二次调用匿名函数,实现累加

PS:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更
多的内存。过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。
作用域链的机制导致一个问题,在循环中里的匿名函数取得的任何变量都是最后一个
值。

  1. //循环里包含匿名函数
  2. function box() {
  3. var arr = [];
  4. for (var i = 0; i < 5; i++) {
  5. arr[i] = function () {
  6. return i;
  7. };
  8. }
  9. return arr;
  10. }
  11. var b = box(); //得到函数数组
  12. alert(b.length); //得到函数集合长度
  13. for (var i = 0; i < b.length; i++) {
  14. alert(b[i]()); //输出每个函数的值,都是最后一个值
  15. }

上面的例子输出的结果都是 5,也就是循环后得到的最大的 i 值。因为 b[i]调用的是匿
名函数,匿名函数并没有自我执行,等到调用的时候,box()已执行完毕,i 早已变成 5,所
以最终的结果就是 5 个 5。

  1. //循环里包含匿名函数-改 1,自我执行匿名函数
  2. function box() {
  3. var arr = [];
  4. for (var i = 0; i < 5; i++) {
  5. arr[i] = (function (num) { //自我执行
  6. return num;
  7. })(i); //并且传参
  8. }
  9. return arr;
  10. }
  11. var b = box();
  12. for (var i = 0; i < b.length; i++) {
  13. alert(b[i]); //这里返回的是数组,直接打印即可
  14. }

改 1 中,我们让匿名函数进行自我执行,导致最终返回给 a[i]的是数组而不是函数了 。
最终导致 b[0]-b[4]中保留了 0,1,2,3,4 的值。

  1. //循环里包含匿名函数-改 2,匿名函数下再做个匿名函数
  2. function box() {
  3. var arr = [];
  4. for (var i = 0; i < 5; i++) {
  5. arr[i] = (function (num) {
  6. return function () { //直接返回值,改 2 变成返回函数
  7. return num; //原理和改 1 一样
  8. }
  9. })(i);
  10. }
  11. return arr;
  12. }
  13. var b = box();
  14. for (var i = 0; i < b.length; i++) {
  15. alert(b[i]()); //这里通过 b[i]()函数调用即可
  16. }

改 1 和改 2 中,我们通过匿名函数自我执行,立即把结果赋值给 a[i]。每一个 i,是调
用方通过按值传递的,所以最终返回的都是指定的递增的 i。而不是 box()函数里的 i。

关于 this 对象
在闭包中使用 this 对象也可能会导致一些问题,this 对象是在运行时基于函数的执行环
境绑定的,如果 this 在全局范围就是 window,如果在对象内部就指向这个对象。而闭包却
在运行时指向 window 的,因为闭包并不属于这个对象的属性或方法。

  1. var user = 'The Window';
  2. var obj = {
  3. user : 'The Object',
  4. getUserFunction : function () {
  5. return function () { //闭包不属于obj,里面的this指向window
  6. return this.user;
  7. };
  8. }
  9. };
  10. alert(obj.getUserFunction()()); //The window
  11. //可以强制指向某个对象
  12. alert(obj.getUserFunction().call(obj)); //The Object
  13. //也可以从上一个作用域中得到对象
  14. getUserFunction : function () {
  15. var that = this; //从对象的方法里得对象
  16. return function () {
  17. return that.user;
  18. };
  19. }

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

  1. function box() {
  2. var oDiv = document.getElementById('oDiv'); //oDiv 用完之后一直驻留在内存
  3. oDiv.onclick = function () {
  4. alert(oDiv.innerHTML); //这里用 oDiv 导致内存泄漏
  5. };
  6. }
  7. box();

那么在最后应该将 oDiv 解除引用来避免内存泄漏。

  1. function box() {
  2. var oDiv = document.getElementById('oDiv');
  3. var text = oDiv.innerHTML;
  4. oDiv.onclick = function () {
  5. alert(text);
  6. };
  7. oDiv = null; //解除引用
  8. }

PS:如果并没有使用解除引用,那么需要等到浏览器关闭才得以释放。

模仿块级作用域
JavaScript 没有块级作用域的概念。

  1. function box(count) {
  2. for (var i=0; i<count; i++) {}
  3. alert(i); //i 不会因为离开了 for 块就失效
  4. }
  5. box(2);
  6. function box(count) {
  7. for (var i=0; i<count; i++) {}
  8. var i; //就算重新声明,也不会前面的值
  9. alert(i);
  10. }
  11. box(2);

以上两个例子,说明 JavaScript 没有块级语句的作用域,if () {} for () {}等没有作用域 ,
如果有,出了这个范围 i 就应该被销毁了。就算重新声明同一个变量也不会改变它的值。
JavaScript 不会提醒你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声
明视而不见(如果初始化了,当然还会执行的)。使用模仿块级作用域可避免这个问题。

  1. //模仿块级作用域(私有作用域)
  2. (function () {
  3. //这里是块级作用域
  4. })();
  5. //使用块级作用域(私有作用域)改写
  6. function box(count) {
  7. (function () {
  8. for (var i = 0; i<count; i++) {}
  9. })();
  10. alert(i); //报错,无法访问
  11. }
  12. box(2);

使用了块级作用域(私有作用域)后,匿名函数中定义的任何变量,都会在执行结束时被
销毁。这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的
变量和函数。一般来说,我们都应该尽可能少向全局作用域中添加变量和函数。在大型项目
中,多人开发的时候,过多的全局变量和函数很容易导致命名冲突,引起灾难性的后果。如
果采用块级作用域(私有作用域),每个开发者既可以使用自己的变量,又不必担心搞乱全局
作用域。

  1. (function () {
  2. var box = [1,2,3,4];
  3. alert(box); //box 出来就不认识了
  4. })();

在全局作用域中使用块级作用域可以减少闭包占用的内存问题,因为没有指向匿名函数
的引用。只要函数执行完毕,就可以立即销毁其作用域链了。

私有变量
JavaScript 没有私有属性的概念;所有的对象属性都是公有的。不过,却有一个私有变
量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问
这些变量。

  1. function box() {
  2. var age = 100; //私有变量,外部无法访问
  3. }

而通过函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量。而
利用这一点,可以创建用于访问私有变量的公有方法。

  1. function Box() {
  2. var age = 100; //私有变量
  3. function run() { //私有函数
  4. return '运行中...';
  5. }
  6. this.get = function () { //对外公共的特权方法
  7. return age + run();
  8. };
  9. }
  10. var box = new Box();
  11. alert(box.get());

可以通过构造方法传参来访问私有变量。

  1. function Person(value) {
  2. var user = value; //这句其实可以省略
  3. this.getUser = function () {
  4. return user;
  5. };
  6. this.setUser = function (value) {
  7. user = value;
  8. };
  9. }

但是对象的方法,在多次调用的时候,会多次创建。可以使用静态私有变量来避免这个问题。

静态私有变量
通过块级作用域(私有作用域)中定义私有变量或函数,同样可以创建对外公共的特权方法。

  1. (function () {
  2. var age = 100;
  3. function run() {
  4. return '运行中...';
  5. }
  6. Box = function () {}; //构造方法
  7. Box.prototype.go = function () { //原型方法
  8. return age + run();
  9. };
  10. })();
  11. var box = new Box();
  12. alert(box.go());

上面的对象声明,采用的是 Box = function () {} 而不是 function Box() {} 因为如果用后
面这种,就变成私有函数了,无法在全局访问到了,所以使用了前面这种。

  1. (function () {
  2. var user = '';
  3. Person = function (value) {
  4. user = value;
  5. };
  6. Person.prototype.getUser = function () {
  7. return user;
  8. };
  9. Person.prototype.setUser = function (value) {
  10. user = value;
  11. }
  12. })();

使用了 prototype 导致方法共享了,而 user 也就变成静态属性了。(所谓静态属性,即共享于不同对象中的属性)。

模块模式
之前采用的都是构造函数的方式来创建私有变量和特权方法。那么对象字面量方式就采用模块模式来创建。

  1. var box = { //字面量对象,也是单例对象
  2. age : 100, //这是公有属性,将要改成私有
  3. run : function () { //这时公有函数,将要改成私有
  4. return '运行中...';
  5. };
  6. };

私有化变量和函数:

  1. var box = function () {
  2. var age = 100;
  3. function run() {
  4. return '运行中...';
  5. }
  6. return { //直接返回对象
  7. go : function () {
  8. return age + run();
  9. }
  10. };
  11. }();

上面的直接返回对象的例子,也可以这么写:

  1. var box = function () {
  2. var age = 100;
  3. function run() {
  4. return '运行中...';
  5. }
  6. var obj = { //创建字面量对象
  7. go : function () {
  8. return age + run();
  9. }
  10. };
  11. return obj; //返回这个对象
  12. }();

字面量的对象声明,其实在设计模式中可以看作是一种单例模式,所谓单例模式,就是永远保持对象的一个实例。

增强的模块模式,这种模式适合返回自定义对象,也就是构造函数。

  1. function Desk() {};
  2. var box = function () {
  3. var age = 100;
  4. function run() {
  5. return '运行中...';
  6. }
  7. var desk = new Desk(); //可以实例化特定的对象
  8. desk.go = function () {
  9. return age + run();
  10. };
  11. return desk;
  12. }();
  13. alert(box.go());

JavaScript闭包总结的更多相关文章

  1. 《Web 前端面试指南》1、JavaScript 闭包深入浅出

    闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...

  2. JavaScript 闭包深入浅出

    闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...

  3. JavaScript闭包(Closure)

    JavaScript闭包(Closure) 本文收集了多本书里对JavaScript闭包(Closure)的解释,或许会对理解闭包有一定帮助. <你不知道的JavsScript> Java ...

  4. Javascript闭包和C#匿名函数对比分析

    C#中引入匿名函数,多少都是受到Javascript的闭包语法和面向函数编程语言的影响.人们发现,在表达式中直接编写函数代码是一种普遍存在的需求,这种语法将比那种必须在某个特定地方定义函数的方式灵活和 ...

  5. javascript闭包理解

    //闭包理解一 function superFun(){ var _super_a='a'; function subfuc(){ console.log(_super_a); } return su ...

  6. Javascript闭包深入解析及实现方法

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

  7. javascript闭包和作用域链

    最近在学习前端知识,看到javascript闭包这里总是云里雾里.于是翻阅了好多资料记录下来本人对闭包的理解. 首先,什么是闭包?看了各位大牛的定义和描述各式各样,我个人认为最容易一种说法: 外部函数 ...

  8. JavaScript闭包深入解析

    for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); } --上面这段 ...

  9. JavaScript 闭包系列二(匿名函数及函数的闭包)

    一. 匿名函数 1. 函数的定义,可分为三种 1) 函数声明方式 function double(x) {     return 2*x; } 2)Function构造函数,把参数列表和函数体都作为字 ...

  10. JavaScript闭包模型

      JavaScript闭包模型 -----  [原创翻译]2016-09-01  09:32:22 < 一>  闭包并不神秘 本文利用JavaScript代码来阐述闭包,目的是为了使普通 ...

随机推荐

  1. ubuntu—终端安装mysql

    ---恢复内容开始--- Step 1 : 安装指令 ~$ sudo apt-get install mysql-server Step 2: 查看是否正常安装 ~$ ps aux | grep my ...

  2. 基于Bootstrap Ace模板+bootstrap.addtabs.js的菜单

    这几天研究了基于bootstrap Ace模板+bootstra.addtabs.js实现菜单的效果 参考了这个人的博客 https://www.cnblogs.com/landeanfen/p/76 ...

  3. Spark Streaming job的生成及数据清理总结

    关于这次总结还是要从一个bug说起....... 场景描述:项目的基本处理流程为:从文件系统读取每隔一分钟上传的日志并由Spark Streaming进行计算消费,最后将结果写入InfluxDB中,然 ...

  4. python2x和python3x的一些区别

    python2x:各种按照自己代码习惯给python贡献源码 python3x:重写之后的源码,优美,清晰,简单 版本 打印函数 rang函数 输入函数 python2x print 或 print( ...

  5. Go语言入门(二)Go语言中的变量、常量、数据类型、流程控制以及函数

    Go语言中的变量 通常用var关键声明变量,有常规方式和简化方式. 常规方式: var name1 type1 name1 = value1 //赋值 简化方式: var name2 = value1 ...

  6. Java基础之this和super关键字用法

    this 在java中,this是一个引用当前类对象的引用变量,主要有以下几种用法: 引用当前类的实例变量· this关键字可以用来引用当前类的实例变量.如果实例变量和某个方法的参数之间存在歧义(实例 ...

  7. xargs命令的使用过程中一个小领悟:管道与xargs的差别

    对xargs的使用总是比较模糊,大概的理解为:通道中,上一个命令的标准输出逐行作为下一个命令的参数 例如 find /var/temp* | xargs rm -r 功效:找出/var/中所有temp ...

  8. Solr与Lucene的区别

    Lucene是一个优秀的开源搜索库,Solr是在Lucene上封装的完善的搜索引擎.通俗地说,如果Solr是汽车,那么Lucene就是发动机,没有发动机,汽车就没法运转,但对于用户来说只可开车,不能开 ...

  9. 20145209 2016-2017-2 《Java程序设计》第4周学习总结

    20145209 2016-2017-2 <Java程序设计>第4周学习总结 教材学习内容总结 继承是面向对象最显著的一个特性.继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性 ...

  10. BZOJ1083_繁忙的都市_KEY

    题目传送门 裸的最小生成树. code: /************************************************************** Problem: 1083 U ...