《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版)>感觉真的 ...
随机推荐
- jvm内存设置及总结
http://dmouse.iteye.com/blog/1264118 jvm内存设置及总结 博客分类: java相关 Application情况: 大量使用了内存缓存,应用负载量较大,900w/d ...
- 6种炫酷的CSS3按钮边框动画特效
6种炫酷的CSS3按钮边框动画特效Button border animate 用鼠标滑过下面的按钮看看效果! Draw Draw Meet Center Spin Spin Circle Spin T ...
- (中等) HDU 1043 Eight,经典搜索问题。
Problem Description The 15-puzzle has been around for over 100 years; even if you don't know it by t ...
- P3197 [HNOI2008]越狱
题目描述 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种.如果相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱 输入输出格式 输入 ...
- Spring3.2新注解@ControllerAdvice
Spring3.2新注解@ControllerAdvice @ControllerAdvice,是spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强.让我们先看看@Control ...
- [cocos2d-x] --- CCNode类详解
Email : awodefeng@163.com 1 CCNode是cocos2d-x中一个很重要的类,CCNode是场景.层.菜单.精灵等的父类.而我们在使用cocos2d-x时,接触最多的就是场 ...
- 前段验证框架 formValidator
http://blog.csdn.net/lorinzhang/article/details/7283557
- RabbitMQ消息队列(九):Publisher的消息确认机制
在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...
- 公众号第三方平台开发-aes解密失败
公众号第三方平台开发-aes解密失败 问题:本地启动项目,配置域名,测试微信公众号,系统正常运行:将项目部署到测试环境执行同样的操作,系统报错,错误异常:aes解密失败..... 调试--寻找问题-- ...
- linux 安装jdk及tomcat指定jdk版本推荐
方法1:用yum命令安装 1. 查看当前jdk版本:Java –version,或者是:rpm -qa | grep jdk 2. 删除当前jdk:yum -y remove java-1.6 ...