javascript中闭包的闭包问题

JavaScript中函数的重要性毋庸置疑。在理解了JavaScript中的函数之后,非常重要的地点就是理解我们怎样使用函数来创建闭包。一直以来,闭包都是JavaScript新手学习时的一个难点所在,它位于JavaScript函数与变量作用域交叉的一个灰色地带:
本文将尽可能简单的方法讲述关于JavaScript闭包的那些事情,使用的代码也非常的简单。如果一开始就讲述闭包的概念,只会使得你更加的困惑。所以我们就从一个我们熟悉的领域开始,慢慢的向闭包的邪恶领域前进,看看我们在那里能发现什么。
下面开始我们的冒险之旅吧!
函数中的函数
我们要做的第一件事情是理解当你在函数中创建了函数并且从函数内部返回一个函数时究竟发生了什么。首先我们来快速的回顾一下函数。
看下面的代码:
function calculateRectangleArea(length,width){
return length*
var roomArea = calculateRectangleArea(10,10);
alert(roomArea);
calculateRectangleArea函数接收两个参数并且返回这两个参数的乘积。在这个例子中没我们将返回的数赋值给了变量roomArea。
当代码运行之后,roomArea变量包含了10乘10的结果,也就是100:
正如你所知道的,一个函数可以返回任何东西。在这个例子中,我们返回了一个数。你可以返回一些文本(也就是字符串),undefined,一个自定义对象等等。只要调用函数的代码知道怎么处理返回的值,你可以做任何你想做的事情。你甚至可以返回另一个函数。我们下面就来看一个这样的例子:
function youSayGoodBye(){
alert('Good Bye!');
function andISayHello(){
alert('Hello!');
return andISayH
你可以在函数内部包括函数。在这个例子中,我们的youSayGoodBye函数包含了一个alert语句以及另一个叫做andTSayHello的函数:
有趣的地方是当youSayGoodBye函数调用时返回了什么东西。它返回了andISayHello函数:
function youSayGoodBye(){
alert('Good Bye!');
function andISayHello(){
alert('Hello!');
return andISayH
下面我们调用这个函数,并且让一个变量指向这个函数的调用结果:
var something = youSayGoodBye();
在这行代码运行的时候,youSayGoodBye函数中的所有代码同时也在运行。这意味着,你可以看到一个对话框(由于alert)说Good Bye!:
当运行结束之后,andISayHello函数将会被创建并且返回。在这个时候,变量something只关注一个东西,这个东西就是andISayHello函数:
由于something现在指向一个函数,因此你可以通过括号标示符调用它:
var something = youSayHello();
something();
当你这么做的时候,返回的内部函数(也就是andISayHello)将会执行。和前面一样,你将会看到一个对话框,但是对话框这次说的是Hello!– 这是由于内部的alert决定的:
上面提到的所有东西都很直观。唯一你可能觉得比较新的地方是一旦一个函数返回一个值,这个函数就不再存在了。唯一存在的东西是返回值。
现在我们已经接近闭包的邪恶领域了。在下一部分中,我们将扩展前面提到的代码来看看一个变形的例子。
内部函数不是自包含函数的情况
在前面的例子中,你的andISayHello函数是一个自包含函数并且不依赖于外部函数的任何变量或状态:
function youSayGoodBye() {
alert("Good Bye!");
function andISayHello() {
alert("Hello!");
return andISayH
在现实的很多场景中,几乎没有这样的自包含函数的例子。你经常会发现需要在内部函数和外部函数之间共享变量和数据。为了强调这一点,我们看看下面的例子:
function stopWatch() {
var startTime = Date.now();
function getDelay() {
var elapsedTime = Date.now() - startT
alert(elapsedTime);
return getD
这个例子展示了一个简单地测量消耗的时间的方式。在stopWatch函数中,你有一个startTime变量来被赋值为Date.now():
var startTime = Date.now();
你也有一个叫做getDelay的内部函数:
function getDelay() {
var elapsedTime = Date.now() - startT
alert(elapsedTime);
getDelay函数展示了一个包含当前时间Date.now()和前面定义的开始时间startTime之间间隔的对话框。
回到外部函数stopWatch(),在运行结束之前发生的最户一件事情是返回getDelay函数。正如你所见的,这里的这段代码和先前的例子非常类似。你有一个外部函数,你有一个内部函数,然后外部函数返回了内部函数。
现在,为了弄清楚,stopWatch函数是怎么运行的,我们添加下面的代码:
var timer = stopWatch();
// 做一些消耗时间的式
for (var i = 0; i & 1000000; i++) {
var foo = Math.random() * 10000;
// 调用返回函数
如果你运行这个例子,你将看到一个对话框展示从初始化到timer函数被调用之间时间间隔的对话框。你的for循环接收时候,timer变量像一个函数一样被调用:
基本上,你现在有了一个秒表可以用来计算一个长时间运行的操作花费了多长时间。
现在你看到我们的简单的秒表例子已经运行起来了,我们回到stopWatch函数看看实际上发生了什么。正如前面所提到的,上面的例子和前面的youSayGoodBye/andISayHello例子很相似。要注意的一点是当getDelay函数返回并赋值给timer变量时发生了什么。
外部函数stopWatch不再起作用,time人变量被绑定到了getDelay函数。现在,有区别的地方来了。getDelay函数依赖于外部函数stopWatch上下文中的startTime变量:
var startTime = Date.now();
var elapsedTime = Date.now() - startT
当getDelay函数被返回时外部函数stopWatch函数不再器作用,那么下面的这行代码又发生了什么?
var elapsedTime = Date.now() - startT
在这个上下文中,看起来startTime变量没有被定义。但是,这段代码显然正常运行了,因此这里存在一些其他的东西。这里提到的“其他的东西”值得就是害羞而神秘的闭包。我们来看看究竟发生了什么似的我们的startTime变量储存了一个实际的值而不是undefined。
JavaScript runtime将会持续跟踪你的变量,内存使用,引用,实际上来说它非常的聪明。在这个例子中,它探测到内部函数(getDelay)依赖于一个来自外部函数(stopWatch)的变量。当这种情况发生时,runtime将会确保任何来自于外部函数的变量仍然在内部函数中可用,即使外部函数已经调用结束了。
为了说明这一点,我们来看一张图:
变量timer依然指向getDelay函数,但是getDelay函数依然可以获取来自于外部函数stopWatch中的startTime变量。这个内部函数 – 由于它将来自于外部函数的相关变量包含进了自己的作用域中 – 因此被称为闭包:
至此,我们可以给闭包下一个定义:闭包就是一个新创建但是依然包含外部作用域变量的函数:
再次回到前面的例子,在timer变量初始化时startTime得到了Date.now()的值。当stopWatch返回了内部桉树getDelay时,stopWatch函数不再起作用。但是内部函数依赖的变量却没有消失。这些共享的变量没有消失。相反,它们被包含进入了内部函数,也就是闭包中。
闭包乍看上去很复杂,但是其实很简单,它存在于JavaScript中的各个地方。重要的一点是记住:闭包允许函数保持运行,即使函数的韩静发生了巨大的变化或者小时。当函数被创建时任何包含在作用域中的变量都会被保护起来以确保函数的正常运行。这样的机制对于JavaScript这样的动态语言来说是非常重要的,因为你可以随时创建,修改以及销毁变量。
本文译自Closures in JavaScript,原文地址
如果你觉得本文对你有帮助,请点击下面的链接为我提供赞助
本站专栏文章皆为原创,转载请注明出处(带有 前端乱炖 字样)和本文的显式链接(),本站和作者保留随时要求删除文章的权利!
闭包有那么难么0.0.
发现一些错别字,一定是大大手误了~
* '外部函数stopWatch不再起作用,time人变量被绑定到了getDelay函数。' 这是应是timer而不是time人。
* '重要的一点是记住:闭包允许函数保持运行,即使函数的韩静发生了巨大的变化或者小时。'应是环境
是保存了外部函数所有的上下文么?(包括注册的事件,对象等等,而不仅仅只是一个变量)
WRITTEN BY
PUBLISHED IN
本专栏其他文章
浏览:32163赞:18
浏览:5485赞:5
浏览:13361赞:14
浏览:10405赞:4
浏览:14763赞:23
浏览:7897赞:2
Power By NodeJS,本站所有代码的地址在彻底理解JS闭包 - 简书
彻底理解JS闭包
闭包并不是JS所独有的,在计算机科学中其是一个普遍的概念,在Python中也有闭包的概念,但闭包在Python应用不是很广泛,JS可谓是把闭包发扬光大,普照你我众猿。
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式,就是在一个函数内部创建另一个函数。
function outer(){
var context = "outer";
return function inner(){
console.log(context);
var fn = outer();
//输出outer
Hint:先认真看上一篇文章&深刻理解JS的作用域链&,理解了作用域链再来看闭包都是分分钟的事。
根据作用域链的创建规则,当执行var fn = outer();语句时,会创建一个outer函数对应的变量对象。
然后返回了一个函数inner,inner函数在创建的时候(注意还没执行),会事先创建一条作用域链,然后将作用域链的引用赋给inner函数的内部属性[[Scope]],
重要的是上面创建的这条作用域链中的首元结点指向了outer函数对应的变量对象。
总结:因为内部函数在被创建时,其作用域链对外部函数对应的变量对象存在一个引用,
而JS采用引用计数的方法进行内存管理,
所以当外部函数被执行完毕后,其对应的变量对象不会被回收,
这样就发生了闭包,在外部函数执行完毕后,我们在内部函数中仍然可以访问外部函数作用域中的变量。
闭包的应用
模仿块级作用域
定义函数的public接口,public方法在JS中被称为特权方法
越看JS越觉得JS是一门奇葩语言,你说我搞个继承还要通过原型去实现,定义类的public接口还要通过闭包去实现,直接下面这样不好吗,清楚明了?
class CSubClass : public CBaseClass
CSubClass():CBaseClass(){};
~CSubClass(void){};
void PublicMethod(){};
protected:
virtual BOOL handle_event (HELEMENT he, BEHAVIOR_EVENT_PARAMS& params);
std::wstring m_wstrTargetId;
JS硬要搞的这么奇葩,更多的时候我们应该把精力放在如何设计类上面,包括如何从实际生活中抽象出一个类,这个类要隐藏什么信息,该把什么暴露出来,而且类的接口应该展现出一致的抽象层次;这个类和其他类应该是什么样的关系,按照耦合关系的强弱,分为依赖、关联、聚合、组合、实现、继承;等等。说了这么多,我只有一个感觉JS的命太好了,这个当年10天之内被设计出来的语言,谁也没有想到现如今统一了浏览器,只能怪Java的Applet不给力,然而语言只不过是工具,技术是相通的,重在应用,更多的时候我们不会一个东西,不是对语言本身不了解,更多的时候是对业务不了解,不知道自己要解决什么问题。
世界上有那么多的城镇,城镇中有那么多的酒馆,你却偏偏走进了我的酒馆。
本文主要参考MDN手册和Learning Advanced JavaScript 在文章开头,我先放出MDN给出的定义: 闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。 现在不需...
谈起闭包,它可是JavaScript两个核心技术之一(异步和闭包),在面试以及实际应用当中,我们都离不开它们,甚至可以说它们是衡量js工程师实力的一个重要指标。下面我们就罗列闭包的几个常见问题,从回答问题的角度来理解和定义你们心中的闭包。 问题如下: 闭包的介绍 我们先看看...
收听音频,戳链接,该文为前公众号itclan,现该号已暂停使用,如需查看后期内容,可移步新号itclanCoder 前言 对于js中的闭包,无论是老司机还是小白,我想,见得不能再多了,然而有时三言两语却很难说得明白,反正在我初学时是这样的,脑子里虽有概念,但是却道不出个所以...
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTML 最坚实的梁柱;分享,是 CSS
里最闪耀的一瞥;总结,是 JavaScript ...
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。 一、变量的作用域要理解闭包,首先必须理解Javascript特殊的变量作用域。变量的作用域无非就是两种:全局变量和局部变量。Javascript语言的特殊之处,就在于函...
本次学习内容:U1L2 本次作业: 蓝色练习册 Tutorial Workbook - P14, Activity A. Look and number. Tutorial Workbook - P14, Activity B. Read and check. 网络练习 O...
近日,中石油和中海油发布了悲催的半年报。上半年,中石油净利润暴跌98%,如果不计算非经常性损益,则亏损了接近95亿;中海油亏损额超过77亿。 油价下跌是两桶油亏损的一大原因,但恐怕也不全是坏事。面包财经(微信公众号ID:mbcaijing)翻查了统计数据发现:低迷的油价让依...
从个人的角度来看: 今天我的心情怎么样?有什么印象深刻的事情?什么事情让我获得了成就感?遇到了什么问题?有什么疑惑和纠结的地方?有什么需要帮助的地方? 早上记了单词; 下午出去骑车去浪了圈,心情挺好的; 把周天做的CVTE的面试题当时不会的又做了遍; 看了CSS3中的属性选...
努力相信 依旧被伤害 背后究竟隐藏着什么 我什么都不知道 强颜欢笑换来的又是什么? 再一次的伤害 扮演个局外人好了 看,又自我欺骗 被人骗了还不够 还要往伤疤上给自己再撒点盐 “看热闹不嫌事儿大”? 好像不太恰当 明明是发生在自己身上 简单点 真诚点 坦诚理解 Javascript 的闭包
<a data-traceid="question_detail_above_text_l&&
前言:还是一篇入门文章。Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包 对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性,并结合一点 ECMAScript语言规范来使读者可以更深入的理解闭包。
注:本文是入门文章,例子素材整理于网络,如果你是高手,欢迎针对文章提出技术性建议和意见。本文讨论的是Javascript,不想做语言对比,如果您对Javascript天生不适,请自行绕道。
什么是闭包
闭包是什么?闭包是Closure,这是静态语言所不具有的一个新特性。但是闭包也不是什么复杂到不可理解的东西,简而言之,闭包就是:
闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。
闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配
当在一个函数内定义另外一个函数就会产生闭包
上面的第二定义是第一个补充说明,抽取第一个定义的主谓宾——闭包是函数的‘局部变量’集合。只是这个局部变量是可以在函数返回后被访问。(这个不是官方定义,但是这个定义应该更有利于你理解闭包)
做为局部变量都可以被函数内的代码访问,这个和静态语言是没有差别。闭包的差别在于局部变变量可以在函数执行结束后仍然被函数外的代码访问。这意味 着函数必须返回一个指向闭包的“引用”,或将这个”引用”赋值给某个外部变量,才能保证闭包中局部变量被外部代码访问。当然包含这个引用的实体应该是一个 对象,因为在Javascript中除了基本类型剩下的就都是对象了。可惜的是,ECMAScript并没有提供相关的成员和方法来访问闭包中的局部变 量。但是在ECMAScript中,函数对象中定义的内部函数() inner function是可以直接访问外部函数的局部变量,通过这种机制,我们就可以以如下的方式完成对闭包的访问了。
function greeting(name) {
var text = 'Hello ' + // local variable
// 每次调用时,产生闭包,并返回内部函数对象给调用者
return function () { alert(text); }
var sayHello=greeting( &Closure& );
sayHello()
// 通过闭包访问到了局部变量text
上述代码的执行结果是:Hello Closure,因为sayHello()函数在greeting函数执行完毕后,仍然可以访问到了定义在其之内的局部变量text。
好了,这个就是传说中闭包的效果,闭包在Javascript中有多种应用场景和模式,比如Singleton,Power Constructor等这些Javascript模式都离不开对闭包的使用。
ECMAScript闭包模型
ECMAScript到底是如何实现闭包的呢?想深入了解的亲们可以获取进行研究,我这里也只做一个简单的讲解,内容也是来自于网络。
在ECMAscript的脚本的函数运行时,每个函数关联都有一个执行上下文场景(Execution Context)&,这个执行上下文场景中包含三个部分
文法环境(The LexicalEnvironment)
变量环境(The VariableEnvironment)
其中第三点this绑定与闭包无关,不在本文中讨论。文法环境中用于解析函数执行过程使用到的变量标识符。我们可以将文法环境想象成一个对象,该对 象包含了两个重要组件,环境记录(Enviroment Recode),和外部引用(指针)。环境记录包含包含了函数内部声明的局部变量和参数变量,外部引用指向了外部函数对象的上下文执行场景。全局的上下文 场景中此引用值为NULL。这样的数据结构就构成了一个单向的链表,每个引用都指向外层的上下文场景。
例如上面我们例子的闭包模型应该是这样,sayHello函数在最下层,上层是函数greeting,最外层是全局场景。如下图:
因此当sayHello被调用的时候,sayHello会通过上下文场景找到局部变量text的值,因此在屏幕的对话框中显示出”Hello Closure” 变量环境(The VariableEnvironment)和文法环境的作用基本相似,具体的区别请参看ECMAScript的规范文档。
闭包的样列
前面的我大致了解了Javascript闭包是什么,闭包在Javascript是怎么实现的。下面我们通过针对一些例子来帮助大家更加深入的理解闭包,下面共有5个样例,例子来自于。 例子1:闭包中局部变量是引用而非拷贝
function say667() {
// Local variable that ends up within closure
var num = 666;
var sayAlert = function() { alert(num); }
return sayA
var sayAlert = say667();
sayAlert()
因此执行结果应该弹出的667而非666。
例子2:多个函数绑定同一个闭包,因为他们定义在同一个函数内。
function setupSomeGlobals() {
// Local variable that ends up within closure
var num = 666;
// Store some references to functions as global variables
gAlertNumber = function() { alert(num); }
gIncreaseNumber = function() { num++; }
gSetNumber = function(x) { num = }
setupSomeGolbals(); // 为三个全局变量赋值
gAlertNumber(); //666
gIncreaseNumber();
gAlertNumber(); // 667
gSetNumber(12);//
gAlertNumber();//12
例子3:当在一个循环中赋值函数时,这些函数将绑定同样的闭包
function buildList(list) {
var result = [];
for (var i = 0; i & list. i++) {
var item = 'item' + list[i];
result.push( function() {alert(item + ' ' + list[i])} );
function testList() {
var fnlist = buildList([1,2,3]);
// using j only to help prevent confusion - could use i
for (var j = 0; j & fnlist. j++) {
fnlist[j]();
testList的执行结果是弹出item3 undefined窗口三次,因为这三个函数绑定了同一个闭包,而且item的值为最后计算的结果,但是当i跳出循环时i值为4,所以list[4]的结果为undefined.
例子4:外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。
function sayAlice() {
var sayAlert = function() { alert(alice); }
// Local variable that ends up within closure
var alice = 'Hello Alice';
return sayA
var helloAlice=sayAlice();
helloAlice();
执行结果是弹出”Hello Alice”的窗口。即使局部变量声明在函数sayAlert之后,局部变量仍然可以被访问到。
例子5:每次函数调用的时候创建一个新的闭包
function newClosure(someNum, someRef) {
// Local variables that end up within closure
var num = someN
var anArray = [1,2,3];
var ref = someR
return function(x) {
anArray.push(num);
alert('num: ' + num +
'\nanArray ' + anArray.toString() +
'\nref.someVar ' + ref.someVar);
closure1=newClosure(40,{someVar:'closure 1'});
closure2=newClosure(1000,{someVar:'closure 2'});
closure1(5); // num:45 anArray[1,2,3,45] ref:'someVar closure1'
closure2(-10);// num:990 anArray[1,2,3,990] ref:'someVar closure2'
闭包的应用
Singleton 单件:
var singleton = function () {
var privateV
function privateFunction(x) {
...privateVariable...
firstMethod: function (a, b) {
...privateVariable...
secondMethod: function (c) {
...privateFunction()...
这个单件通过闭包来实现。通过闭包完成了私有的成员和方法的封装。匿名主函数返回一个对象。对象包含了两个方法,方法1可以方法私有变量,方法2访 问内部私有函数。需要注意的地方是匿名主函数结束的地方的’()’,如果没有这个’()’就不能产生单件。因为匿名函数只能返回了唯一的对象,而且不能被 其他地方调用。这个就是利用闭包产生单件的方法。
可惜都被墙了。
(Douglas Crockford 大神的视频,一定要看啊)
那几个例子举的真是好,恍然大悟之感。。
In order to implement lexical scoping, the internal state of a JavaScript function object must
include not only the code of the function but also a reference to the current scope chain.
This combination of a function object and a scope (a set of variable bindings) in which
the function’s variables are resolved is called a closure in the computer science literature.
We described it as a list of objects, not a stack of bindings.
Each time a JavaScript function is invoked, a new object is created to hold the
local variables for that invocation, and that object is added to the scope chain.
When the function returns, that variable binding object is removed from the scope chain.
If there were no nested functions, there are no more references to the binding object
and it gets garbage collected. If there were nested functions defined,
then each of those functions has a reference to the scope chain, and that scope chain
refers to the variable binding object. If those nested functions objects remained within
their outer function, however, then they themselves will be garbage collected, along
with the variable binding object they referred to. But if the function defines a nested
function and returns it or stores it into a property somewhere, then there will be an
external reference to the nested function. It won’t be garbage collected, and the variable
binding object it refers to won’t be garbage collected either.
--- 共有 5 条评论 ---
我才发现自己水平有限!
JavaScript: The Definitive Guide
压力真心大
表示看不懂
http://javascript-reference.info/javascript-closures-for-dummies.htm
这个很有帮助呢~~
jquery的插件就是写在闭包里面的
总是啰啰嗦嗦的一大堆还没看明白就先看晕了
--- 共有 1 条评论 ---
这是我看到的解释最好的闭包
Common Lisp支持闭包,关于Common Lisp闭包的一个最典型例子是这样的:
[1]& (setf *fn* (let ((i 0))& &&&&&&&&&&& #'(lambda () (setf i (+ i 1))))) #FUNCTION :LAMBDA NIL (SETF I (+ I 1))& [2]& (funcall *fn*) 1 [3]& (funcall *fn*) 2 [4]& (funcall *fn*) 3
按照之前我们对变量的理解,let引入的i只是一个局部变量,在离开定义的环境后,该变量生命周期将终结。理论上我们三次调用*fn*所对应的你们函数得到的结果应该是相同的才对。但就是由于在let构造的局部作用域内的那个匿名函数引用了外部的变量i,导致变量i可以脱离其原生作用域的束缚,让其生命周期等同于了其内部的那个匿名函数,这个内部的匿名函数就被称为闭包,而那个被引用的外部变量被成为&(free variable)。当我们连续调用函数*fn*时,i就像一个全局变量一样,每次值都加一。
引用了自由变量的闭包似乎是终结了自由变量的局部绑定关系,将自由变量从局部作用域环境中取出,并重新放入一个与闭包同生命周期的新作用域。自由变量会常驻内存中,这也是闭包的常用场景之一。闭包的另外一个用途可能就是出于保护自由变量的考虑,让自由变量只有通过闭包函数才能访问到。
--- 共有 3 条评论 ---
嗯,解释得比较清楚,虽然lisp代码没有看懂。
: 你可以认为他是全局变量,不过不是全局变量。他是存在时间是根据函数的存在时间来定的
可不可以理解为i虽然在函数里
其实可以看成定义在函数外
闭包和传统语言的“封装”之间究竟有什么差别?
--- 共有 1 条评论 ---
说到点子上了,求解释
很强大啊。。。Access denied | www.ruanyifeng.com used Cloudflare to restrict access
Please enable cookies.
What happened?
The owner of this website (www.ruanyifeng.com) has banned your access based on your browser's signature (433add-ua98).

我要回帖

更多关于 javascript闭包写法 的文章

 

随机推荐