在JavaScript里,“闭包”是一个神奇的东西。借着闭包的力量,我们将跨过面向对象的领域,来攀登一座新的高峰。保罗格雷厄姆
曾经说过,
我
认为目前为止只有两种真正干净利落, 始终如一的编程模式:C语言模式和Lisp语言模式.此二者就象两座高地,
在它们中间是尤如沼泽的低地。在这里C语言代表着过程式语言的精髓,它目前所知的高层境界是面向对象。而称为Lisp的语言,则以另一种形式的无与伦比的
美,成为与过程化对等的存在,即,我们将要介绍的函数式编程。
22.1动态语言与闭包
程序语言中的闭包(closure)概念不是由JavaScript最先提出的,从smalltalk开始,闭包就成了编程语言的一个重要概念。几乎所有的知名动态语言(如Perl、python、ruby等)都支持闭包,JavaScript也不例外。
闭包
(
closure
)
的确是个精确但又很难解释的电脑名词。因此在理解它之前,必须先解释下面一些简单概念。
22.1.1动态语言
所谓动态程序设计语言(Dynamic Programming
Language),准确地说,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。相反,非动态语言在编译(或解释)时,程序结构已经被确定,在执行过程中不能再发生改变。
JavaScript是一个典型的动态语言。除此之外如Ruby、Python等也都属于动态语言,而C、C++等语言则不属于动态语言。
一些人习惯上
将编译型语言认为是非动态语言,而解释型语言认为是动态语言,实际上这是完全错误的概念,动态语言的概念与语言是编译还是解释没有关系,一些解释型的语言
也可以是静态语言,编译型语言确实不易设计为动态语言,但也仍然可以通过良好的设计和使用技巧达到“动态”的效果。
在这里还需要区分一下另外一对容易和上面概念混淆的概念,即动态类型语言(Dynamically Typed Language) 和静态类型语言(Static Typed Language)。
所谓动态类型语言是指在执行期间才去发现数据类型的语言,静态类型语言与之相反,如JavaScript、VBScript和Perl都是典型的动态类型
语言。很多人常常将动态类型语言和动态语言混为一谈,显然从上面的描述看来,它们是两个完全不同的概念。虽然,大多数动态语言都是动态类型语言,但动态语
言本身并不要求一定是动态类型的,而动态类型语言也不一定是动态语言。
22.1.2语法域和执行域
所谓语法域,是指定义某个程序段落时的区域,所谓执行域则是指实际调用某个程序段落时所影响的区域。
在非动态语言中,语法域和执行域范围基本上是一致的,执行域通常只能访问它自身语法域的范围和少量向它开放的语法域,而不能访问它外层或者与它关联的执行域。而在动态语言中,执行域的范围通常大得多。
非动态语言,
如C++的函数在调用时(执行域上)只能访问自身语法域上允许访问的环境,如全局变量和函数、所在对象的属性和方法以及自身的参数和临时变量,这和定义函
数时的许可范围一致。动态语言如JavaScript的函数不但能够访问语法域上的这些范围,还能够访问它外层环境中的执行域范围,例如:
例22.1 动态语言的执行域
<html>
<head>
<title>Example-22.1 动态语言的执行域</title>
</head>
<body>
<script>
<!—
//产生随机数的函数
function RandomAlert()
{
var x = Math.random()
return function()
{
alert(x);
}
}
var a = RandomAlert();
//闭包的执行域随函数调用而创建
var b = RandomAlert();
a(); //调用a,打印出产生的随机数
b(); //调用b,打印出产生的随机数
//一般情况下,a和b得到的数值不同
-->
</script>
</body>
</html>
22.1.2 JavaScript的闭包
在程序语言中,所谓闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。这些外部执行域的非持久型变量神奇地保留它们在闭包最初定义(或创建)时的值
(深连结)。
从上面的概念可以看出,闭包通常是在动态语言中才有的概念,它是某些可以访问外部执行域的段落。JavaScript中的闭包,是通过定义在函数体内部的function来实现的。
例22.1就是典型的闭包应用,RandomAlert()函数的返回值是一个闭包,a(),b()分别访问了闭包两次被创建时对应的外层RandomAlert()函数的执行域上的局部变量x的值。
闭包这个概念我们之前已经多次提到过,但是一直没有解释清楚。相信你即使看了本节前面两段的
解释,仍然还是会觉得有一点困惑。闭包和函数的概念到底有什么相同点和不同点,相信这是大多数读到这里的读者心中最大的疑惑。其实,闭包和函数的关系,应
当类似于一种动态和静态、结构和实例的关系,下面再通过一个例子来简单说明:
例22.2 闭包的本质
<html>
<head>
<title>Example-22.2
闭包的本质</title>
</head>
<body>
<script>
<!—
//A是一个普通的函数
function
A(a)
{
return
a;
}
//B是一个带函数返回值的函数
function
B(b)
{
return
function (){
return
b;
}
}
var x
= A(10);
//因为A除了返回a外什么也没做,执行A()函数后,调用堆栈被销毁
//没有产生闭包,或者说在调用“瞬间”产生了闭包,然后马上被销毁
var y
= B(20);
//因为B返回了一个匿名函数引用,它访问到B()被调用时产生的环境
//因此这里产生了一个“闭包结构”(closure或者function instance)
//在它的环境中,b = 20,因此y()的返回结果是20
var z
= B(30);
//同样,这里产生了第二个“闭包结构”
//在它的环境中,b = 30,因此z()的返回结果是30
alert(x); //得到10
alert(y());
//得到20
alert(z());
//得到30
-->
</script>
</body>
</html>
我们说例22.2中,y()和z()的结果不同,因为两次B()创造的闭包被执行时访问的是不同的b值,它正好是分别的调用B()时b被初始化的值。这里
最奇怪的地方在于,当y()和z()被调用时,B()函数调用已经结束了。如果你有C++、Java或者其他什么编程语言的知识,也许你的潜意识里会认为
当B()调用结束时,局部变量b的值已经被销毁,但结果却是令人惊讶的,由于被返回的闭包里引用了B()调用域上的b值,所以它并没有随着B()调用的结
束而被销毁。
类似的还有之前我们见到过的例22.1和例4.6,在这里我们再次列出例4.6:
function
dice(count, side) //count定义骰子的数量,side定义每个骰子的面数
{
var
ench = Math.floor(Math.random() * 6); //+0~+5的骰子随机变数修正
//这里返回一个闭包,该闭包的作用是对指定的面数和修正值的骰子进行“投掷”
return
function()
{
var score = 0;
for(var i = 0; i < count;
i++)
{
score +=
Math.floor(Math.random() * side) +1;
}
return score + ench;
}
}
var d1 =
dice(2,6); //生成一组2d6+n的骰子,其中的n为0~5的随机数
var d2 =
dice(1,20); //生成一颗20面的骰子,带有0~5的随机点数修正
例4.6中,d1、d2引用的闭包都使用了外部环境
中的局部变量ench和side的值,而这两个局部变量是在dice()方法才被初始化的,在dice()调用结束后,它们并没有被销毁。当你调用
d1()和d2()时,你将会引用到d1和d2在获取闭包时分别创建的side和ench值。
我通常认为闭包是一种引用结构,至少在JavaScript中是这样的。JavaScript中的闭包(closure),也可以理解为一种“函数实例引用”(function
instatnce referer)。
分享到:
相关推荐
内容概要:本文详细介绍了Swift编程语言中的函数与闭包概念,包括函数的基础用法(声明与调用、参数与返回值)、函数类型与变量、内嵌函数与函数链、闭包的概念及其各种高级用法(尾随闭包、隐式返回闭包、捕获列表...
当闭包捕获并引用了外部环境的变量时,这些变量的生命周期将与闭包绑定。Swift为防止内存泄漏,提供了强引用循环检测,自动处理可能导致对象无法释放的情况。 在本Demo中,我们可能有两个视图控制器,例如...
2. **闭包与匿名函数** 在Swift中,闭包本质上就是匿名函数,因为它没有名字。然而,如果闭包在代码中多次使用,我们可以为其定义一个别名,使其看起来像是一个命名函数。 3. **闭包的自动捕获值** 当闭包在某个...
闭包在C语言中可能不如在一些动态类型的语言中那么常见,但依然可以通过巧妙的技巧实现。这里我们将详细讨论如何用C语言实现传递闭包、自反闭包和对称闭包这三种闭包算法。 首先,我们需要理解这些闭包的定义: 1....
基于JavaScript编程语言之闭包技术在焦点轮播上的应用 本文探讨了JavaScript编程语言中闭包技术在焦点轮播上的应用。闭包技术是JavaScript中一个重要的技术,它可以使函数内部的变量被外部函数访问。闭包技术的特点...
闭包在现在的很多流行的语言中都存在,例如 C++、C# 。闭包允许我 们创建函数指针,并把它们作为参数传递。在这篇文章中,将粗略的看一遍Java8的特性,并介绍 Lambda表达式。而且将试着放一些样例程序来解释一些概念...
闭包在程序设计中是一种非常重要的概念,尤其是在像PHP这样的动态类型语言中。闭包,简单来说,就是能够记住其被定义时的环境的函数,即使这个环境在函数执行时已经不再存在。它允许函数访问并操作外部作用域中的...
#### 一、NFA与ε闭包概念介绍 **非确定有限自动机(NFA)**是一种理论计算模型,它扩展了确定有限自动机(DFA)的概念,允许在某些情况下从一个状态出发到达多个状态。在NFA中,存在一种特殊的转移类型称为ε-转移,...
闭包是编程语言中的一个重要概念,特别是在JavaScript等函数式编程语言中。对于初学者来说,它可能显得有些抽象,但理解闭包对于提升编程技能至关重要。闭包是一种特殊的作用域,它允许函数访问并操作其外部作用域的...
闭包在很多编程语言中都有类似的概念,但在Swift中,它们的设计既简洁又灵活,使得闭包成为解决许多问题的关键工具。以下是对"Swift之闭包ClosureDemo"的详细解释。 首先,我们要理解闭包的基本结构。在Swift中,...
总的来说,"山东大学三元闭包实验"是一个结合理论与实践的教学案例,它涵盖了图论的基本概念、数据库关系的理解以及R语言的编程实践。通过参与这个实验,学生将有机会深入理解和运用三元闭包,这对于提高他们在IT...
Swift是一种强大的编程语言,尤其适用于iOS、iPadOS、macOS、watchOS和tvOS的应用开发。在Swift中,Closure(闭包)是一个重要的概念,它允许...同时,理解Swift闭包与Blocks的区别有助于写出更加高效、简洁的代码。
2. **闭包与匿名函数**:Python的`lambda`表达式可以创建匿名函数,这些函数也可以形成闭包。例如,`lambda x: x + 1`是一个简单的闭包,它能够记住外部作用域的值。 3. **非局部变量**:使用`nonlocal`关键字可以...
在编程语言理论中,"closure"一词通常指的是“闭包”,它是一个非常重要的概念,尤其是在函数式编程和动态类型语言中。闭包是函数和与其相关的引用环境(即变量的值)的组合,这个组合使得函数能够记住它被定义时的...
#### 一、闭包概念与特性 **闭包**是JavaScript语言的一个重要特性,它使得函数可以访问并操作其外部作用域内的变量,即使该函数在其外部作用域之外被调用。要理解闭包,首先需要了解JavaScript的作用域规则。 ###...
本地对象属于语言的一部分,而宿主对象则是由环境提供的(如DOM对象)。 - **属性赋值**:可以通过直接赋值的方式创建对象的属性,例如`objectRef.testNumber = 5`。 - **属性读取**:当从对象属性上读取值时,如果...
在Swift编程语言中,闭包(Closure)是一种强大的特性,它可以捕获和存储上下文中的变量,这使得闭包能够灵活地处理数据。当我们说"闭包作为属性"时,这意味着我们将一个闭包赋值给类或结构体的实例变量,从而让这个...
Block在Objective-C中与Swift的闭包相似,也是一种匿名函数,但其语法有所不同。Block定义通常包含在`^`符号内,并且可以直接捕获其定义范围内的变量。以下是一个Objective-C Block的例子: ```objc int (^addBlock...
迭代器、代码块和闭包是编程中的三个重要概念,特别是在Python这样的动态语言中,它们在构建高效、灵活的程序中扮演着关键角色。 首先,我们来深入理解迭代器。迭代器是一种对象,它能够按照特定顺序遍历一组数据,...