用最通俗易懂的代码帮助新手理解javascript闭包 推荐
2012-03-01 22:44
661 查看
最近看了几篇有关javascript闭包的文章,包括最近正火的汤姆大叔系列,还有《javascript高级程序设计》中的文章,……我看不懂,里面有些代码是在大学教科书中看都没看过的,天书一般。幸好最近遇到两本好书《ppk on javascript》和《object-oriented JavaScript》,正字阅读中,后者还没有中文版,但前者还是建议看原版,写的不复杂,有兴趣的朋友可以看看,适合想进阶的朋友。
今天就结合这两本书,用最浅显的语言和最通俗的方式谈谈javascript中的闭包,因为也是新手,所以有有误的地方请各位指出,谢谢
一. 准备知识
1.函数作为函数的参数
在学习javascript中,你始终要有一个有学习与其他语言不同的概念:函数(function)不么特殊的东西,它也是一种数据,与bool ,string,number没有什么两样。
函数的参数可以string,number,bool如:
function(a, b) {return a + b;}
但同样也可以传入函数。对你没有听错,函数的参数是函数!加入你有以下两个函数:
复制代码 代码如下:
//把三个数翻一倍
function multiplyByTwo(a, b, c) {
var i, ar = [];
for(i = 0; i < 3; i++) {
ar[i] = arguments[i] * 2;
}
return ar;
}
复制代码 代码如下:
//把数加一
function addOne(a) {
return a + 1;
}
然后这么使用
复制代码 代码如下:
var myarr = [];
//先把每个数乘以二,用了一个循环
myarr = multiplyByTwo(10, 20, 30);
//再把每个数加一,又用了一个循环
for (var i = 0; i < 3; i++) {myarr[i] = addOne(myarr[i]);}
要注意到其实这个过程用了两个循环,还是有提升的空间的,不如这么做:
复制代码 代码如下:
function multiplyByTwo(a, b, c, addOne) {
var i, ar = [];
for(i = 0; i < 3; i++) {
ar[i] = addOne (arguments[i] * 2);
}
return ar;
}
这样就把函数当做参数传递进去了,并且在第一个循环中直接调用。这样的函数就是著名的回调函数(Callback function)
2.函数作为返回值
在函数中可以有返回值,但是我们一般都熟悉数值的返回,如
复制代码 代码如下:
function ex(){
return 12
}
但你一旦意识到函数只是一种数据的话,你就可以想到同样可以返回函数。注意看下面这个函数:
复制代码 代码如下:
function a() {
alert('A!');
return function(){
alert('B!');
};
}
它返回了一个弹出”B!”的函数。接下来使用它:
复制代码 代码如下:
var newFunc = a();
newFunc();
结果是什么呢?首先执行a()的时候,弹出”A!”,此时newFunc接受了a的返回值,一个函数――此时newFunc就变成了那个被a返回的函数,再执行newFunc时,弹出”B!”
3.javascript的作用域
javascript的作用域很特别,它是以函数为单位的,而不是像其他语言以块为单位(如一个循环中),看下面这个例子:
var a = 1; function f(){var b = 1; return a;}
如果你此时试图想得到b的值:在firebug中试图输入alert(b)的话,你会得到错误提示:
b is not defined
为什么你可以这么理解:你所在的编程环境或者窗口是最顶级的一个函数,好像一个宇宙,但是b只是在你内部函数的一个变量,宇宙中的小星球上的一个点,你很难找到它,所以在这个环境中你不能调用它的;反之这个内部函数可以调用变量a,因为它暴露在整个宇宙中,无处藏身,同时也可以调用b,因为它就在自己的星球上,函数内部。
就上面这个例子说:
在f()外,a可见,b不可见
在f()内,a可见,b也可见
再复杂点:
复制代码 代码如下:
var a = 1; //b,c在这一层都不可见
function f(){
var b = 1;
function n() { //a,b,c对这个n函数都可以调用,因为a,b暴露在外,c又是自己内部的
var c = 3;
}
}
问你,函数b可以调用变量c吗?不行,记住javascript的作用域是以函数为单位的,c在n的内部,所以对f来说是不可见的。
function f(){
var b = "b";
return function(){ //没有名字的函数,所以是匿名函数
return b;
}
}
注意返回的函数可以访问它父亲函数中的变量b
此时如果你想取b的值,当然是undefined
但是如果你这么做:
复制代码 代码如下:
var n = f();
n();
你可以取到b的值了!虽然此时n函数在f的外面,b又属于f内部的变量,但是f内部出了一个内鬼,返回了b的值……
现在大家有点感觉了吧
闭包2:
复制代码 代码如下:
var n;
function f(){
var b = "b";
n = function(){
return b;
}
}
如果此时调用f会怎么样?那就生成了一个n的全局范围函数,但是它却能访问f的内部,照样返回b的值,与上面有异曲同工之妙!
闭包3:
你还可以用闭包访问函数的参数
复制代码 代码如下:
function f(arg) {
var n = function(){
return arg;
};
arg++;
return n;
}
此时如果使用:
复制代码 代码如下:
var m = f(123);
m();
结果是124
因为此时f中返回的匿名函数经过了两道转手,先给n,再赋给外面的m,但本质没有变,把定义时父函数的参数返回了
闭包4:
复制代码 代码如下:
var getValue, setValue;
function() {
var secret = 0;
getValue = function(){
return secret;
};
setValue = function(v){
secret = v;
};
})
运行:
复制代码 代码如下:
getValue()
0
setValue(123)
getValue()
123
这个就不用解释了吧,如果你有面向对象语言基础的话(如C#),这里的getValue和setValue就类似于一个对象的属性访问器,你可以通过这两个访问器来赋值和取值,而不是能访问其中内容
其实书中还有几个闭包的例子,但是原理用上面四个就足够了,希望能起抛砖引玉的作用,给javascript进阶者对闭包有一个更深刻的理解
今天就结合这两本书,用最浅显的语言和最通俗的方式谈谈javascript中的闭包,因为也是新手,所以有有误的地方请各位指出,谢谢
一. 准备知识
1.函数作为函数的参数
在学习javascript中,你始终要有一个有学习与其他语言不同的概念:函数(function)不么特殊的东西,它也是一种数据,与bool ,string,number没有什么两样。
函数的参数可以string,number,bool如:
function(a, b) {return a + b;}
但同样也可以传入函数。对你没有听错,函数的参数是函数!加入你有以下两个函数:
复制代码 代码如下:
//把三个数翻一倍
function multiplyByTwo(a, b, c) {
var i, ar = [];
for(i = 0; i < 3; i++) {
ar[i] = arguments[i] * 2;
}
return ar;
}
复制代码 代码如下:
//把数加一
function addOne(a) {
return a + 1;
}
然后这么使用
复制代码 代码如下:
var myarr = [];
//先把每个数乘以二,用了一个循环
myarr = multiplyByTwo(10, 20, 30);
//再把每个数加一,又用了一个循环
for (var i = 0; i < 3; i++) {myarr[i] = addOne(myarr[i]);}
要注意到其实这个过程用了两个循环,还是有提升的空间的,不如这么做:
复制代码 代码如下:
function multiplyByTwo(a, b, c, addOne) {
var i, ar = [];
for(i = 0; i < 3; i++) {
ar[i] = addOne (arguments[i] * 2);
}
return ar;
}
这样就把函数当做参数传递进去了,并且在第一个循环中直接调用。这样的函数就是著名的回调函数(Callback function)
2.函数作为返回值
在函数中可以有返回值,但是我们一般都熟悉数值的返回,如
复制代码 代码如下:
function ex(){
return 12
}
但你一旦意识到函数只是一种数据的话,你就可以想到同样可以返回函数。注意看下面这个函数:
复制代码 代码如下:
function a() {
alert('A!');
return function(){
alert('B!');
};
}
它返回了一个弹出”B!”的函数。接下来使用它:
复制代码 代码如下:
var newFunc = a();
newFunc();
结果是什么呢?首先执行a()的时候,弹出”A!”,此时newFunc接受了a的返回值,一个函数――此时newFunc就变成了那个被a返回的函数,再执行newFunc时,弹出”B!”
3.javascript的作用域
javascript的作用域很特别,它是以函数为单位的,而不是像其他语言以块为单位(如一个循环中),看下面这个例子:
var a = 1; function f(){var b = 1; return a;}
如果你此时试图想得到b的值:在firebug中试图输入alert(b)的话,你会得到错误提示:
b is not defined
为什么你可以这么理解:你所在的编程环境或者窗口是最顶级的一个函数,好像一个宇宙,但是b只是在你内部函数的一个变量,宇宙中的小星球上的一个点,你很难找到它,所以在这个环境中你不能调用它的;反之这个内部函数可以调用变量a,因为它暴露在整个宇宙中,无处藏身,同时也可以调用b,因为它就在自己的星球上,函数内部。
就上面这个例子说:
在f()外,a可见,b不可见
在f()内,a可见,b也可见
再复杂点:
复制代码 代码如下:
var a = 1; //b,c在这一层都不可见
function f(){
var b = 1;
function n() { //a,b,c对这个n函数都可以调用,因为a,b暴露在外,c又是自己内部的
var c = 3;
}
}
问你,函数b可以调用变量c吗?不行,记住javascript的作用域是以函数为单位的,c在n的内部,所以对f来说是不可见的。
开始正式谈闭包:
首先看这个图:
假设G,F,N 分别代表三个层次的函数,层次如图所示,a,b,c分别是其中的变量。根据上面谈到的作用域,我们有如下结论:
- 如果你在a点,你是不可以引用b的,因为b对你是不可见的
- 只有c可以引用b
闭包的吊诡之处的就在于发生了如下情况:
N突破了F的限制!跑到于a同一层了!因为函数只认它们在定义时所处的环境(而不是执行时,这点很重要),N中的c仍然可以访问b!此时的a还是不可以访问b!
但是这是怎么实现的呢?如下:
闭包1:
function f(){
var b = "b";
return function(){ //没有名字的函数,所以是匿名函数
return b;
}
}
注意返回的函数可以访问它父亲函数中的变量b
此时如果你想取b的值,当然是undefined
但是如果你这么做:
复制代码 代码如下:
var n = f();
n();
你可以取到b的值了!虽然此时n函数在f的外面,b又属于f内部的变量,但是f内部出了一个内鬼,返回了b的值……
现在大家有点感觉了吧
闭包2:
复制代码 代码如下:
var n;
function f(){
var b = "b";
n = function(){
return b;
}
}
如果此时调用f会怎么样?那就生成了一个n的全局范围函数,但是它却能访问f的内部,照样返回b的值,与上面有异曲同工之妙!
闭包3:
你还可以用闭包访问函数的参数
复制代码 代码如下:
function f(arg) {
var n = function(){
return arg;
};
arg++;
return n;
}
此时如果使用:
复制代码 代码如下:
var m = f(123);
m();
结果是124
因为此时f中返回的匿名函数经过了两道转手,先给n,再赋给外面的m,但本质没有变,把定义时父函数的参数返回了
闭包4:
复制代码 代码如下:
var getValue, setValue;
function() {
var secret = 0;
getValue = function(){
return secret;
};
setValue = function(v){
secret = v;
};
})
运行:
复制代码 代码如下:
getValue()
0
setValue(123)
getValue()
123
这个就不用解释了吧,如果你有面向对象语言基础的话(如C#),这里的getValue和setValue就类似于一个对象的属性访问器,你可以通过这两个访问器来赋值和取值,而不是能访问其中内容
其实书中还有几个闭包的例子,但是原理用上面四个就足够了,希望能起抛砖引玉的作用,给javascript进阶者对闭包有一个更深刻的理解
您可能感兴趣的文章:
- javascript闭包的高级使用方法实例
- JavaScript自执行闭包的小例子
- 深入Javascript函数、递归与闭包(执行环境、变量对象与作用域链)使用详解
- 谈谈JavaScript中的函数与闭包
- 深入理解JavaScript 闭包究竟是什么
- JavaScript中的作用域链和闭包
- javascript学习笔记(十三) js闭包介绍(转)
- Javascript 闭包引起的IE内存泄露分析
- 深入理解JavaScript系列(16) 闭包(Closures)
- JavaScript 高级篇之闭包、模拟类,继承(五)
- JavaScript高级程序设计 读书笔记之八 Function类及闭包
- javaScript 利用闭包模拟对象的私有属性
- JavaScript闭包 懂不懂由你反正我是懂了
- JavaScript 匿名函数(anonymous function)与闭包(closure)
- javascript的闭包介绍(司徒正美)
- javascript 闭包
- Javascript闭包演示代码小结
- 基于javascript 闭包基础分享
相关文章推荐
- 用最通俗易懂的代码帮助新手理解javascript闭包 推荐
- 用最通俗易懂的代码帮助新手理解javascript闭包
- 批处理代码【watch】 (无聊之作)帮助新手理解基本循环命令
- 通俗易懂的理解javascript闭包
- 复制所有属性进行继承 转自 博客园 汤姆大叔 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 利用eclipse中的各种功能帮助你理解代码
- 深入理解Javascript闭包 新手版
- 虚函数表测试代码 帮助理解虚函数表
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 理解梯度下降,随机梯度下降,附电影推荐系统的简单代码小样 1.
- ES6中的面向对象class,对新手学习理解大有帮助。
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- Handler伪代码帮助理解
- (C语言版)链表(一)——实现单向链表创建、插入、删除等简单操作(包含个人理解说明及注释,新手跟着写代码)
- 新手帮助:C#高级编程(第7版)中弱事件模式的例子的理解
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- activeMQ消息队列之JMS基础(附了一些代码帮助理解前期可跳过)
- 深入理解JavaScript:代码复用模式(推荐篇)
- 指针与数组的示例代码(帮助理解)
- 最好自己手动敲下代码,对理解有很大帮助。