JavaScript闭包的一些理解

原文:JavaScript闭包的一些理解

简单一点的说:闭包就是能够读取其他函数内部变量的函数。那如何实现读取其它函数内部变量呢,大家都知道在JavaScript中内部函数可以访问其父函数中的变量,那如果将内部函数返回是不是代表能够通过它访问其父函数中的变量了呢,闭包的原理事实上就是这样。

摘录

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

闭包的主要作用:

  1. 可以读取函数内部的变量
  2. 让函数内部的变量值始终保持在内存中

使用闭包应该注意的地方: 

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。 

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

闭包的一些例子

下面来看闭包相关的一些例子,希望从demo中能够深入理解一下JavaScript中的闭包。

function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

在上面的这个demo中,displayName事实上就是一个闭包,我们知道在全局范围内是根本没法访问makeFunc函数中的局部变量name,但是通过displayName这个闭包通过访问makeFunc函数中的局部变量name,这也就是为什么执行myFunc函数时弹出的是Mozilla

下面来看一个有趣的例子。

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

下面的这个例子可能大家一看就知道答案了,因为闭包函数increment、decrement、value可以访问privateCounter,所以答案不言而喻了。 

var counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };
})();

alert(counter.value()); /* Alerts 0 */
counter.increment();
counter.increment();
alert(counter.value()); /* Alerts 2 */
counter.decrement();
alert(counter.value()); /* Alerts 1 */

 下面的这个例子也不难,因为makeCounter返回的是一个对象,所以每个对象内部变量privateCounter不同,所以下面的例子中对象counter1 和对象counter2能够访问到的privateCounter各占不同的内存空间。

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var counter1 = makeCounter();
var counter2 = makeCounter();
alert(counter1.value()); /* Alerts 0 */
counter1.increment();
counter1.increment();
alert(counter1.value()); /* Alerts 2 */
counter1.decrement();
alert(counter1.value()); /* Alerts 1 */
alert(counter2.value()); /* Alerts 0 */

 再来看一个典型的闭包的例子

 假设页面上的html元素如下代码所示:

 

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>

 

然后我们的JS代码如下所示:

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

本来我们希望

  • 在email文本框中focus的时候,help元素中显示的是Your e-mail address
  • 在name文本框中focus的时候,help元素中显示的是Your full name
  • 在age文本框中focus的时候,help元素中显示的是Your age (you must be over 16)

结果却发现不管哪个文本框focus,help元素始终显示的是Your age (you must be over 16)  这事实上就是闭包的一个特性,它始终访问的是局部变量的最终值。

所以你应该用下方的代码来实现你预期的效果

        function showHelp(help) {
            document.getElementById('help').innerHTML = help;
        }

        function makeHelpCallback(help) {
            return function () {
                showHelp(help);
            };
        }

        function setupHelp() {
            var helpText = [
                { 'id': 'email', 'help': 'Your e-mail address' },
                { 'id': 'name', 'help': 'Your full name' },
                { 'id': 'age', 'help': 'Your age (you must be over 16)' }
            ];

            for (var i = 0; i < helpText.length; i++) {
                var item = helpText[i];
                document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
            }
        }

        setupHelp();

上面的这个示例使用了一个闭包来保存函数局部变量的值,相信你呆会看了下方的示例,肯定会马上明白的。

var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());//The Window  this为全局对象,所以alert处理的name为The window

this的指向是由它所在函数调用的上下文决定的,而不是由它所在函数定义的上下文决定的。因为闭包最后的返回值是一个函数,注意紧紧是一个函数而已 并没有执行,等到alert调用时才执行,而这时执行调用的方法this所指向的变量为window.

var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;//注意这里用局部变量that保存this的值    that 为object对象,所以alert 处理的name为My object
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()());//注意这里是有两个()         //弹出My Object

如果嵌套函数作为函数调用,其this值不是全局对象(非严格模式下)就是undefined(严格模式下);(上述弹出The window的示例中getNameFunc就是作为函数调用)

如果嵌套函数作为方法调用,其this值指向调用它的对象。(这个例子中弹出My Object变是作为方法调用)

 

关于Prototype的小示例

 

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}

 

在上述这个例子中,大家都明白如果你声明了100个MyObject对象的话就会用100个name、message、getName、getMessage在内存中,100个name、message属性这个无可厚非,但是100个getName、getMessage方法未免就有些浪费资源的,因为事实上所有对象是可以共享方法来实现节省资源。这样你可能会想到用如下代码来声明MyObject对象

 

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};

 

但我只能说这样的代码我实在是无法恭维,原因是重写的prototype,大家都知道重写的prototype带来的坏处,最简洁的一句话来形容坏处就是如果你之前给MyObject所作的一切建设都得重新开始。

所以推荐使用下面的示例,但是下面的这个示例可能习惯于写服务器端编程语言的人会很看不惯其风格(哈哈,这个你想其它办法......)

 

function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};

 参考文献为阮大师的  学习Javascript闭包(Closure)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

人都是有惰性的,其实自己的英文水平还可以就是懒得去提升,现在才发现原来看英文博客其实挺好的。

建议大家有空提升一下自己的英文水平,事实上国外一些技术大牛们写的博客确实挺不错的。如果大家有前端好的英文博文可以推荐一下给我。

时间: 2024-10-26 05:46:23

JavaScript闭包的一些理解的相关文章

javascript闭包的理解

 1.首先我们要知道变量作用域链 变量的作用域分两种:全局变量和局部变量.没有定义到任何函数中的变量为全局变量,在函数中定义的变量为局部变量,注意在函数内部定义变量时一定要使用var关键字,不带var关键字的变量为全局变量. javascript中每一段代码都有与之关联的作用域链,这个作用域链是一个对象列表或者链表,定义了这段代码"作用域"中的变量.顶层代码的作用域由全局变量组成:不包含嵌套的函数的作用域链有两个对象:一个是定义的函数参数和局部变量的对象,一个是全局变量对象:而嵌套函数

Javascript闭包简单理解

原文:Javascript闭包简单理解 提到闭包,想必大家都早有耳闻,下面说下我的简单理解.说实话平时工作中实际手动写闭包的场景并不多,但是项目中用到的第三方框架和组件或多或少用到了闭包.所以,了解闭包是非常必要的.呵呵... 一.什么是闭包简而言之,就是能够读取其他函数内部变量的函数.由于JS变量作用域的特性,外部不能访问内部变量,内部可以外部变量. 二.使用场景1. 实现私有成员.2. 保护命名空间,避免污染全局变量.3. 缓存变量. 先看一个封装的例子: var person = func

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

原文:全面理解Javascript闭包和闭包的几种写法及用途 好久没有写博客了,过了一个十一长假都变懒了,今天总算是恢复状态了.好了,进入正题,今天来说一说javascript里面的闭包吧!本篇博客主要讲一些实用的东西,主要将闭包的写法.用法和用途. 一.什么是闭包和闭包的几种写法和用法                                                       1.什么是闭包 闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函

深入理解javascript闭包的教程

1,闭包是什么 (百度百科定义)--闭包是可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变 量)."闭包" 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境 (作用域).     W3C ECMAScript在闭包的解释中,有这么一句话:闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用

JavaScript 闭包深入理解(closure)_javascript技巧

一.什么是闭包? "官方"的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话通俗的来说就是:JavaScript中所有的function都是一个闭包.不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的"闭包".看下面这段代码: function a() { var i = 0; function b() { a

用最通俗易懂的代码帮助新手理解javascript闭包 推荐_javascript技巧

最近看了几篇有关javascript闭包的文章,包括最近正火的汤姆大叔系列,还有<javascript高级程序设计>中的文章,--我看不懂,里面有些代码是在大学教科书中看都没看过的,天书一般.幸好最近遇到两本好书<ppk on javascript>和<object-oriented JavaScript>,正字阅读中,后者还没有中文版,但前者还是建议看原版,写的不复杂,有兴趣的朋友可以看看,适合想进阶的朋友. 今天就结合这两本书,用最浅显的语言和最通俗的方式谈谈jav

javascript闭包的理解_javascript技巧

1.首先我们要知道变量作用域链 变量的作用域分两种:全局变量和局部变量.没有定义到任何函数中的变量为全局变量,在函数中定义的变量为局部变量,注意在函数内部定义变量时一定要使用var关键字,不带var关键字的变量为全局变量. javascript中每一段代码都有与之关联的作用域链,这个作用域链是一个对象列表或者链表,定义了这段代码"作用域"中的变量.顶层代码的作用域由全局变量组成:不包含嵌套的函数的作用域链有两个对象:一个是定义的函数参数和局部变量的对象,一个是全局变量对象:而嵌套函数的

理解javascript闭包

原文:理解javascript闭包 1.闭包是什么 官方解释:闭包是一个拥有很多变量和绑定了这些变量的环境的表达式(其实就是函数),因而这些变量也是该表达式的一部分.这个定义虽然太学术,但是告诉我们两个信息: 1)闭包是一个函数 2)函数中有很多变量 上面两个是构成闭包的两个主要条件. 下面我们用通俗的话来解释一下:js中的所有函数都是闭包(因为函数中的局部变量只能函数内部访问),但是嵌套函数产生的闭包更加强大,也是我们现在所探讨的闭包. 如果上面的解释还不够通俗,下面的终极解释我想你一定能够看

JavaScript 闭包深入理解(closure)

最近在网上查阅了不少Javascript闭包(closure)相关的资料,写的大多是非常的学术和专业.对于初学者来说别说理解闭包了,就连文字叙述都很难看懂.撰写此文的目的就是用最通俗的文字揭开Javascript闭包的真实面目. -一.什么是闭包?"官方"的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话通俗的来说就是:JavaScript中所有的functio