学习Javascript闭包(Closure)
2016-03-29 15:28
295 查看
《javascript高级程序设计》(第三版)第7章第2节:
闭包是指有权访问另一个函数作用域中的变量函数。
《javascript权威指南》 (第六版)第8章第6节:
从技术的角度讲,所有的JavaScript函数都是闭包:它们都是对象,它们都关联到作用域链。
或者看网上教程:
JavaScript 秘密花园
闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。
我理解的闭包定义:闭包就是一个访问父函数局部变量的函数。当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。
作用:1、读取函数内部的变量。2、让这些变量的值始终保持在内存中。有时候我们需要一个模块中定义这样一个变量:希望这个变量一直保存在内存中但又不会“污染”全局的变量,这个时候,我们就可以用闭包来定义这个模块。
如果你看了上述定义和理论一头雾水(几乎是肯定的),那么来看代码。
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
如何从外部读取局部变量?
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
f2函数,就是闭包。
重复一段上面的话,结合代码来理解:我理解的闭包定义:闭包就是一个访问父函数局部变量的函数。当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。
示例代码1
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
示例代码2
count是A中的一个变量,它的值在B中被改变,函数B每执行一次,count的值就在原来的基础上累加1。因此,A中的count一直保存在内存中。
举一个实际的例子;你有一个比价类型的应用,需要实时的获取t1,t2两个网站上商品的价格信息。你的核心代码大概是这样的。
首先想到的就是优化part3,将显示的部分抽离成一个单独的函数
这个函数必须带有参数site,表明price是来自哪个网站的。
人一切开始优化代码了,就会上瘾,part3可以做成函数,part1,part2为什么不也做成函数咧,
到这个时候,你就会发现query_t*这种函数非常没有意思,除了t*各不相同,里面的内容完全一样。这不像人类写的代码,好像机器写的代码。
有更好的办法吗?
这里面,$.get的第二参数,是一个匿名函数,它使用query函数的局块变量site.就形成一个闭包。这个匿名函数的具体功能,根据site参数的不同而不同。也可以理解为query函数调用时会根据site参数,生成它相应的匿名函数。
总结一下,闭包给了js函数生成函数的能力,增加了js代码的抽象能力。
另外,js中function即可以用于函数定义,又可以用于类的定义,这样我们就可以通过闭包,来实现生成类的类。也就是说类的实例是一个类。
闭包是指有权访问另一个函数作用域中的变量函数。
《javascript权威指南》 (第六版)第8章第6节:
从技术的角度讲,所有的JavaScript函数都是闭包:它们都是对象,它们都关联到作用域链。
或者看网上教程:
JavaScript 秘密花园
闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。
我理解的闭包定义:闭包就是一个访问父函数局部变量的函数。当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。
作用:1、读取函数内部的变量。2、让这些变量的值始终保持在内存中。有时候我们需要一个模块中定义这样一个变量:希望这个变量一直保存在内存中但又不会“污染”全局的变量,这个时候,我们就可以用闭包来定义这个模块。
如果你看了上述定义和理论一头雾水(几乎是肯定的),那么来看代码。
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
var n=999; function f1(){ alert(n); } f1(); // 999</span>另一方面,在函数外部自然无法读取函数内的局部变量。这里在alert(n);前面加f1();也是一样的。
function f1(){ var n=999; } al 4000 ert(n); // error</span>这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
function f1(){ n=999; } f1(); alert(n); // 999</span>
如何从外部读取局部变量?
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
function f1(){ var n=999; function f2(){ alert(n); // 999 } }
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999
f2函数,就是闭包。
重复一段上面的话,结合代码来理解:我理解的闭包定义:闭包就是一个访问父函数局部变量的函数。当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。
示例代码1
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000</span>在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
示例代码2
function A(){ var count = 0; function B(){ count ++; console.log(count); } return B; } var c = A(); c();// 1 c();// 2 c();// 3</span>
count是A中的一个变量,它的值在B中被改变,函数B每执行一次,count的值就在原来的基础上累加1。因此,A中的count一直保存在内存中。
举一个实际的例子;你有一个比价类型的应用,需要实时的获取t1,t2两个网站上商品的价格信息。你的核心代码大概是这样的。
//version 0.1 function query_t1(item) { var api_url_t1; ...part1 //根据item生成请求api地址, api_url_t1 $.get(api_url_t1, function(data) { var price ...part2 //解析data数据,提取price信息。 ...part3 //使用price信息。将价格显示在t1对应的位置 }) } function query_t2(item) { //和query_t2类似 } </span>如果只需要你取两个网站的数据,这样写还算ok。慢慢的,你发现在产品经理的要求下,你要获取网站越来越多,你不断的复制自己的代码 。代码中有大量重复的部分,维护也变得复杂。比如,你调整一个price的显示方式,所有query_t*都需要对应地方的修改。你开始厌倦,想减少那些重复的代码。
首先想到的就是优化part3,将显示的部分抽离成一个单独的函数
//version 0.2 function show_price(site,price) { ... //使用price信息。将价格显示在site对应的位置 } function query_t1(item) { var api_url_t1; ...part1 //根据item生成请求api地址, api_url_t1 $.get(api_url_t1, function(data) { var price ...part2 //解析data数据,提取price信息。 show_price("t1",price) }) } ... </span>
这个函数必须带有参数site,表明price是来自哪个网站的。
人一切开始优化代码了,就会上瘾,part3可以做成函数,part1,part2为什么不也做成函数咧,
//version 0.3 //////////基础函数///////// function show_price(site,price) { ... //使用price信息。将价格显示在site对应的位置 } function get_api_url(site,item) { ... //根据item生成请求api地址,并返回 } function get_price(site,data) { ... //解析data数据,提取price信息,并返回 } ///////////具体查询函数/////////// function query_t1(item) { var api_url_t1= get_api_url("t1",item) $.get(api_url_t1, function(data) { var price = get_price("t1",data) show_price("t1",price) }) } function query_t2(item) { var api_url_t2= get_api_url("t2",item) $.get(api_url_t2, function(data) { var price = get_price("t2", data) show_price("t2",price) }) } ... </span>
到这个时候,你就会发现query_t*这种函数非常没有意思,除了t*各不相同,里面的内容完全一样。这不像人类写的代码,好像机器写的代码。
有更好的办法吗?
//version 0.4 ///////////具体查询函数/////////// function query(site,item) { var api_url = get_api_url(site,item) $.get(api_url, function(data) { var price = get_price(site,data) show_price(site,price) }) } </span>
这里面,$.get的第二参数,是一个匿名函数,它使用query函数的局块变量site.就形成一个闭包。这个匿名函数的具体功能,根据site参数的不同而不同。也可以理解为query函数调用时会根据site参数,生成它相应的匿名函数。
总结一下,闭包给了js函数生成函数的能力,增加了js代码的抽象能力。
另外,js中function即可以用于函数定义,又可以用于类的定义,这样我们就可以通过闭包,来实现生成类的类。也就是说类的实例是一个类。
相关文章推荐
- JavaScript头像上传插件源码分享
- angular js 写的部门树 有选中和 取消操作
- js设计模式-建造者模式
- Cordova从服务器更新客户端的JS文件
- JS实现子元素scroll父元素容器不跟随滚动
- ExtJS5 - 实现带周数的日期选择控件
- <%@ INCLUDE FILE=""%>与<JSP:INCLUDE PAGE=""/>区别
- jsp页面中html,javascript.css的执行顺序
- js事件模型
- Iframe 自适应高度(js)
- javaScript基础—imooc
- JS 日期格式化
- js解决movebox移动问题
- Javascript数组、json对象基本操作
- javascript中 .eq()的用法
- logcat 格式化输出json
- 使用js模拟i18n国际化的例子
- javascript中不易分清的slice,splice和split三个函数
- JavaScript正则表达式
- 分析js闭包引起的事件注册问题