浅谈JavaScript中闭包
引言
闭包可以说是JavaScript中最有特色的一个地方,很好的理解闭包是更深层次的学习JavaScript的基础。这篇文章我们就来简单的谈下JavaScript下的闭包。
闭包是什么?
闭包是什么?通俗的解释是:有权访问另一个函数作用域中变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数(作为其子函数)。下面我们还是以前面的一个例子来简单介绍下:
//通过属性名称来对数组元素进行排序
function createComparisonFunction(propertyName) {
return function (obj1, obj2) {
var val1 = obj1[propertyName];
var val2 = obj2[propertyName];
if (val1 < val2) {
return -1;
}
else if (val1 > val2) {
return 1;
}
else {
return 0;
}
}
}
我们看的在这个函数中我们定义了一个匿名函数,并且将匿名函数作为值返回。注意代码地4、5行,这两行代码访问了外部函数中的变量propertyName。即使这个函数返回了,或者在其他地方被调用了,我们通过这个匿名函数仍然可以访问这个变量。这是为什么呢?想想前面在对象内部搜索属性的机制。很明显,匿名函数的作用域链包含了createComparisonFunction的作用域链。这又是为什么呢?还得从函数被第一次调用发生的一些细节上进行讨论。
函数第一次调用到底发生了什么
之前介绍作用域链的博客中有关于这方面内容的介绍。相信大家肯定还记忆犹新。当一个函数第一次被调用的时候,会创建一个执行环境和作用域链,并把作用域链赋值给一个特殊的内部属性[Scope]。然后使用this、arguments和其他命名参数的值来初始化函数的活动对象。外部函数的活动对象位于第二位,外部函数的外部函数的活动对象在第三位,直到作为作用域链终点的全局执行换环境。
下面还是通过一个简单的例子来重温下这方面的内容:
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 == value2) {
return 0;
}
else {
return 1;
}
}
var result = compare(5, 10);
那么,按照我们之前的描述。在执行第12行代码的时候,作用域链相关的分析应该是这样的。看图:

后台的每一个执行环境都有一个表示变量的对象--变量对象。全局环境的变量对象始终存在,像compare函数这样的局部环境的变量对象,只是在运行时存在。在创建compare()函数的时候,会创建一个预先包含全局变量对象的作用域链。这个作用域链被包含在内部的[Scope]属性中。当调用compare()函数的时候,会为函数创建一个执行环境,然后通过复制函数的[Scope]属性中的对象构建起执行环境的作用域链。
无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕以后,局部变量对象就会被销毁,内存中只保存全局作用域。但是闭包让情况变的有点不同。
下面我们来看下我们最开始的那个例子:
var compare = createComparisonFunction("name");
var result = compare({ name: "Nicolas" }, { name: "Grey" });
在另一个函数内部会将包含函数(外部函数)的活动对象添加到它的作用域链中。因此在createComparisonFunction函数内部定义的匿名函数的作用域链中会将createComparisonFunction函数的变量对象包含在自己的作用域链中。下面这张图很好的展示了这一点:

匿名函数的作用域链中引用了外部函数的变量对象(活动对象)。但是:createComparisonFunction函数执行完以后,其活动对象也不会被销毁。因为匿名函数的作用域链中还引用着createComparisonFunction的活动对象。我们也可以这样认为,createComparisonFunction函数执行完以后,其作用域链被销毁,但是其活动对象仍然在内存中。所以,过度的使用闭包可能会导致内存占用过高。
闭包与变量
作用域链的这种配置机制导致了一个副作用,即闭包只能取得包含函数中任意变量的最后一个值。因为闭包通过作用域链引用的是整个变量对象。外部函数的变量存储在其变量对象中。下面的例子可以展示这个问题:
/**
* 闭包与变量的关系示例
**/
function createFunctions() {
var result = [];
for (var i = 0; i < 10; i++) {
result[i] = function () {
return i;
}
}
return result;
} var funs = createFunctions();
for (var i = 0; i < 10; i++) {
alert(funs[i]()); //输出10次10
}
我们看到每一个函数都输出10。并不是我们想象中的1-10之间的数值。因为每一个result数组引用的匿名函数内部都包含了createFunctions函数的活动对象。循环每一次的调用,修改的都是createFunctions变量对象中的i值。最后我们调用的时候看到的只是最后的一个i的值。那么我们怎么修改,才能按预想的输出1-10呢。问题的关键在于:我们如果能每一次循环的时候把i的值预存起来不就可以了吗?看看下面的这个改进方案:
function createFunctions() {
var result = [];
for (var i = 0; i < 10; i++) {
result[i] = (function (argument) {
return function () {
return argument;
}
})(i);
}
return result;
}
var funs = createFunctions();
for (var i = 0; i < 10; i++) {
alert(funs[i]()); //输出1-9
}
我们通过改进后,终于如愿的输出了1-9。看看到底发生了什么?在代码的第4-8行,我们看到我们创建了一个匿名函数,并且将i的值作为参数传递给它,然后立即执行这个匿名函数。这个匿名函数内部返回了另一个匿名函数,result数组中保存的匿名函数的作用域链里面就会有4个活动对象,分别是本身的活动对象、外部匿名函数(已执行)的活动对象(包含传递的i的值,即argument)、createFunctions的活动对象、全局活动对象。下面我们在执行返回的匿名函数时,通过作用域链来搜索到argument变量。每一个argument变量都是当时执行时传递的i的值。
关于this对象
在闭包中使用this值也会导致一些问题。this对象是在运行时根据函数的执行环境绑定的。在全局执行环境中,this等于window,而当函数作为某一个对象的方法调用时,this等于那个对象。匿名函数的执行环境具有全局性,this的值通常等于window。但有时候,可能由于编写闭包的方式不同,这一点可能不会那么明显。比如下面的例子:
var name = "The Window";
var object = {
name: "The Object",
getNameFun: function () {
return function () {
return this.name;
}
}
} alert(object.getNameFun()()); //输出The Window
按照之前在作用域链中搜索变量的机制。输出应该是The Object才对。但是为什么是The Window呢?前面应该提到过,每个函数在被调用时,其活动对象都会自动获取两个变量this和arguments。内部函数在搜索这两个变量的时候,只会搜索到其活动对象为止,因此,无法访问外部函数的这两个变量。不过通过简单的修改我们可以实现弹出The Object的效果。请看下面的例子:
var name = "The Window";
var object = {
name: "The Object",
getNameFun: function () {
var that = this;
return function () {
return that.name;
}
}
} alert(object.getNameFun()()); //输出The Object
我们在返回匿名函数之前,将this保存在that变量中,作为闭包,最深层次的匿名函数在调用时,其作用域链中会包含getNameFun这个函数的活动对象。因此这时that还是引用object对象。我们能正常的弹出The Object。讲到这里,相信大家对闭包都有一个详细的了解了把。最后推荐大家看个网页,里面有很多经典的闭包的事例哦。http://www.oschina.net/question/28_41112。
浅谈JavaScript中闭包的更多相关文章
- 浅谈JavaScript中的闭包
浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...
- 浅谈JavaScript中的null和undefined
浅谈JavaScript中的null和undefined null null是JavaScript中的关键字,表示一个特殊值,常用来描述"空值". 对null进行typeof类型运 ...
- 浅谈JavaScript中的正则表达式(适用初学者观看)
浅谈JavaScript中的正则表达式 1.什么是正则表达式(RegExp)? 官方定义: 正则表达式是一种特殊的字符串模式,用于匹配一组字符串,就好比用模具做产品,而正则就是这个模具,定义一种规则去 ...
- 浅谈JavaScript中的内存管理
一门语言的内存存储方式是我们学习他必须要了解的,接下来让我浅谈一下自己对他的认识. 首先说,JavaScript中的变量包含两种两种类型: 1)值类型或基本类型:undefined.null.numb ...
- 浅谈JavaScript中的继承
引言 在JavaScript中,实现继承的主要方式是通过原型链技术.这一篇文章我们就通过介绍JavaScript中实现继承的几种方式来慢慢领会JavaScript中继承实现的点点滴滴. 原型链介绍 原 ...
- 浅谈JavaScript中的this
引言 JavaScript 是一种脚本语言,因此被很多人认为是简单易学的.然而情况恰恰相反,JavaScript 支持函数式编程.闭包.基于原型的继承等高级功能.本文仅采撷其中的一例:JavaScri ...
- 浅谈JavaScript中继承的实现
谈到js中的面向对象编程,都有一个共同点,选择原型属性还是构造函数,两者各有利弊,而就片面的从js的对象创建以及继承的实现两个方面来说,官方所推荐的是两个相结合,各尽其责,各取其长,在前面的例子中,我 ...
- 【总结】浅谈JavaScript中的接口
一.什么是接口 接口是面向对象JavaScript程序员的工具箱中最有用的工具之一.在设计模式中提出的可重用的面向对象设计的原则之一就是“针对接口编程而不是实现编程”,即我们所说的面向接口编程,这个概 ...
- 浅谈JavaScript中的defer,async
引言 开始重读<<JavaScript高级程序设计>>一书,看到关于JavaScript中关于defer.async的部分.网上查询了点资料,觉得蛮好的.现在总结下. defe ...
随机推荐
- block的使用
转载自:http://mobile.51cto.com/hot-403897.htm 一.概述 Block是C级别的语法和运行时特性.Block比较类似C函数,但是Block比之C函数,其灵活性体现在 ...
- 58. Android一些开发习惯总结
作者:漫步 链接:https://www.zhihu.com/question/27227425/answer/35973793 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...
- 【BZOJ-1367】sequence 可并堆+中位数
1367: [Baltic2004]sequence Time Limit: 20 Sec Memory Limit: 64 MBSubmit: 932 Solved: 348[Submit][S ...
- HA模式手动切换namenode状态
查看状态 hdfs haadmin -getServiceState nn1 有时候通过网页访问两个namenode的http-address,看到默认的主namenode状态变成了standy,这时 ...
- 【poj3017】 Cut the Sequence
http://poj.org/problem?id=3017 (题目链接) 题意 给出一个数列要求将它分割成许多块,每块的数的和不超过m,要求每块中最大的数之和最小. Solution 这道题真的很不 ...
- BZOJ4548 小奇的糖果
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...
- JDK与Java SE/EE/ME的区别
1. Java SE(Java Platform,Standard Edition). Java SE 以前称为 J2SE.它允许开发和部署在桌面.服务器.嵌入式环境和实时环境中使用的 Java 应用 ...
- XCode显示iOS Simulators时不显示系统版本号并出现Identifier(UUID)
如果出现如下的显示问题,说明你添加了多个相同系统版本的iOS Simulators: 打开Windows->Devices,查看已经出现了相同版本的模拟器: 那么我们删除一些相同的模拟器,即可解 ...
- dedecms /plus/feedback.php SQL Injection Vul
catalog . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述 . Dedecms v5.7的plus\feedback.php SQL ...
- 《ODAY安全:软件漏洞分析技术》学习心得-----shellcode的一点小小的思考
I will Make Impossible To I'm possible -----------LittleHann 看了2个多星期.终于把0DAY这本书给看完了,自己动手将书上的实验一个一个实现 ...