深入理解javascript中的闭包!(转)
1、闭包的经典错误
假如页面上有若干个div,我们想给它每个绑定一个onclick方法,于是有了下面的代码。
function A(){
var divs=document.getElementsByTagName("div");
for(var i=0; i<divs.length; i++){
divs[i].onclick=function(){
alert(i);
}
}
}
我原以为每次都弹出相对应的i,可是结果却是每次都弹出divs.length的值。于是查资料后知道解决方法是这样的。
function A(){
var divs=document.getElementsByTagName("div");
for(var i=0; i<divs.length; i++){
(function(n){
divs[i].onclick=function(){
alert(n);
}
})(i)
}
}
2、内部函数
因为是刚开始学习js,所以就从基础知识说起,什么是内部函数呢?说白了,内部函数就是定义在另一个函数中的函数,例如:
function Fn(){
function subFn(){
};//subFn就是一个内部函数,它定义在Fn函数里面。
}
由于subFn被包含在Fn里,这就意味着在Fn内部调用subFn函数是有效的,而在Fn外部调用subFn则是无效的。下面的代码会导致一个javascript的错误:
function Fn(){
document.write("Fn");
function subFn(){
document.write("subFn");
}
}
subFn();
不过在Fn内部调用subFn函数则是有效地:
function Fn(){
document.write("Fn");
function subFn(){
document.write("subFn");
}
subFn();
}
Fn();
2.1、伟大的逃脱
既然目前这样我们不能够在Fn函数外部调用subFn,那怎么才能实现让Fn函数外部也能调用subFn呢?javascript运行开发人员像传递任何类型的数据一样传递函数(这是关键),也就是说,javascript中的内部函数可以逃脱定义他们的外部函数。
(1)逃脱的方法有很多中:比如可以将一个内部函数指定给一个全局变量:
function Fn(){
document.write("Fn");
function subFn(){
document.write("subFn");
}
global=subFn;
}
Fn();
global();
当在调用Fn()函数时,会修改全局变量global,这时候它的引用为subFn;此后调用global和subFn是一样的效果。如果在Fn外部直接调用subFn仍然是会出错的,因为内部函数虽然通过引用保存在全局变量中实现了逃脱,但这个函数的名字依然只存在于Fn的作用域中。
(2)也可以通过父函数的返回值来获得内部函数的引用。
function Fn(){
document.write("Fn");
function subFn(){
document.write("subFn");
}
return subFn;
}
var subRef=Fn();
subRef();
这里并没有在Fn的内部修改全局变量,而是返回了一个对subFn的引用,通过调用Fn这个函数可以获取subFn的引用,而且这个引用可以保存在变量中。
这种即使离开函数作用域的情况下仍能够调用内部函数的事实,意味着只要存在调用内部函数的可能,javascript就要保留被引用的函数。而且javascript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,javascript垃圾收集器才能释放相应的内存空间。
说了半天终于说道和闭包的关系了,闭包是指有权访问另一个函数作用域的变量的函数,创建闭包的常见方式则是在一个函数内部创建另一个函数,就是我们上述说的内部函数。所以刚才说的不是废话,也是和闭包有关的。
1.2、变量的作用域
内部函数也可以有自己的变量,这些变量都被限制在内部函数的作用域中。
function Fn(){
document.write("Fn");
function subFn(){
var subVar=0;
subVar++;
document.write("subFn\t");
document.write("subVar="+subVar+"<br/>");
}
return subFn;
}
var subRef=Fn();
subRef();
subRef();
var subRef2=Fn();
subRef2();
subRef2();
每当通过引用和其他方式调用这个内部函数时,就会创建一个新的subVar变量,然后加1,得出的结果如下:
Fn
subFn innerVar = 1
subFn innerVar = 1
Fn
subFn innerVar = 1
subFn innerVar = 1
内部函数也可以像其他函数一样引用全局变量。
var global=0;
function Fn(){
document.write("Fn");
function subFn(){
global++;
document.write("subFn\t");
document.write("globalVar="+global+"<br/>");
}
return subFn();
}
var subRef=Fn();
subRef();
subRef();
var subRef2=Fn();
subRef2();
subRef2();
现在每次调用这个内部函数都会持续的递增这个全局变量的值。
Fn
subFn globalVar = 1
subFn globalVar = 2
Fn
subFn globalVar = 3
subFn globalVar = 4
如果这个变量是父函数的局部变量呢?因为内部函数会引用到父函数的作用域,内部函数也可以引用到这些变量。
function Fn() {
var outerVar = 0;
document.write("Fn<br/>");
function subFn() {
outerVar++;
document.write("subFn\t");
document.write("outerVar = " + outerVar + "<br/>");
}
return subFn;
}
var subRef = Fn();
subRef ();
subRef ();
var subRef2 = outerFn();
subRef2();
subRef2();
这次的结果非常有意思,也许会出乎我们的意料。
Fn
subFn outerVar = 1
subFn outerVar = 2
Fn
subFn outerVar = 1
subFn outerVar = 2
我们看到的是前两种情况合成的效果,通过每个引用调用的subFn都会独立递增的outerVar,也就是说第二次调用Fn并没有继续沿用outerVar的值,而是在第二次调用的作用域创建并绑定了一个新的outerVar实例,两个计数器无关。
当内部函数在定义他的作用域外部被引用时,就创建了该内部函数的一个闭包,这种情况下我们称既不是内部函数局部变量,也不是其参数的变量为自由变量,称外部函数的调用坏境为封闭闭包的坏境,从本质上讲,如果内部函数引用了外部函数的变量,相当于授权了该变量能够被延迟使用,因此当外部函数被调用完后,这些变量的内存不会被释放(最后的值会被保存),闭包仍然需要使用它们。
3、解惑
现在回过头来看刚才那个经典错误。就会明白为什么每次都弹出divs.length;
function A(){
var divs=document.getElementsByTagName("div");
for(var i=0; i<divs.length; i++){
divs[i].onclick=function(){
alert(i);
}
}
}
上面的代码在页面加载后就会执行,当i的值为divs.length时判断条件不成立,for循环执行完毕,但是因为每个divs的每个onclick方法为内部函数,所以i被闭包引用,内存不能销毁,i的值一直保持divs.length,直到程序改变它或者所有的onclick函数被销毁(主动把函数设为null或者页面卸载)时才会被回收,这样我每点击一次div,onclick函数就会查找i的值,一查等于divs.length,所以就每次都弹出的是divs .length;而第二种方法是使用了一个立即执行的函数又创建了一个闭包,函数声明方法括号内就成了表达式,后面在加上括号,括号就是调用咯,这是把i当参数传入,函数立即执行,num保存每次i的值。
这样,大家对闭包有一定的了解了吧。当然完全了解的话需要把函数的执行坏境和作用域链搞清楚。我个人觉得还蛮详细的。辛苦了~~~
深入理解javascript中的闭包!(转)的更多相关文章
- 深入理解JavaScript中的闭包
闭包没有想象的那么简单 闭包的概念在JavaScript中占据了十分重要的地位,有不少开发者分不清匿名函数和闭包的概念,把它们混为一谈,我希望借这篇文章能够让大家对闭包有一个清晰的认识. 大家都知道变 ...
- 【原】理解javascript中的闭包
闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术.下来对其进行一个小小的总结 什么是闭包? 官方说法: 闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见 ...
- 【原】理解javascript中的闭包(***********************************************)
阅读目录 什么是闭包? 闭包的特性 闭包的作用: 闭包的代码示例 注意事项 总结 闭包在javascript来说是比较重要的概念,平时工作中也是用的比较多的一项技术.下来对其进行一个小小的总结 回到顶 ...
- 全面理解JavaScript中的闭包的含义及用法
1.什么是闭包 闭包:闭包就是能够读取其他函数内部变量的函数;闭包简单理解成“定义在一个函数内部的函数”. 闭包的形式:即内部函数能够使用它所在级别的外部函数的参数,属性或者内部函数等,并且能在包含它 ...
- 理解JavaScript中的闭包
(这篇文章后面关于onclick事件的解释是错误的,请不要被误导了2016.6.16) 闭包这个概念给JavaScript初学者心中留下了巨大的阴影,网络上关于闭包的文章不可谓不多,但是能让初学者看懂 ...
- 理解 JavaScript 中的 this
前言 理解this是我们要深入理解 JavaScript 中必不可少的一个步骤,同时只有理解了 this,你才能更加清晰地写出与自己预期一致的 JavaScript 代码. 本文是这系列的第三篇,往期 ...
- [译]Javascript中的闭包(closures)
本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...
- JavaScript中的闭包理解
原创文章,转载请注明:JavaScript中的闭包理解 By Lucio.Yang 1.JavaScript闭包 在小学期开发项目的时候,用node.js开发了服务器,过程中遇到了node.js的第 ...
- 深入理解javascript原型和闭包 (转)
该教程绕开了javascript的一些基本的语法知识,直接讲解javascript中最难理解的两个部分,也是和其他主流面向对象语言区别最大的两个部分--原型和闭包,当然,肯定少不了原型链和作用域链.帮 ...
随机推荐
- 【转】C++ 笔试面试题目
原文:http://blog.csdn.net/txgc1009/article/details/6700830 许多面试题看似简单,却需要深厚的基本功才能给出完美的解答.企业要求面试者写一个最简单的 ...
- windows 2003 远程登录时如何修改管理员密码
今天买的vps,需要修改密码.但是自己不会,看网上好多人都说是,按ctrl+alt+del .但是我试过之后发现不对,后来又找到说是使用ctrl+alt+end 更改密码就可以了. 千万不要通过那个 ...
- [Practical Git] Compare file changes with git diff
It can be helpful to see the changes between two sets of code; git diff lets us do this by comparing ...
- android中设置快捷键方法setShortcut参数的说明
setShortcut之所以要两个参数来设定两个快捷键是为了应对不同的手机键盘.数字快捷键为12键键盘(0~9,*,#,共12个按键)所准备的.其实我怀疑有这种键盘的手机能装Android么?因为我的 ...
- mysql服务器辅助选项
查看控制台命令行前缀 : echo $PS1 ,例如输出 '[\u@\h \w]# 其中,\u是用户名,\h是主机名称: hostname -s 可以查看当前主机名, hostname 'z ...
- mvc_ajax_for form
在上一篇介绍MVC中的Ajax实现方法的时候,曾经提到了除了使用Ajax HTML Helper方式来实现之外,Jquery也是实现Ajax的另外一种方案. 通过get方法实现AJax请求 View ...
- 如何把 excel 设为文本格式?
选择要设置的单元格,右键选择 --- “设置单元格格式” --- 选 “ 分类 ” 下面的 “ 文本 ” --- 确定. 修改前: 修改后:
- Using zend-navigation in your Album Module
Using zend-navigation in your Album Module In this tutorial we will use the zend-navigation componen ...
- java 获取本机ip地址
/** * 取当前系统站点本地地址 linux下 和 window下可用 * * @return */ public static String getLocalIP() { String sIP = ...
- Java基础知识强化之网络编程笔记16:Android网络通信之 使用Http的Get方式读取网络数据(基于HTTP通信技术)
使用Http的Get方式读取网络数据,使用Get方式与网络通信是最常见的Http通信,建立链接之后就可以通过输入流读取网络数据. 详见:Android(java)学习笔记209:采用get请求提交数据 ...