原文: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)); //
console.log(add10(2)); //

下面的这个例子可能大家一看就知道答案了,因为闭包函数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

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

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

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

  1. 对JavaScript闭包和原型理解

    最近在学js脚本的一些东西觉得里面有2个知识点比较难理解所以做了如下总结. 1.闭包 简单的理解:一个函数a ,内部有个函数b,那么这个函数b当被作为a函数的返回值得时候被外部的全局变量引用了,那么这 ...

  2. javascript 闭包最简单理解

    首先说3点与闭包有关系的东西. 一.变量的作用域 变量的作用域不难理解. 1.函数内部可以访问函数外部的变量,而函数外部不能访问函数内部的变量. 2.如果在函数内定义变量的时候,不加var,那么是全局 ...

  3. 我也谈javascript闭包的原理理解

    参考原文:http://www.oschina.net/question/28_41112 前言:还是一篇入门文章.Javascript中有几个非常重要的语言特性——对象.原型继承.闭包.其中闭包 对 ...

  4. JavaScript 闭包(个人理解)

    当function里嵌套function时,内部的function可以访问外部function里的变量.但这不是闭包 function foo(x) { var tmp = 3; function b ...

  5. JavaScript闭包的深入理解

    闭包算是javascript中一个比较难理解的概念,想要深入理解闭包的原理,首先需要搞清楚其他几个概念: 一.栈内存和堆内存 学过C/C++的同学可能知道,计算机系统将内存分为栈和堆两部分(大学的基础 ...

  6. JavaScript闭包函数的理解

    闭包就是一个函数能够访问其函数外部作用域中的变量,即在外面可以调用函数中的函数的变量,其实他就是将函数内外部连接起来的桥梁 闭包三大特点: 1. 函数嵌套函数 2. 内部函数可以访问外部函数的变量 3 ...

  7. 关于JavaScript闭包的粗浅理解

    在JavaScript中,使用var创建变量,会创建全局变量或局部变量. 只有在非函数内创建的变量,才是全局变量,该变量可以在任何地方被读取. 而在函数内创建变量时,只有在函数内部才可读取.在函数外部 ...

  8. JavaScript 闭包的详细分享(三种创建方式)(附小实例)

    JavaScript闭包的详细理解 一.原理:闭包函数--指有权访问私有函数里面的变量和对象还有方法等:通俗的讲就是突破私有函数的作用域,让函数外面能够使用函数里面的变量及方法. 1.第一种创建方式 ...

  9. JavaScript闭包理解【关键字:普通函数、闭包、解决获取元素标签索引】

    以前总觉得闭包很抽象,很难理解,所以百度一下"闭包"概览,百度的解释是:“闭包是指可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的 ...

随机推荐

  1. Java NIO 系列教程(转)

    原文中说了最重要的3个概念,Channel 通道Buffer 缓冲区Selector 选择器其中Channel对应以前的流,Buffer不是什么新东西,Selector是因为nio可以使用异步的非堵塞 ...

  2. 打印出所有&quot;水仙花数

    时间限制: 1 Sec  内存限制: 128 MB 提交: 695  解决: 352 [提交][状态][讨论版] 题目描述 打印出所有"水仙花数",所谓"水仙花数&quo ...

  3. iOS8发展~Swift(三)UI详细解释

    一个.总结 使用Swift去完成iOS的UI接口,事实上,目前的想法和OC实现几乎一致,只是在措辞非常大的差异,修改前更更大的个人控制.为有纯代码强迫症,所以接下来创建一个纯代码动项目,然后在此基础上 ...

  4. 【Java基础】System.arraycopy()的使用详解

    由于在Java中System.arraycopy()方法在一维数组和二维数组中的表现不同,所以做了一个测试 public static void main(String[] args) { int[] ...

  5. 玩转Web之JavaScript(三)-----javaScript语法总结(三) 窗口/滚动条/文本的相关语法

    JS语法集锦(三) 窗口/滚动条/文本 alert("文本")    警告框:警告框经常用于确保用户可以得到某些信息,当警告框出现后,用户需要点击确定按钮才能继续进行操作. con ...

  6. tomcatserver乱码问题,tomcat与数据库之间的编码统一转换

    在tomcat文件夹的conf文件夹下,改动server.xml文件,在以下截图中的位置加上URIEncoding="UTF-8"则表示tomcat编码转换为utf-8风格, 一般 ...

  7. 淘宝异构数据源数据交换工具 DataX

    淘宝异构数据源数据交换工具 DataX 阅读目录 DataX是什么? DataX用来解决什么? DataX特点? DataX结构模式(框架+插件) DataX在淘宝的运用 DataX是什么? Data ...

  8. ios学习网络------4 UIWebView以三种方式中的本地数据

    UIWebView这是IOS内置的浏览器.能够浏览网页,打开文档  html/htm  pdf   docx  txt等待格文档类型. safari浏览器是通过UIWebView制作. server将 ...

  9. SharePoint管理中心来配置资源限制(大名单)

    SharePoint管理中心来配置资源限制(大名单) 名单SharePoint核心.SharePoint一切的一切都是列表. 我可以说SharePoint内容为驱动的列表. 之前版本号的SharePo ...

  10. Ajax实践之用户是否存在

    关于Ajax在之前的学习中,已经对它的基础知识有了初步的了解.仅仅是欠实践. 那么接下来就让实践来检验一下真理吧! 基础见:http://blog.csdn.net/liu_yujie2011com/ ...