原文: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. Apple Swift编程语言入门

    1   简单介绍 今天凌晨Apple刚刚公布了Swift编程语言,本文从其公布的书籍<The Swift Programming Language>中摘录和提取而成.希望对各位的iOS&a ...

  2. 伪异步IO理解

    伪异步IO实在堵塞IO的基础上将每个client发送过来的请求由新创建的线程来处理改进为用线程池来处理.因此避免了为每个client请求创建一个新线程造成的资源耗尽问题. 来看一下伪异步IO的服务端代 ...

  3. POJ1274_The Perfect Stall(二部图最大匹配)

    解决报告 http://blog.csdn.net/juncoder/article/details/38136193 id=1274">题目传送门 题意: n头m个机器,求最大匹配. ...

  4. OpenCV——Delaunay三角 [转载]

    从这个博客转载 http://blog.csdn.net/raby_gyl/article/details/17409717 请其它同学转载时注明原始文章的出处! Delaunay三角剖分是1934年 ...

  5. Hibernate一个简短的引论

    我们从几个方面进行阐述Hibernate When? What ? How? When? Hibernate由来是因为当时EJBBean1.1在处理entittBean架构时,花费的时间要比业务逻辑很 ...

  6. W5500EVB TCP Client模式设置说明

    W5500EVB是WIZnet为了方便用户更好了解.使用W5500这款网络芯片所开发的评估板,该板採用了 STM32F103RCT6+W5500 的设计.基于 ARM 的 Cortex-M3 平台.那 ...

  7. WebGL 在 OpenGL ES 指令 iOS 在 C 分歧版指令分析

    WebGL 中 OpenGL ES 指令与 iOS 中 C 版指令的差异简析 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途 ...

  8. apk反编译工具-apktool

    apk很easy被反编译,关于这篇文章apktool反编译工具. (一)优势反编译 1.能学习别人优秀代码 看看优秀apk他们的string.color命名规则,看看布局排版等都能学习到东西. 2.进 ...

  9. Team Foundation Server 2015使用教程--团队项目创建

  10. Python网络02 Python服务器进化

    原文:Python网络02 Python服务器进化 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! **注意,在Python 3. ...