javascript 闭包的理解(一)
过很多谈如何理解闭包的方法,但大多数文章,都是照抄或者解释《Javascript高级程序设计(第三版)》对于闭包的讲解,甚至例程都不约而同的引用高程三181页‘闭包与变量’一节的那个“返回数组各个项,结果各个项的值都相同”的例程,还有些文章的讲解过程上一步与下一步之间的跨度简直就是一步登天,让人反复看半天都无法理解。
闭包的理解需要很多概念做铺垫,包括变量作用域链、执行环境、变量活动对象、引用式垃圾内存收集机制等,如果对本文涉及的这些概念不理解,可以去找本《Javascript高级程序设计(第三版)》好好看看并理解这些概念。
我自己也是在反复编写一些例子并仔细认真的阅读和理解高程三与闭包相关的概念和知识后,终于对闭包有了一定的理解,为了检验学习效果,特别写篇文章以便检验自己是否真正理解和掌握。
在理解闭包之前,我们先来看些理解闭包容易忽略的小东西。
//例子1:
function foo(){
var a = 1;
return a;
};
console.log(foo()); //
上面的这个例子很好理解,一个函数如果return一个值,那么这个函数就可以当作return的值直接使用,无论它return的是一个引用类型(函数、数组)还是基本数据类型。
//例子2:
function foo(){ function foos(){
var b = 2;
return b;
} };
console.log(foos()); //Uncaught ReferenceError: foos is not defined
这个例子说明,如果一个函数B被声明在一个函数A的内部,那么在函数A外部,通过函数B的函数名直接调用函数B,是会出未定义错误的。你可以理解为:变量作用域规则中的局部作用域规则对于函数声明同样成立。
//例子3:
function foo(){ var a = 1; function foos(){
return a;//注意:返回的a是foo中定义的变量,而不是foos中定义的变量。
} console.log(foos());
}; foo();//
例子3说明foos只能在foo内部执行,这符合我们对于“变量作用域规则中的局部作用域规则,对于函数声明同样成立”的理解。
//例子4:
function foo(){
var arr = new Array();
for(var i=0;i<10;i++){
arr[i] = function(){
return i;
};
}
return function(){
for(var k = 0;k<arr.length;k++){
console.log(arr[k]());
}
};
}; console.log(foo()());//输出10个10;
例子4中中执行foo()以后得到的是foo自己返回的一个匿名函数(如下所示):
return function(){
for(var k = 0;k<arr.length;k++){
console.log(arr[k]());
}
};
那么,此时我们还需要执行一遍这个得到的匿名函数,才能得出最后的值(也就是:10个10),而执行这个得到的匿名函数(我们不妨叫它函数C)时,foo已经执行了一遍了,这个时候foo的变量i的值已经是10了,由于变量对象是通过引用赋值的,所以这个时候foo内的闭包函数(也就是引用了foo的变量i的匿名函数)再来引用foo的变量i,那么得到的值自然就是10了。这里的关键在与foo内的闭包函数执行的时机,如果闭包函数立即执行后再赋值给arr[i],那么就可以得到我们期望的值0到9了;
//例子5:
function foo(){
var arr = new Array(); for(var i=0;i<10;i++){ arr[i] = function(){
return i;
}();
}
return function(){
for(var k = 0;k<arr.length;k++){
console.log(arr[k]);
}
};
}; console.log(foo()());//0到9
关于例子4更详细的解释:
在例子4中,根据javascript的引用垃圾收集机制(这个机制的规则是:存在大于0个引用的变量不应该被销毁),虽然此时函数foo已经执行完了,但是foo的这个变量i因为被匿名函数(我们不妨叫它函数B)引用,所以这个i还是没有被垃圾收集程序销毁,那么这个i此时存在哪里呢?肯定不是存在于函数foo的执行环境里面,因为执行环境创建和销毁的规则是:当一个函数创建时,则创建这个函数的执行环境,一旦这个函数执行完毕则马上销毁这个执行环境。因此这个i只可能存在于foo内部的匿名函数(函数B)的执行环境中,这个函数B的执行环境中不但有自己的作用域和自己的变量对象,而且包含了其外部函数foo的作用域和foo的变量对象,变量i就存在于函数B包含的foo的作用域中和变量对象中。由于javascript不能直接操作内存空间,所以javascript只能以引用的方式访问变量对象,当foo的变量i的值已经变成10了的时候,再去执行foo的闭包函数(函数B),那么此时对i的引用自然会得到10。
使用以上实验得出的原理,我们还可以很容易的理解《Javascript高级程序设计(第三版)》181页最下面的那个例子:
//《Javascript高级程序设计(第三版)》181页最下面的那个例子: function createFunctions(){
var result = new Array(); for(var i=0;i<10;i++){
result[i] = function(num){
return function(){
return num;
}
}(i);//这里是将参数传给该匿名函数,并且立即执行该匿名函数的写法;
}
return result;
} for(var i=0;i<10;i++){
console.log(createFunctions()[i]());
}//0,1,2,3,4,5,6,7,8,9
同时,我们可以很容易的改写这个例子:
function createFunctions(){
var result = new Array(); for(var i=0;i<10;i++){
result[i] = function(){
return function(){
return i;
}()//这里添加了立即执行
}();//这里是将参数传给该匿名函数,并且立即执行该匿名函数的写法;
}
return result;
} for(var i=0;i<10;i++){
console.log(createFunctions()[i]);//这里去掉了一对括号
}//0,1,2,3,4,5,6,7,8,9
这个改写的例子同时证明,最内层的匿名函数还是可以引用最外面全局函数的变量。
下面这张图片中的例子,使用闭包就可以很好的理解了:
要是实在不理解,还可以试着这样来验证一下帮助理解:
function foo(){ var diss = "mem_count"; return function(){ return diss; } } var fo = foo(); var as = fo();
//undefined
as
//"mem_count"
var as2 = fo();
//undefined
as2
//"mem_count"
fo()
//"mem_count"
fo()
//"mem_count"
fo()
//"mem_count"
如果这样还是没办法理解,那真是应该先去看看变量的作用域、垃圾回收机制,函数表达式等基础知识了。
javascript 闭包的理解(一)的更多相关文章
- javascript闭包的理解
闭包是Javascript的一个难点,但也是一个很重要的知识点. 1.首先我们要知道变量作用域链 变量的作用域分两种:全局变量和局部变量.没有定义到任何函数中的变量为全局变量,在函数中定义的变量为局部 ...
- 我对 javascript 闭包的理解
学js的学到闭包,但是理解不深. 后来看了一下这篇文章: 地址:http://leepiao.blog.163.com/blog/static/4850313020112835355917/ 内容如下 ...
- 对JavaScript闭包的理解
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 在开始了解闭包前我们必须要先理解JavaScript的变量作用域. 一.变量的作用域无非就是两 ...
- 关于Javascript 闭包的理解
一.什么是闭包? 官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话 ...
- 关于Javascript闭包的理解
以下内容属个人理解,如有看不明白或漏洞之处,纯属水平不佳,还望见谅. 关于闭包,高程里的定义是:指有权访问另一个函数作用域中的变量的函数.创建闭包最常见的方法就是在一个函数的内部再创建一个函数. 这里 ...
- javascript闭包的理解和实例
所谓闭包,值得是词法表示包括不必要计算的变量的函数,也就是说,该函数可以使用函数外定义的变量. 顺便提示一下: 词法作用域:变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通 ...
- Javascript闭包简单理解
提到闭包,想必大家都早有耳闻,下面说下我的简单理解.平时写代码.第三方框架和组件都或多或少用到了闭包.所以,了解闭包是非常必要的.呵呵... 一.什么是闭包简而言之,就是能够读取其他函数内部变量的函数 ...
- javascript 闭包的理解(二)
// 定义一个User构造函数 function User(properties){ //遍历对象属性,确保它作用域正确 for(var i in properties){ (function(whi ...
- javascript 闭包的理解
1 需要明白概念: 执行环境 变量对象,活动对象 作用域,作用域链 闭包 垃圾处理机制 闭包陷阱
随机推荐
- MySQL命令行分号无法结束问题解决
背景:输入一串查询语句,以分号结束,发现没有结束,再打回车,分号,还是不完.什么exit,quit,bye,都不顶用如果要ctrl+C吧,又得退出mysql,一切重来,很麻烦.后来终于发现,引起这种现 ...
- deepin 安装golang protobuf
1.安装库文件protobuf,地址:https://github.com/protocolbuffers/protobuf/releases 我电脑是deepin 64位的,所以我直接下载https ...
- mysql 连接慢的问题
现象: 今发现站点訪问数据库变慢,经查,查询数据库非常快,连接数据库比較耗时. 解决的方法: 在mysql的配置文件my.cnf中,在[mysqld]以下加上这个配置就能够了. 附录:[mysqld] ...
- oracle函数 nls_charset_name(n1)
[功能]返回字符集名称参应id值 [参数]n1,数值型 [返回]字符型 sql> select nls_charset_name(852) from dual; nls_char ------- ...
- 设置 Tomcat 的JVM运行内存
win7,64位: Tomcat7.0.5:jdk1.7: 情况一:Tomcat注册成系统服务,如何修改JVM运行内存? WINDOW 64位 , cmd打开注册表(regedit) HKEY_LOC ...
- jq获取单选框、复选框、下拉菜单的值
1.<input type="radio" name="testradio" value="jquery获取radio的值" /> ...
- Ext--Layout(布局)
EXT中的布局,常用的有border.column.fit.form.tabel这几种. Fit布局,子元素将自动填满整个父容器(对元素设置宽度无效),如果容器组件中有多个子元素,则只会显示第一个子元 ...
- H3C TCP连接的拆除
- SuperSocket 中的日志系统
当 SuperSocket boostrap 启动时,日志系统将会自动启动. 所以你无须创建自己的日志工具,最好直接使用SuperSocket内置的日志功能. SuperSocket 默认使用log4 ...
- JavaScript 全国级省市县联动
<div class="right_content clearfix"> <h3 class="common_title2">收货地址& ...