《JAVASCRIPT高级程序设计》闭包
一、闭包的概念
闭包是JAVASCRIPT中最重要的概念之一,闭包是指有权访问另一个函数作用域中变量的函数;创建闭包常见的方式,就是在一个函数内部,创建另一个函数。以下的例子创建了一个闭包,加粗的两行代码访问了外部函数中的变量propertyName,即使这个内部函数被返回后在其他地方调用,它仍然可以访问这个变量。
function createComparison(propertyName){ return function(object1, object2){ var value1 = object1[propertyName]; var value2 = object2[propertyName]; if(value1 > value2){ return 1; }else if(value1 < value2){ return -1; }else{ return 0; } }; }
一般来说,当函数执行完毕后,局部活动对象就会被销毁,内存中只保留全局作用域,但是闭包的情况有所不同。要弄清楚其中的细节,必须彻底弄清楚函数在第一次调用时都发生了什么。
当某个函数被调用时,会创建一个执行环境以及相应的作用域链,并把作用域链赋值给一个特殊的内部属性[[scope]];然后用this, arguments和其他命名参数的值来初始化函数的活动对象。在作用域链中,外部函数的活动对象处于第二位,外部函数的外部函数的活动对象处于第三位……一直到作为作用域终点的全局执行环境。
所以,在上面的例子中,当创建compare函数时,会创建一个包含全局变量的作用域链,并保存在内部的[[scope]]属性中;当调用compare函数时,会创建一个执行环境,并复制[[scope]]属性中保存的作用域链。当访问一个变量时,在作用域链中从前往后搜索;一般来说,当函数执行完毕后,局部活动对象就会被销毁,内存中只保留全局作用域,但是对于例子中的闭包,当它返回后,它的作用域链被初始化为包含函数的活动对象和全局变量对象。因此,返回的匿名函数可以返回所有的变量。另外,在createComparision执行完毕后,其活动对象也不会销毁,因为匿名函数的作用域链仍然在引用这个活动对象,直到匿名函数也被销毁,如下例:
// 创建闭包(函数) var compareNames = createComparison("name"); // 调用闭包(函数) var result = compareNames({name:"Lillian"},{name:"Matthew"}); // 解除对闭包(函数)的引用,以便安全释放内存 compareNames = null;
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存,因此过度使用闭包可能导致内存占用过多,建议在绝对必要时,再考虑使用闭包。
二、副作用以及解决方法
1、变量
作用链机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何一个变量的最后一个值。
function createFunctions(){ var result = new Array(); for(var i = 0; i < 10; i++){ result[i] = function(){ return i; }; } return result; } var returnFunctions = createFunctions(); for(var i = 0; i< 10; i++){ console.log(returnFunctions[i]()); } // 结果是10个10
解决方法:不直接赋值给闭包,而是定义匿名函数并立即执行,将匿名函数的结果赋值给数组;这个匿名函数自身也是一个闭包,不过返回的值是变量i创建时的值。
function createFunctions(){ var result = new Array(); for(var i = 0; i < 10; i++){ result[i] = function(num){ return function(){ return num; }; }(i); } return result; } var returnFunctions = createFunctions(); for(var i = 0; i< 10; i++){ console.log(returnFunctions[i]()); } // 结果是0,1,2,3,4,5,6,7,8,9
2、this
我们知道,在全局环境中,this等于windows对象,而当函数被作为某个对象的方法调用时,this等于那个对象。但是在闭包中,每个函数被调用时,其活动对象都会自动取得两个特殊的变量,this和arguments。内部函数在搜索这两个变量时,只搜索到活动对象为止,因此永远不可能访问外部函数中的变量。如下例所示:
var name = "The Windows"; var object = { name :"my Object", getNameFun:function(){ return function(){ return this.name; }; } }; alert(object.getNameFun()()); //"The Windows"
我们可以通过以下这种方式来避免这个问题:
var name = "The Windows"; var object = { name :"my Object", getNameFun:function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFun()()); //"my Object"
3、内存泄露
闭包可能会带来内存泄露问题:由于闭包会引用包含函数的整个活动对象,如果闭包的作用域链中保存着一个html对象,那么久意味着该元素无法被销毁(活动对象至少保存着一个引用,通过引用计数来销毁内存的话,引用计数不为0,则无法销毁)。
三、模仿块级作用域
JAVASCRIPT没有块级作用域的概念,这就意味着在块语句中定义的变量,实际是保存在函数中而非语句中的。
function outputNumbers(count){ for(var i = 0; i< count; i++){ } alert(i); // 不会报错,由于没有块级作用域,for循环结束后,i并不会被销毁 }
我们函数声明转化成函数表达式,来模仿块级作用域:
(function(){ // 这里是块级作用域 })();
在一个由很多开发人员共同参与的大型应用程序中,过多的全局变量和函数很容易导致命名冲突。通过这种方式来创建私有作用域,可以很好的避免这个问题:
function outputNumbers(count){ (function(){ for(var i = 0; i< count; i++){ } })(); alert(i); // 报错,i没有定义 }
四、函数的定义
说了这么多,我们再来回顾一下函数的定义。定义函数的方式有两种,一种是函数声明,一种是函数表达式。其中,函数声明的一个重要特征是函数声明提升,这意味着可以把函数声明放在函数调用的后面;而函数表达式没有这种特性。
// 正确 sayHi(); function sayHi(){ alert("Hi"); } // 报错 sayHello(); var sayHello = function(){ alert("helllo"); };
《JAVASCRIPT高级程序设计》闭包的更多相关文章
- JavaScript高级程序设计——闭包
前言 有很多人搞不清匿名函数和闭包这两个概念,经常混用.闭包是指有权访问另一个函数作用域中的变量的函数.匿名函数就是没有实际名字的函数. 闭包 概念 闭包,其实是一种语言特性,它是指的是程序设计语言中 ...
- 《JavaScript高级程序设计(第3版)》阅读总结记录第一章之JavaScript简介
前言: 为什么会想到把<JavaScript 高级程序设计(第 3 版)>总结记录呢,之前写过一篇博客,研究的轮播效果,后来又去看了<JavaScript 高级程序设计(第3版)&g ...
- 读javascript高级程序设计00-目录
javascript高级编程读书笔记系列,也是本砖头书.感觉js是一种很好上手的语言,不过本书细细读来发现了很多之前不了解的细节,受益良多.<br/>本笔记是为了方便日后查阅,仅作学习交流 ...
- 读javascript高级程序设计-目录
javascript高级编程读书笔记系列,也是本砖头书.感觉js是一种很好上手的语言,不过本书细细读来发现了很多之前不了解的细节,受益良多.<br/>本笔记是为了方便日后查阅,仅作学习交流 ...
- 读书笔记(03) - 性能 - JavaScript高级程序设计
作用域链查找 作用域链的查找是逐层向上查找.查找的层次越多,速度越慢.随着硬件性能的提升和浏览器引擎的优化,这个慢我们基本可以忽略. 除了层级查找损耗的问题,变量的修改应只在局部环境进行,尽量避免在局 ...
- 《Javascript高级程序设计》阅读记录(七):第七章
<Javascript高级程序设计>中,2-7章中已经涵盖了大部分精华内容,所以摘录到博客中,方便随时回忆.本系列基本完成,之后的章节,可能看情况进行摘录. 这个系列以往文字地址: < ...
- JavaScript高级程序设计第三版.CHM【带实例】
从驱动全球商业.贸易及管理领域不计其数的复杂应用程序的角度来看,说 JavaScript 已经成为当今世界上最流行的编程语言一点儿都不为过. JavaScript 是一种非常松散的面向对象语言,也是 ...
- javascript高级程序设计学习笔记
javascript高级程序设计,当枕头书已经好久了~zz 现在觉得自己在js的开发上遇到了一些瓶颈,归根究底还是基础太薄弱,所以重新刷一遍js高程希望有更新的认识. 一.javascript简介 ...
- JavaScript高级程序设计(读书笔记)之函数表达式
定义函数的方式有两种:一种是函数声明,另一种就是函数表达式. 函数声明的一个重要特征就是函数声明提升(function declaration hoisting),意思是在执行代码前会先读取函数声明. ...
- 《JavaScript高级程序设计(第3版)》笔记-序
很少看书,不喜欢看书,主要是上学时总坐不住,没有多大定性,一本书可以两天看完,随便翻翻,也可以丢在角落里几个月不去动一下. 上次碰到了<JavaScript高级程序设计(第3版)>感觉真的 ...
随机推荐
- UWSGITOP-----监控uwsgi 性能
启动 uwsgi -x etc/bfdds_cookiemapping_conf.xml --stats /tmp/stats.socket 查看 uwsgitop /tmp/stats.socket ...
- 电子工程师名片——FAT16文件系统(转)
源:电子工程师名片——FAT16文件系统 从8月8号开始,连续一个月利用每天下班时间和周末的时间终于初步完成了一个电子工程师的电路板名片,就像U盘一样,不过这个FLASH只有64KB的大小,用的单片机 ...
- javascript books
脑子里突然冒出这么一句话: “反射 是一种 代理!!!”,javascript ECMScript 6中也开始实现 Reflect 了. php的动态代理, 反射方面,在很多框架中都用到了.也是很多框 ...
- AFNetWorking发送post请求,Code=-1016错误
使用AFNetWorking发送post请求时,可能会出现下面Code=-1016问题.打印的error如下: Error:Error Domain=com.alamofire.error.seria ...
- box2d中的物理世界
box2d中的物理世界,即b2World类就是一个包含了各种物体(body,物理体,或者叫刚体),固定附着物(fixture,形状与物理体的绑定物)以及各种约束体(比如关节),并使其在当中完成各种交互 ...
- 【Xilinx-Petalinux学习】-05-OpenCV程序测试
占位, 通过上一次编译的opencv库,运行程序,实现图像处理
- 三星note4,微信公众号开发,页面闪退
在做微信公众号开发时,使用三星note4测试,有一个select框闪退的问题出现. 出现该问题的原因是该选择框,设置了appearance:none;属性.查找了官方解释是: 所有主流浏览器都不支持 ...
- Zookeeper的基本概念
Reference: http://mp.weixin.qq.com/s?src=3×tamp=1477979201&ver=1&signature=bBZqNrN ...
- linux 套接字编程入门--Hello World
下述代码是linux套接字编程的入门代码.分为服务端和客户端源码. 服务端代码的主要流程是绑定ip地址和端口号建立套接字,等待客户端发起访问.接受客户端请求之后,向客户端发送字符串"hell ...
- IOS web app一些实用的属性设置
IOS对safari私有的属性很多,虽然很多不为人知但是却很实用.掌握好这些属性对web app和混合app的开发会很有帮助. 1.format-detection[telephone=no] 是否自 ...