JavaScript闭包的一些理解
简单一点的说:闭包就是能够读取其他函数内部变量的函数。那如何实现读取其它函数内部变量呢,大家都知道在JavaScript中内部函数可以访问其父函数中的变量,那如果将内部函数返回是不是代表能够通过它访问其父函数中的变量了呢,闭包的原理事实上就是这样。
摘录
闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
闭包的主要作用:
- 可以读取函数内部的变量
- 让函数内部的变量值始终保持在内存中
使用闭包应该注意的地方:
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闭包的一些理解的更多相关文章
- 对JavaScript闭包和原型理解
最近在学js脚本的一些东西觉得里面有2个知识点比较难理解所以做了如下总结. 1.闭包 简单的理解:一个函数a ,内部有个函数b,那么这个函数b当被作为a函数的返回值得时候被外部的全局变量引用了,那么这 ...
- javascript 闭包最简单理解
首先说3点与闭包有关系的东西. 一.变量的作用域 变量的作用域不难理解. 1.函数内部可以访问函数外部的变量,而函数外部不能访问函数内部的变量. 2.如果在函数内定义变量的时候,不加var,那么是全局 ...
- 我也谈javascript闭包的原理理解
参考原文:http://www.oschina.net/question/28_41112 前言:还是一篇入门文章.Javascript中有几个非常重要的语言特性——对象.原型继承.闭包.其中闭包 对 ...
- JavaScript 闭包(个人理解)
当function里嵌套function时,内部的function可以访问外部function里的变量.但这不是闭包 function foo(x) { var tmp = 3; function b ...
- JavaScript闭包的深入理解
闭包算是javascript中一个比较难理解的概念,想要深入理解闭包的原理,首先需要搞清楚其他几个概念: 一.栈内存和堆内存 学过C/C++的同学可能知道,计算机系统将内存分为栈和堆两部分(大学的基础 ...
- JavaScript闭包函数的理解
闭包就是一个函数能够访问其函数外部作用域中的变量,即在外面可以调用函数中的函数的变量,其实他就是将函数内外部连接起来的桥梁 闭包三大特点: 1. 函数嵌套函数 2. 内部函数可以访问外部函数的变量 3 ...
- 关于JavaScript闭包的粗浅理解
在JavaScript中,使用var创建变量,会创建全局变量或局部变量. 只有在非函数内创建的变量,才是全局变量,该变量可以在任何地方被读取. 而在函数内创建变量时,只有在函数内部才可读取.在函数外部 ...
- JavaScript 闭包的详细分享(三种创建方式)(附小实例)
JavaScript闭包的详细理解 一.原理:闭包函数--指有权访问私有函数里面的变量和对象还有方法等:通俗的讲就是突破私有函数的作用域,让函数外面能够使用函数里面的变量及方法. 1.第一种创建方式 ...
- JavaScript闭包理解【关键字:普通函数、闭包、解决获取元素标签索引】
以前总觉得闭包很抽象,很难理解,所以百度一下"闭包"概览,百度的解释是:“闭包是指可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的 ...
随机推荐
- crm创建基于fetch自己的自定义报告
在解决方案资源管理器,右键点击"报表"目录.然后点击"增加了新的报告". 打开"报表向导". 在"欢迎来到报表向导"前, ...
- c# winform 中的 工具栏自动隐藏 splitter用法 带源码
c# winform 中的 工具栏自动隐藏 splitter用法 带源码 代码下载地址 http://download.csdn.net/detail/simadi/7649313
- Android 4.4环境搭建——Android SDK下载与安装
学习开发Android应用程序,须要下载安装Android SDK.在Android的官方站点的二级域名http://developer.android.com/index.html中.能够下载到完整 ...
- mapreduce任务失败、重试、猜測式运行机制小结
mapreduce中我们自己定义的mapper和reducer程序在运行后有可能遇上出错退出的情况,mapreduce中jobtracker会全程追踪任务的运行情况,对于出错的任务mapreduce也 ...
- android关键知识
1.handler 与Looper 与MessageQueue .Message关系 handler:是处理主线程(ui线程)处理耗时操作的线程,通过post message到MessageQueue ...
- Eclipse4.4设备egit插件提交本地项目代码到远程仓库
一.设备egit 打开Eclipse的Marketplace.在搜索框中输入egit就可以,能够看到Eclipse4.4已经默认安装了egit,当然假设有新版本号的egit公布的话,也能够在下图上点击 ...
- [Unity3D]Unity3D游戏开发3D选择场景中的对象,并显示轮廓效果强化版
大家好,我是秦培,欢迎关注我的博客,我的博客地址blog.csdn.net/qinyuanpei. 在上一篇文章中,我们通过自己定义着色器实现了一个简单的在3D游戏中选取.显示物体轮廓的实例. 在文章 ...
- 配置SQL Server 2008的资源调控器实现负载均衡
原文:配置SQL Server 2008的资源调控器实现负载均衡 转自:http://www.ithov.com/server/93267.shtml 1.为什么引入"资源调控器" ...
- cocos2dx tolua传递参数分析
cocos2dx tolua传递参数分析: tolua_Cocos2d_CCNode_addChild00 == void CCNode::addChild(CCNode *child) tolua_ ...
- 移动端 延迟加载echo.js的使用
浏览器支持ie8+ <img src="img/blank.gif" alt="" data-echo="img/album-1.jpg&q ...