作家
登录

用最通俗易懂的代码帮助新手理解javascript闭包 推荐

作者: 来源:www.28hudong.com 2013-03-30 00:32:15 阅读 我要评论

最近看了几篇有关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来说是不可见的。 开始正式谈闭包: 首先看这个图: 假设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进阶者对闭包有一个更深刻的理解

  推荐阅读

  nodejs入门详解(多篇文章结合)

Node.js是一套用来编写高性能网络服务器的JavaScript工具包,一系列的变化由此开始。比较独特的是,Node.js会假设你是在POSIX环境下运行它Linux 或 Mac OS X。如果你是在Windows下,那就需要安装MinGW以获得一个仿>>>详细阅读


本文标题:用最通俗易懂的代码帮助新手理解javascript闭包 推荐

地址:http://www.17bianji.com/kaifa2/JS/23575.html

关键词: 探索发现

乐购科技部分新闻及文章转载自互联网,供读者交流和学习,若有涉及作者版权等问题请及时与我们联系,以便更正、删除或按规定办理。感谢所有提供资讯的网站,欢迎各类媒体与乐购科技进行文章共享合作。

网友点评
自媒体专栏

评论

热度

精彩导读
栏目ID=71的表不存在(操作类型=0)