javascript语言中函数闭包现象

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

浅析javascript语言中的函数闭包现象

摘要:闭包在很多javascript高级应用中都会出现。本文主要以javascript语言为例分析闭包现象的形成,理解闭包的运行机制及使用。

关键词:闭包;内部变量;作用域

中图分类号:tp312.2 文献标识码:a 文章编号:1007-9599 (2012) 23-0000-02

闭包问题是英格兰brighton beers活动中提出来的。闭包的概念很抽象,如果不用代码来说明,将很难用描述性语句把它解释清楚。所以本文将以javascript语言为例解释说明什么是闭包,分析闭包的运行机制及使用注意事项。

1 闭包的概念

什么是闭包?在计算机科学中,闭包(closure)是词法闭包(lexical closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。ecmascript允许使用内部函数--即函数定义和函数表达式位于另一个函数的函数体内。[1]而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。由于在javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包

简单理解成“定义在一个函数内部的函数”。

2 理解闭包在javascript中的运行及使用

2.1 如何理解闭包

闭包的创建相对容易,有时创建闭包编程人员根本没有意识到这是闭包,尤其是在ie等常见的浏览器环境下运行下,所编写的程序在很大程度上存在潜在的问题。因此在编写javascript高级应用程序是,对闭包的使用和它的运行机制必须有一定了解。而了解闭包运行机制的就要先解析在编写过程中的环境变量及作用域。javascript中每个函数都是一个函数对象(函数实例),既然是对象,就有相关的属性和方法。[scope]就是每个函数对象都具有的一个仅供javascript引擎内部使用的属性,该属性是一个集合(类似于链表结构),集合中保存了该函数在被创建时的作用域中的所有对象,而这个作用域集合形成的链表则被称为scopechain (作用域链)[2]。该作用域链中保存的作用域对象,就是该函数可以访问的所有数据。例如定义一个函数求两个数的和运算

当add函数被创建时,函数所在的全局作用域的全局对象被放置到add函数的作用域链([[scope]]属性)中。从图2-1中看到作用域链的第一个对象保存的是全局对象,全局对象中保存了诸如this,window,document以及全局对象中的add函数。这也就是为什么可以在在全局作用域下的函数中访问window(this),访问全局变量,访问函数自身的原因。当局部变量和全局变量同名时,会使用局部变量而不使用全局变量,

2.2 全局变量在javascript函数中的应用

分析javascript特殊的变量作用域是可以方便编程人员理解闭包。在编程语言中,所有的变量的作用域从函数的角度来说都可以分成:全局变量和局部变量。而javascript语言的是比较特殊的一种语言,它可以从函数内部可以直接读取并引用全局变量。一个闭包就是一个引用了其被生成的环境中、所属的变量范围内的所有变量的函数。[3]例如定义了一个函数f1,代码如下:

var x = 1;

function f1()

{var y = 1;

var result = x + y;

document.wirte(“result=”, result);

};

这里我们首先定义了一个变量“x”,值为1。然后我们定义无参无返回值函数f1,在f1函数内部result中使用了“x”变量。这个变量是被f1 引用了,自动被添加到了f1的运行环境中了。

当我们执行f1时,它会输出了一个预期的结果2。但函数f1执行时,原始的“x”此时已经不在是它当初的变量环境,但它仍然能被使用。

2.3 局部变量在javascript函数中的应用

在javascript语言中,函数内部的子函数可以直接读取局部变量,所以闭包也可以说成是“嵌套定义在一个函数内部的函数”。

而闭包就成为了函数内部和函数外部相联系一个媒介。

因此,有时候当需要得到函数内的局部变量。可以在函数的内部,再定义一个函数。例如定义了一个函数f2代码如下:

function f2()

{n=1;

function f3()

{var s=n+2

alert(“s=”+ s);//s的结果=3 }

return f3;

}

在代码中,函数f3就被定义在函数f2内部,这时函数f2内部的所有局部变量,对函数f3都是有效的。即n可以被f3函数引用,但是f3内部的局部变量,对f2就是不可见的。即s变量在f2函数中不可见。这就是javascript语言中的“chain scope”,计算机专业术语称为“链式作用域”,在链式作用域中子对象会逐层向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是有效的,子对象可以随意使用父对象的变量,但是子对象中的变量却屏蔽了,父对象没法使用它的变量。

在代码运行中,函数f3可以使用f2中的局部变量n,但父对象函数f2想要读取f3的内部变量怎么办?这里只要把f3作为返回值,则函数f2即可读取到内部变量s的值。这里的f3()就是一个闭包。

2.4 闭包在javascript语言中的应用

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。例如定义一个函数a,在函数a内部定义如下函数b,代码如下:

function a() //外层函数a

{ var n=1; //临时变量n

function b()//内层函数b

{

alert(n++);//引用外层临时变量n

}

return b; }//返回函数b

var result=a (); //调用函数a,函数b被引用

result(); //函数b中n变量被引用值为2.

}

在这段代码中,函数b实际上就是一个闭包函数。在程序代码中函数b被调用了两次,变量n值第一次的n=1,第二次的n=2。这说明了,函数a中的局部变量n一直驻留在内存中,当函数a调用后没有被释放,第二次调用时仍然再使用。这种情况说明,函数b 可以引用函数a的全局变量,在运行中函数b始终在内存中,而函数b的存在依赖于函数a,在调用结束后,函数a也会驻留在内存,不会被javascript的垃圾回收机制回收。

相关文档
最新文档