《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版)>感觉真的 ...
随机推荐
- flex中form表单中子元素之间的距离控制
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.ado ...
- 关于iOS自定义控件:在view上实现事件和代理
自定义控件.h #import <UIKit/UIKit.h> #import "PPViewtouchesBeginDelegate.h" @interface PP ...
- CodeForces 620C Pearls in a Row
水题,每当出现重复就分割开来,最后留下的尾巴给最后一段 #include<cstdio> #include<cstring> #include<cmath> #in ...
- UILabel的抗压缩、抗拉伸、以及控件的约束简述
今天来说一说UILabel的约束设置问题 首先主要介绍:Priority(控件约束的优先级).Content Hugging Priority(控件抗拉伸优先级).Content Compressio ...
- OpenStack - liberty CentOS 7
OpenStack私有云部署 Controller Node: em1(10.6.17.11),em2() Computer Node: em1(10.6.17.12),e ...
- Apache2 MPM 模式了解
一.MPM MPM(Multi-Processing Module (MPM) implements a hybrid multi-process multi-threaded server)是Apa ...
- POJ 3187 Backward Digit Sums
暴力DFS+验证. 验证如果是暴力检验可能复杂度会太高,事实上可以o(1)进行,这个可以o(n*n)dp预处理. #include<cstdio> #include<cstring& ...
- 在Android studio环境下使用EventBus
EventBus是一个订阅/发布消息总线,实现在应用程序里面,组件之间,线程之间的通信.因为event是任意的类型,所以这个使用起来非常方便. eventbus中的角色: event:当然就是事件啦 ...
- Android L(5.0)源码之手势识别onTouchEvent
onTouchEvent同样也是在view中定义的一个方法.处理传递到view 的手势事件.通过MotionEvent的getAction()方法来获取Touch事件的类型,类型包括ACTION_DO ...
- DateFormat 竟然是非线程安全的?!!!!!
今天撸代码忽然发现一个奇怪的一场抛出,经过一番排查发现有可能DateFormat 的多线程问题造成的,网上一查DateFormat竟然非线程安全.那我原先的代码...(细思极恐)