<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>为之漫笔</title>
	<atom:link href="http://lisf.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://lisf.wordpress.com</link>
	<description>天下事有难易乎？</description>
	<lastBuildDate>Wed, 01 Aug 2007 06:52:51 +0000</lastBuildDate>
	<generator>http://wordpress.com/</generator>
	<language>zh-cn</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<cloud domain='lisf.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://www.gravatar.com/blavatar/6d96841dd6cffe616f31a5688c2974f5?s=96&#038;d=http://s.wordpress.com/i/buttonw-com.png</url>
		<title>为之漫笔</title>
		<link>http://lisf.wordpress.com</link>
	</image>
			<item>
		<title>理解 JavaScript 闭包[未完成]</title>
		<link>http://lisf.wordpress.com/2007/08/01/understand-javascript-closures/</link>
		<comments>http://lisf.wordpress.com/2007/08/01/understand-javascript-closures/#comments</comments>
		<pubDate>Wed, 01 Aug 2007 06:52:51 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[转载]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/08/01/understand-javascript-closures/</guid>
		<description><![CDATA[原文链接：JavaScript Closures
目录

简介
对象属性名解析

值的赋予
值的读取


标识符解析、执行环境和作用域链

执行环境
作用域链与 [[scope]]
标识符解析


闭包

自动垃圾收集
构成闭包


通过闭包可以做什么？

例 1：为函数引用设置延时
例 2：通过对象实例方法关联函数
例 3：包装相关的功能
其他例子


意外的闭包
Internet Explorer 的内存泄漏问题

简介
返回目录

Closure
所谓“闭包”，指的是一个拥有许多变量和绑定了这些变量的环境的表达式（通常是一个函数），因而这些变量也是该表达式的一部分。 



闭包是 ECMAScript （JavaScript）最强大的特性之一，但用好闭包的前提是必须理解闭包。闭包的创建相对容易，人们甚至会在不经意间创建闭包，但这些无意创建的闭包却存在潜在的危害，尤其是在比较常见的浏览器环境下。如果想要扬长避短地使用闭包这一特性，则必须了解它们的工作机制。而闭包工作机制的实现很大程度上有赖于标识符（或者说对象属性）解析过程中作用域的角色。
关于闭包，最简单的描述就是 ECMAScript 允许使用内部函数－－即函数定义和函数表达式位于另一个函数的函数体内。而且，这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时，就会形成闭包。也就是说，内部函数会在外部函数返回后被执行。而当这个内部函数执行时，它仍然必需访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明（最初时）的值是外部函数返回时的值，但也会受到内部函数的影响。
遗憾的是，要适当地理解闭包就必须理解闭包背后运行的机制，以及许多相关的技术细节。虽然本文的前半部分并没有涉及 ECMA 262 规范指定的某些算法，但仍然有许多无法回避或简化的内容。对于个别熟悉对象属性名解析的人来说，可以跳过相关的内容，但是除非你对闭包也非常熟悉，否则最好是不要跳下面几节。
对象属性名解析
返回目录
ECMAScript 认可两类对象：原生（Native）对象和宿主（Host）对象，其中宿主对象包含一个被称为内置对象的原生对象的子类（ECMA 262 3rd Ed Section 4.3）。原生对象属于语言，而宿主对象由环境提供，比如说可能是文档对象、DOM 等类似的对象。
原生对象具有松散和动态的命名属性（对于某些实现的内置对象子类别而言，动态性是受限的－－但这不是太大的问题）。对象的命名属性用于保存值，该值可以是指向另一个对象（Objects）的引用（在这个意义上说，函数也是对象），也可以是一些基本的数据类型，比如：String、Number、Boolean、Null 或 Undefined。其中比较特殊的是 Undefined 类型，因为可以给对象的属性指定一个 Undefined 类型的值，而不会删除对象的相应属性。而且，该属性只是保存着 undefined 值。  
下面简要介绍一下如何设置和读取对象的属性值，并最大程度地体现相应的内部细节。
值的赋予
返回目录
对象的命名属性可以通过为该命名属性赋值来创建，或重新赋值。即，对于：
var objectRef = new Object(); //创建一个普通的 JavaScript 对象。
可以通过下面语句来创建名为 “testNumber” 的属性：
objectRef.testNumber = 5;
/* &#8211; 或- */
objectRef["testNumber"] = 5;
在赋值之前，对象中没有“testNumber” 属性，但在赋值后，则创建一个属性。之后的任何赋值语句都不需要再创建这个属性，而只会重新设置它的值：
objectRef.testNumber = 8;
/* &#8211; or:- */
objectRef["testNumber"] = 8;
稍后我们会介绍，Javascript [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=51&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>原文链接：<a target="_blank" href="http://www.jibbering.com/faq/faq_notes/closures.html" title="javascript closures">JavaScript Closures</a></p>
<h2>目录</h2>
<ul>
<li><a href="#clIntro">简介</a></li>
<li><a href="#clResO">对象属性名解析</a>
<ul>
<li><a href="#clResA">值的赋予</a></li>
<li><a href="#clResR">值的读取</a></li>
</ul>
</li>
<li><a href="#clIRExSc">标识符解析、执行环境和作用域链</a>
<ul>
<li><a href="#clExCon">执行环境</a></li>
<li><a href="#clScCh">作用域链与 [[scope]]</a></li>
<li><a href="#clIdRes">标识符解析</a></li>
</ul>
</li>
<li><a href="#clClose">闭包</a>
<ul>
<li><a href="#clAtGb">自动垃圾收集</a></li>
<li><a href="#clFrmC">构成闭包</a></li>
</ul>
</li>
<li><a href="#clClDo">通过闭包可以做什么？</a>
<ul>
<li><a href="#clSto">例 1：为函数引用设置延时</a></li>
<li><a href="#clObjI">例 2：通过对象实例方法关联函数</a></li>
<li><a href="#clEncap">例 3：包装相关的功能</a></li>
<li><a href="#clOtE">其他例子</a></li>
</ul>
</li>
<li><a href="#clAc">意外的闭包</a></li>
<li><a href="#clMem">Internet Explorer 的内存泄漏问题</a></li>
</ul>
<h2><a name="clIntro" title="clIntro" id="clIntro"></a>简介</h2>
<p><a href="#toc">返回目录</a></p>
<blockquote cite="http://groups.google.com/groups?selm=wu535hos.fsf@hotpop.com"><dl>
<dt><a name="clDefN" title="clDefN" id="clDefN"></a>Closure</dt>
<dd>所谓“闭包”，指的是一个拥有许多变量和绑定了这些变量的环境的表达式（通常是一个函数），因而这些变量也是该表达式的一部分。 </dd>
<dd></dd>
</dl>
</blockquote>
<p>闭包是 ECMAScript （JavaScript）最强大的特性之一，但用好闭包的前提是必须理解闭包。闭包的创建相对容易，人们甚至会在不经意间创建闭包，但这些无意创建的闭包却存在潜在的危害，尤其是在比较常见的浏览器环境下。如果想要扬长避短地使用闭包这一特性，则必须了解它们的工作机制。而闭包工作机制的实现很大程度上有赖于标识符（或者说对象属性）解析过程中作用域的角色。</p>
<p>关于闭包，最简单的描述就是 ECMAScript 允许使用内部函数－－即函数定义和函数表达式位于另一个函数的函数体内。而且，这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时，就会形成闭包。也就是说，内部函数会在外部函数返回后被执行。而当这个内部函数执行时，它仍然必需访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明（最初时）的值是外部函数返回时的值，但也会受到内部函数的影响。</p>
<p>遗憾的是，要适当地理解闭包就必须理解闭包背后运行的机制，以及许多相关的技术细节。虽然本文的前半部分并没有涉及 ECMA 262 规范指定的某些算法，但仍然有许多无法回避或简化的内容。对于个别熟悉对象属性名解析的人来说，可以跳过相关的内容，但是除非你对闭包也非常熟悉，否则最好是不要跳下面几节。</p>
<h2><a name="clResO" title="clResO" id="clResO"></a>对象属性名解析</h2>
<p><a href="#toc">返回目录</a></p>
<p>ECMAScript 认可两类对象：原生（Native）对象和宿主（Host）对象，其中宿主对象包含一个被称为内置对象的原生对象的子类（ECMA 262 3rd Ed Section 4.3）。原生对象属于语言，而宿主对象由环境提供，比如说可能是文档对象、DOM 等类似的对象。</p>
<p>原生对象具有松散和动态的命名属性（对于某些实现的内置对象子类别而言，动态性是受限的－－但这不是太大的问题）。对象的命名属性用于保存值，该值可以是指向另一个对象（Objects）的引用（在这个意义上说，函数也是对象），也可以是一些基本的数据类型，比如：String、Number、Boolean、Null 或 Undefined。其中比较特殊的是 Undefined 类型，因为可以给对象的属性指定一个 Undefined 类型的值，而不会删除对象的相应属性。而且，该属性只是保存着 <font face="Courier New">undefined </font>值。 <code> </code></p>
<p>下面简要介绍一下如何设置和读取对象的属性值，并最大程度地体现相应的内部细节。</p>
<h3><a name="clResA" title="clResA" id="clResA"></a>值的赋予</h3>
<p><a href="#toc">返回目录</a></p>
<p>对象的命名属性可以通过为该命名属性赋值来创建，或重新赋值。即，对于：</p>
<p>var objectRef = new Object(); <span class="commentJS">//创建一个普通的 JavaScript 对象。</span></p>
<p>可以通过下面语句来创建名为 “testNumber” 的属性：</p>
<p class="code">objectRef.testNumber = 5;<br />
<span class="commentJS">/* &#8211; 或- */</span><br />
objectRef["testNumber"] = 5;</p>
<p>在赋值之前，对象中没有“testNumber” 属性，但在赋值后，则创建一个属性。之后的任何赋值语句都不需要再创建这个属性，而只会重新设置它的值：</p>
<p class="code">objectRef.testNumber = 8;<br />
<span class="commentJS">/* &#8211; or:- */</span><br />
objectRef["testNumber"] = 8;</p>
<p>稍后我们会介绍，Javascript 对象都有原型（prototypes）属性，而这些原型本身也是对象，因而也可以带有命名的属性。但是，原型对象命名属性的作用并不体现在赋值阶段。同样，在将值赋给其命名属性时，如果对象没有该属性则会创建该命名属性，否则会重设该属性的值。</p>
<h3><a name="clResR" title="clResR" id="clResR"></a>值的读取</h3>
<p><a href="#toc">返回目录</a></p>
<p>当读取对象的属性值时，原型对象的作用便体现出来。如果对象的原型中包含属性访问器（property accessor）所使用的属性名，那么该属性的值就会返回：</p>
<p class="code">/* 为命名属性赋值。如果在赋值前对象没有相应的属性，那么赋值后就会得到一个：*/<br />
objectRef.testNumber = 8;</p>
<p class="code">/* 从属性中读取值 */<br />
var val = objectRef.testNumber;</p>
<p class="code">/* 现在， &#8211; val &#8211; 中保存着刚赋给对象命名属性的值 8*/</p>
<p>而且，由于所有对象都有原型，而原型本身也是对象，所以原型也可能有原型，这样就构成了所谓的原型链。原型链终止于链中原型为 null 的对象。<code>Object</code> 构造函数的默认原型就有一个 null 原型，因此：</p>
<p class="code">var objectRef = new Object(); //创建一个普通的 JavaScript 对象。</p>
<p>创建了一个原型为 <code>Object.prototype</code> 的对象，而该原型自身则拥有一个值为 null 的原型。也就是说， <code>objectRef</code> 的原型链中只包含一个对象－－ <code>Object.prototype</code>。但对于下面的代码而言：</p>
<p class="code">/* 创建 &#8211; MyObject1 &#8211; 类型对象的函数*/<br />
function MyObject1(formalParameter){<br />
/* 给创建的对象添加一个名为 &#8211; testNumber &#8211; 的属性并将传递给构造函数的第一个参数作为该属性的值：*/<br />
this.testNumber = formalParameter;<br />
}<br />
/* 创建 &#8211; MyObject2 &#8211; 类型对象的函数*/<br />
function MyObject2(formalParameter){<br />
/* 给创建的对象添加一个名为 &#8211; testString &#8211; 的属性并将传递给构造函数的第一个参数作为该属性的值：*/<br />
this.testString = formalParameter;<br />
}</p>
<p class="code">/* 接下来的操作用 MyObject1 类的实例替换了所有与 MyObject2 类的实例相关联的原型。而且，为 MyObject1 构造函数传递了参数 &#8211; 8 &#8211; ，因而其 &#8211; testNumber &#8211; 属性被赋予该值：*/<br />
MyObject2.prototype = new MyObject1( 8 );</p>
<p class="code">/* 最后，将一个字符串作为构造函数的第一个参数，创建一个 &#8211; MyObject2 &#8211; 的实例，并将指向该对象的引用赋给变量 &#8211; objectRef &#8211; ：*/<br />
var objectRef = new MyObject2( &#8220;String_Value&#8221; );</p>
<p>被变量 <code>objectRef</code> 所引用的 <code>MyObject2</code> 的实例拥有一个原型链。该链中的第一个对象是在创建后被指定给 <code>MyObject2</code> 构造函数的 <code>prototype</code> 属性的 <code>MyObject1</code> 的一个实例。<code>MyObject1</code> 的实例也有一个原型，即与 <code>Object.prototype</code> 所引用的对象对应的默认的 Object 对象的原型。最后， <code>Object.prototype</code> 有一个值为 null 的原型，因此这条原型链到此结束。</p>
<p>当某个属性访问器尝试读取由 <code>objectRef</code> 所引用的对象的属性值时，整个原型链都会被搜索。在下面这种简单的情况下：</p>
<p class="code">var val = objectRef.testString;</p>
<p>- 因为 <code>objectRef</code> 所引用的 <code>MyObject2</code> 的实例有一个名为“testString”的属性，因此被设置为“String_Value”的该属性的值被赋给了变量 <code>val</code>。但是：</p>
<p class="code">var val = objectRef.testNumber;</p>
<p>- 则不能从 <code>MyObject2</code> 实例自身中读取到相应的命名属性值，因为该实例没有这个属性。然而，变量 <code>val</code> 的值仍然被设置为 <code>8</code>，而不是未定义－－这是因为在该实例中查找相应的命名属性失败后，解释程序会继续检查其原型对象。而该实例的原型对象是 <code>MyObject1</code> 的实例，这个实例有一个名为“testNumber”的属性并且值为 <code>8</code>，所以这个属性访问器最后会取得值 <code>8</code>。而且，虽然<br />
<code>MyObject1</code> 和 <code>MyObject2</code> 都没有定义 <code>toString</code> 方法，但是当属性访问器通过 <code>objectRef</code> 读取 <code>toString</code> 属性的值时：</p>
<p class="code">var val = objectRef.toString;</p>
<p>- 变量 <code>val</code> 也会被赋予一个函数的引用。这个函数就是在 <code>Object.prototype</code> 的 <code>toString</code> 属性中所保存的函数。之所以会返回这个函数，是因为发生了搜索 <code>objectRef</code> 原型链的过程。当在作为对象的 <code>objectRef</code> 中发现没有“toString”属性存在时，会搜索其原型对象，而当原型对象中不存在该属性时，则会继续搜索原型的原型。而原型链中最终的原型是 <code>Object.prototype</code>，这个对象确实有一个 <code>toString</code> 方法，因此该方法的引用被返回。</p>
<p>最后：</p>
<p class="code">var val = objectRef.madeUpProperty;</p>
<p>- 返回 <code>undefined</code>，因为在搜索原型链的过程中，直至 <code>Object.prototype</code> 的原型－－null，都没有找到任何对象有名为“madeUpPeoperty”的属性，因此最终返回 <code>undefined</code>。</p>
<p>不论是在对象或对象的原型中，读取命名属性值的时候只返回首先找到的属性值。而当为对象的命名属性赋值时，如果对象自身不存在该属性则创建相应的属性。</p>
<p>这意味着，如果像 <code>objectRef.testNumber = 3</code> 执行一条赋值语句，那么 <code>MyObject2</code> 实例自身也会创建一个名为“testNumber”的属性，而之后任何读取该命名属性的尝试都将获得相同的新值。这时候，属性访问器不会再进一步搜索原型链，但 <code>MyObject1</code> 实例值为 <code>8</code> 的“testNumber”属性并没有被修改。给<br />
<code>objectRef</code> 对象的赋值只是遮挡了其原型链中相应的属性。</p>
<p>注意：ECMAScript 为 Object 类型定义了一个内部 <code>[[prototype]]</code> 属性。这个属性不能通过脚本直接访问，但在属性访问器解析过程中，则需要用到这个内部 <code>[[prototype]]</code> 属性所引用的对象链－－即原型链。可以通过一个公共的 <code>prototype</code> 属性，来对与内部的 <code>[[prototype]]</code> 属性对应的原型对象进行赋值或定义。这两者之间的关系在 ECMA 262（3rd edition）中有详细描述，但超出了本文要讨论的范畴。</p>
<h2><a name="clIRExSc" title="clIRExSc" id="clIRExSc"></a>标识符解析、执行环境和作用域链</h2>
<h3><a name="clExCon" title="clExCon" id="clExCon"></a>执行环境</h3>
<p><a href="#toc">返回目录</a></p>
<p>执行环境是 ECMAScript 规范（ECMA 262 第 3 版）用于定义 ECMAScript 实现必要行为的一个抽象的概念。对如何实现执行环境，规范没有作规定。但由于执行环境中包含引用规范所定义结构的相关属性，因此执行环境中应该保有（甚至实现）带有属性的对象－－即使属性不是公共属性。</p>
<p>所有 JavaScript 代码都是在一个执行环境中被执行的。全局代码（作为内置的JS 文件执行的代码，或者 <code>HTML</code> 页面加载的代码）是在我称之为“全局执行环境”的执行环境中执行的，而对函数的每次调用（<br />
有可能是作为构造函数）同样有关联的执行环境。通过 <code>eval</code> 函数执行的代码也有截然不同的执行环境，但因为 JavaScript 程序员在正常情况下一般不会使用 <code>eval</code>，所以这里不作讨论。有关执行环境的详细说明请参阅 ECMA 262（3rd edition）第 10.2 节。</p>
<p>当调用一个 JavaScript 函数时，该函数就会进入相应的执行环境。如果又调用了另外一个函数（或者递归地调用同一个函数），则又会创建一个新的执行环境，并且在函数调用期间执行过程都处于该环境中。当调用的函数返回后，执行过程会返回原始执行环境。因而，运行中的 JavaScript 代码就构成了一个执行环境栈。</p>
<p>在创建执行环境的过程中，会按照定义的先后顺序完成一系列操作。首先，在一个函数的执行环境中，会创建一个“活动”对象。活动对象是规范中规定的另外一种机制。之所以称之为对象，是因为它拥有可访问的命名属性，但是它又不像正常对象那样具有原型（至少没有定义的原型），而且不能通过 JavaScript 代码直接引用活动对象。</p>
<p>为函数调用创建执行环境的下一步是创建一个 <code>arguments</code> 对象，这是一个类似数组的对象，它以整数索引的数组成员一一对应地保存着调用函数时所传递的参数。这个对象也有 <code>length</code> 和 <code>callee</code> 属性（这两个属性与我们讨论的内容无关，详见规范）。然后，会为活动对象创建一个名为“arguments”的属性，该属性引用前面创建的 <code>arguments</code>对象。</p>
<p>接着，为执行环境分配作用域。作用域由对象列表（链）组成。每个函数对象都有一个内部的 <code>[[scope]]</code> 属性（该属性我们稍后会详细介绍），这个属性也由对象列表（链）组成。指定给一个函数调用执行环境的作用域，由该函数对象的 <code>[[scope]]</code> 属性所引用的对象列表（链）组成，同时，活动对象被添加到该对象列表的顶部（链的前端）。</p>
<p>之后会发生由 ECMA 262 中所谓“可变”对象完成的“变量实例化”的过程。只不过此时使用活动对象作为可变对象（这里很重要，请注意：它们是同一个对象）。此时会根据函数的形式参数为可变对象创建命名属性，如果调用函数时传递的参数与形式参数一致，则相应参数的值会赋给这些命名属性（否则，会给命名属性赋 <code>undefined</code> 值）。对于定义的内部函数，会以其声明时所用名称为可变对象创建同名属性，而这个内部函数则被创建为函数对象并指定给该属性。变量实例化的最后一步是根据函数内部声明的所有局部变量为可变对象创建命名属性。</p>
<p>根据声明的局部变量创建的可变对象属性在变量实例化期间会被赋予 <code>undefined</code> 值。在执行函数体内的代码、并计算相应的赋值表达式之前不会对局部变量执行真正的实例化。</p>
<p>事实上，拥有 <code>arguments</code> 属性的活动对象和拥有与函数局部变量一致的命名属性的可变对象是同一个对象。因此，可以将标识符 <code>arguments</code> 作为函数的局部变量来看待。</p>
<p>最后，要为使用 <code>this</code> 关键字而赋值。如果所赋的值引用一个对象，那么前缀以 <code>this</code> 关键字的属性访问器就是引用该对象的属性。如果所赋（内部）值是 null，那么 <code>this</code> 关键字则引用全局对象。</p>
<p>创建全局执行环境的过程会稍有不同，因为它没有参数，所以不需要通过定义的活动对象来引用这些参数。但全局执行环境也需要一个作用域，而它的作用域链实际上只由一个对象－－全局对象－－组成。全局执行环境也会有变量实例化的过程，它的内部函数就是涉及大部分 JavaScript 代码的、常规的顶级函数声明。而且，在变量实例化过程中全局对象就是可变对象，这就是为什么全局性声明的函数是全局对象属性的原因。全局性声明的变量同样如此。</p>
<p>全局执行环境也会使用 <code>this</code> 对象来引用全局对象。</p>
<h3><a name="clScCh" title="clScCh" id="clScCh"></a>作用域链与 [[scope]]</h3>
<p><a href="#toc">返回目录</a></p>
<p>调用函数时创建的执行环境会包含一个作用域链，这个作用域链是通过将该执行环境的活动（可变）对象添加到保存于所调用函数对象的 <code>[[scope]]</code> 属性中的作用域链前端而构成的。所以，理解函数对象内部的 <code>[[scope]]</code> 属性的定义过程是关键。</p>
<p>在 ECMAScript 中，函数也是对象。函数对象在变量实例化期间会根据函数声明来创建，或者是在计算函数表达式或调用 <code>Function</code> 构造器时创建。</p>
<p>通过调用 <code>Function</code> 构造器创建的函数对象，其内部的 <code>[[scope]]</code> 属性引用的作用域链中始终只包含全局对象。</p>
<p>通过函数声明或函数表达式创建的函数对象，其内部的 <code>[[scope]]</code> 属性引用的则是创建它们的执行环境的作用域链。</p>
<p>在最简单的情况下，比如声明如下全局函数：-</p>
<p class="code">function exampleFunction(formalParameter){<br />
&#8230; <span class="commentJS">// 函数体内的代码</span><br />
}</p>
<p>- 当为创建全局执行环境而进行变量实例化时，会根据上面的函数声明创建相应的函数对象。因为全局执行环境的作用域链中只包含全局对象，所以它就给自己创建的、并以名为“exampleFunction”的属性引用的这个函数对象的内部 <code>[[scope]]</code> 属性，赋予了只包含全局对象的作用域链。</p>
<p>当在全局环境中计算函数表达式时，也会发生类似的指定作用域链的过程：-</p>
<p>var exampleFuncRef = function(){<br />
&#8230; <span class="commentJS">// 函数体代码<br />
}</span><span class="commentJS">- 在这种情况下，不同的是在全局执行环境的变量实例化过程中，会先创建全局对象的一个命名属性。而在计算赋值语句之前，暂时不会创建函数对象，也不会将该函数对象的引用指定给全局对象的命名属性。但是，最终还是会在全局执行环境中创建这个函数对象（当计算函数表达式时。译者注），而为这个创建的函数对象的 <code>[[scope]]</code> 属性指定的作用域链中仍然只包含全局对象。</span><span class="commentJS">内部的函数声明或表达式会导致在包含它们的外部函数的执行环境中创建相应的函数对象，因此这些函数对象的作用域链会稍微复杂一些。在下面的代码中，先定义了一个带有内部函数声明的外部函数，然后执行了外部函数：-</span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></p>
<p class="code">function exampleOuterFunction(formalParameter){<br />
function exampleInnerFuncitonDec(){<br />
&#8230; // 内部函数体代码<br />
}<br />
&#8230; // 其余的外部函数体代码<br />
}<br />
exampleOuterFunction( 5 );</p>
<p>与外部函数声明对应的函数对象会在全局执行环境的变量实例化过程中被创建。因此，外部函数对象的 <code>[[scope]]</code> 属性中会包含一个只有全局对象的“单项目”作用域链。</p>
<p>当在全局执行环境中调用 <code>exampleOuterFunction</code> 函数时，会为该函数调用创建一个新的执行环境和一个活动（可变）对象。这个新执行环境的作用域就由新的活动对象后跟外部函数对象的 <code>[[scope]]</code> 属性所引用的作用域链（只有全局对象）构成。在新执行环境的变量实例化过程中，会创建一个与内部函数声明对应的函数对象，而同时会给这个函数对象的 <code>[[scope]]</code> 属性指定创建该函数对象的执行环境（即新执行环境。译者注）的作用域值－－即一个包含活动对象后跟全局对象的作用域链。</p>
<p>到目前为止，所有过程都是自动、或者由源代码的结构所控制的。但我们发现，执行环境的作用域链定义了执行环境所创建的函数对象的 <code>[[scope]]</code> 属性，而函数对象的 <code>[[scope]]</code> 属性则定义了它的执行环境的作用域（包括相应的活动对象）。不过，ECMAScript 也提供了用于修改作用域链 <code>with</code> 语句。</p>
<p><code>with</code> 语句会计算一个表达式，如果该表达式是一个对象，那么就将这个对象添加到当前执行环境的作用域链中（在活动&lt;可变&gt;对象之前）。然后，执行 <code>with</code> 语句（它自身也可能是一个语句块）中的其他语句。之后，又恢复到它之前的执行环境的作用域链中。</p>
<p><code>with</code> 语句不会影响在变量实例化过程中根据函数声明创建函数对象。但是，可以在一个 <code>with</code> 语句内部对函数表达式求值：-</p>
<p></span></p>
<pre>
<p class="code"> /* 创建全局变量 - y - 它引用一个对象：- */
var y = {x:5}; // 带有一个属性 - x - 的对象直接量
function exampleFuncWith(){
  var z;
  /* 将全局对象 - y - 引用的对象添加到作用域链的前端：- */
  with(y){
  /* 对函数表达式求值，以创建函数对象并将该函数对象的引用指定给局部变量 - z - :- */
  z = function(){
  ... // 内部函数表达式中的代码;
  }
}
...
}
/* 执行 - exampleFuncWith - 函数:- */</pre>
<p>exampleFuncWith();<span class="commentJS">在调用 <code>exampleFuncWith</code> 函数时创建的执行环境中包含一个由其活动对象后跟全局对象构成的作用域链。而在执行 <code>with</code> 语句时，又会把全局变量 <code>y</code> 引用的对象添加到这个作用域链的前端。在对其中的函数表达式求值的过程中，所创建函数对象的 <code>[[scope]]</code> 属性与创建它的执行环境的作用域保持一致－－即，该属性会引用一个由对象 <code>y</code> 后跟来自对外部函数调用时所创建执行环境的活动对象，后跟全局对象的作用域链。</span><span class="commentJS">当与 <code>with</code> 语句相关的语句块执行结束时，执行环境的作用域得以恢复（<code>y</code> 会被移除），但是已经创建的函数对象（<code>z</code>。译者注）的 <code>[[scope]]</code> 属性所引用的作用域链中位于最前面的仍然是对象 <code>y</code>。</span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></span><span class="commentJS"></p>
<h3><a name="clIdRes" title="clIdRes" id="clIdRes"></a>标识符解析</h3>
<p><a href="#toc">返回目录</a></p>
<p>标识符是沿作用域链逆向解析的。ECMA 262 将 <code>this</code> 归类为关键字而不是标识符，是不合理的。因为解析 <code>this</code> 值时始终要根据使用它的执行环境来判断，而与作用域链无关。</p>
<p>标识符解析的过程从作用域链中的第一个对象开始。检查该对象中是否包含与标识符对应的属性名。因为作用域链是一条对象链，所以这个检查过程中也会包含相应对象的原型链（如果有）。如果没有在作用域链的第一个对象中发现相应的值，解析过程会继续搜索下一个对象。这样依次类推直至找到作用域链中包含以标识符为属性名的对象为止，也有可能在作用域链的所有对象中都没有发现该标识符。</p>
<p>当在对象中使用如上所述的属性访问器时，也会发生同样的标识符解析操作。当属性访问器中有相应的属性替换对象时，该对象在作用域链中被确定，而标识符则充当该对象的属性名。全局对象始终都位于作用域链的尾端。</p>
<p>因为与函数调用相关的执行环境将会把活动（可变）对象添加到链的前端，所以在函数体内使用的标识符会首先检查自己是否与形式参数、内部函数声明的名称或局部变量一致。这些都可以由活动（可变）对象的命名属性来确定。</p>
<h2><a name="clClose" title="clClose" id="clClose"></a>闭包</h2>
<h3><a name="clAtGb" title="clAtGb" id="clAtGb"></a>自动垃圾收集</h3>
<p><a href="#toc">返回目录</a></p>
<p>ECMAScript 要求使用自动垃圾收集机制。但规范中并没有详细说明相关的细节，而是留给了实现来决定。但据了解，相当一些实现对它们的垃圾收集操作只赋予了很低的优先级。但是，大致的思想都是相同的，即如果对象不再“可引用（由于不存在对它的引用，使执行代码无法再访问到它）”时，该对象就成为垃圾收集的目标。因而，在将来的某个时刻会将这个对象销毁并将它所占用的一切资源释放，以便操作系统重新利用。</p>
<p>正常情况下，当退出一个执行环境时就会满足类似的条件。此时，作用域链结构中的活动（可变）对象以及在该执行环境中创建的任何对象－－包括函数对象，都不再“可引用”，因此将成为垃圾收集的目标。</p>
<h3><a name="clFrmC" title="clFrmC" id="clFrmC"></a>构成闭包</h3>
<p><a href="#toc">返回目录</a></p>
<p>闭包是通过在对一个函数调用的执行环境中返回一个函数对象而构成的。比如，在对函数调用的过程中，将一个对内部函数对象的引用指定给另一个对象的属性。或者，直接将这样一个（内部）函数对象的引用指定给一个全局变量、或者一个全局性对象的属性，或者一个作为参数以引用方式传递给外部函数的对象。例如：-</p>
<pre>
<p class="code">function exampleClosureForm(arg1, arg2){
var localVar = 8;
  function exampleReturned(innerArg){
    return ((arg1 + arg2)/(innerArg + localVar));
  }
  /* 返回一个定义为 exampleReturned 的内部函数的引用 -:- */
 return exampleReturned;
}
var globalVar = exampleClosureForm(2, 4);</pre>
<p>这种情况下，在调用外部函数 <code>exampleClosureForm</code> 的执行环境中所创建的函数对象就不会被当作垃圾收集，因为该函数对象被一个全局变量所引用，而且仍然是可以访问的，甚至可以通过 <code>globalVar(n)</code> 来执行。</p>
<p>的确，情况比正常的时候要复杂一些。因为现在这个被变量 <code>globalVar</code> 引用的内部函数对象的 <code>[[scope]]</code> 属性所引用的作用域链中，包含着属于创建该内部函数对象的执行环境的活动对象（和全局对象）。由于在执行被 <code>globalVar</code> 引用的函数对象时，每次都要把该函数对象的 <code>[[scope]]</code> 属性所引用的整个作用域链添加到创建的（内部函数的）执行环境的作用域中（即此时的作用域中包括：内部执行环境的活动对象、外部执行环境的活动对象、全局对象。译者注）， 所以这个（外部执行环境的）活动对象不会被当作垃圾收集。</p>
<p>闭包因此而构成。此时，内部函数对象拥有自由的变量，而位于该函数作用域链中的活动（可变）对象则成为与变量绑定的环境。</p>
<p>由于活动（可变）对象受限于内部函数对象（现在被 <code>globalVar</code> 变量引用）的 <code>[[scope]]</code> 属性中作用域链的引用，所以活动对象连同它的变量声明－－即属性的值，都会被保留。而在对内部函数调用的执行环境中进行作用域解析时，将会把与活动（可变）对象的命名属性一致的标识符作为该对象的属性来解析。活动对象的这些属性值即使是在创建它的执行环境退出后，仍然可以被读取和设置。</p>
<p>在上面的例子中，当外部函数返回（退出它的执行环境）时，其活动（可变）对象的变量声明中记录了形式参数、内部函数定义以及局部变量的值。<code>arg1</code> 属性的值为 <code>2</code>，而 <code>arg2</code> 属性的值为 <code>4</code>，<code>localVar</code> 的值是 <code>8</code>，还有一个 <code>exampleReturned</code> 属性，它引用由外部函数返回的内部函数对象。（为方便起见，我们将在后面的讨论中，称这个活动&lt;可变&gt;对象为 &#8220;ActOuter1&#8243;）。</p>
<p>如果再次调用 <code>exampleClosureForm</code> 函数，如：-</p>
<p class="code">var secondGlobalVar = exampleClosureForm(12, 3);</p>
<p>- 则会创建一个新的执行环境和一个新的活动对象。而且，会返回一个新的函数对象，该函数对象的 <code>[[scope]]</code> 属性引用的作用域链与前一次不同，因为这一次的作用域链中包含着第二个执行环境的活动对象，而这个活动对象的属性 <code>arg1</code> 值为 <code>12</code> 而属性 <code>arg2</code> 值为 <code>3</code>。（为方便起见，我们将在后面的讨论中，称这个活动&lt;可变&gt;对象为 &#8220;ActOuter2&#8243;）。</p>
<p>通过第二次执行 <code>exampleClosureForm</code> 函数，第二个也是截然不同的闭包诞生了。</p>
<p>通过执行 <code>exampleClosureForm</code> 创建的两个函数对象分别被指定给了全局变量 <code>globalVar</code> 和 <code>secondGlobalVar</code>，并返回了表达式 <code>((arg1 + arg2)/(innerArg + localVar))</code>。该表达式对其中的四个标识符应用了不同的操作符。如何确定这些标识符的值是体现闭包价值的关键所在。</p>
<p>我们来看一看，在执行由 <code>globalVar</code> 引用的函数对象－－如 <code>globalVar(2)</code>－－时的情形。此时，会创建一个新的执行环境和相应的活动对象（我们将称之为“ActInner1”），并把该活动对象添加到执行的函数对象的 <code>[[scope]]</code> 属性所引用的作用域链的前端。ActInner1 会带有一个属性 <code>innerArg</code>，根据传递的形式参数，其值被指定为 <code>2</code>。这个新执行环境的作用域链变是： <span class="scopeCh">ActInner1-&gt;</span><span class="scopeCh">ActOuter1-&gt;</span><span class="scopeCh">global object</span>.</p>
<p>为了返回表达式 <code>((arg1 + arg2)/(innerArg + localVar))</code> 的值，要沿着作用域链进行标识符解析。表达式中标识符的值将通过依次查找作用域链中的每个对象（与标识符名称一致）的属性来确定。</p>
<p>作用域链中的第一个对象是 ActInner1，它有一个名为 <code>innerArg</code> 的属性，值是 <code>2</code>。所有其他三个标识符在 ActOuter1 中都有对应的属性：<code>arg1</code> 是 <code>2</code>，<code>arg2</code> 是<br />
<code>4</code> 而 <code>localVar</code> 是 <code>8</code>。最后，函数调用返回 <code>((2 + 2)/(2 + 8))</code>。</p>
<p>现在再来看一看由 <code>secondGlobalVar</code> 引用的同一个函数对象的执行情况，比如 <code>secondGlobalVar(5)</code>。我们把这次新执行环境的活动对象称为 “ActInner2”，则作用域链就变成了：<span class="scopeCh">ActInner2-&gt;</span><span class="scopeCh">ActOuter2-&gt;</span><span class="scopeCh">global object</span>。ActInner2 返回 <code>innerArg</code> 的值 <code>5</code>，而 ActOuter2 分别返回 <code>arg1</code>、<code>arg2</code> 和 <code>localVar</code> 的值 <code>12</code>、<code>3</code> 和 <code>8</code>。函数调用返回的值就是 <code>((12 + 3)/(5 + 8))</code>。</p>
<p>如果再执行一次 <code>secondGlobalVar</code>，则又会有一个新活动对象被添加到作用域链的前端，但 ActOuter2 仍然是链中的第二个对象，而他的命名属性会再次用于完成标识符 <code>arg1</code>、<code>arg2</code> 和 <code>localVar</code> 的解析。</p>
<p>这就是 ECMAScript 的内部函数获取、维持和访问创建他们的执行环境的形式参数、声明的内部函数以及局部变量的过程。这个过程说明了构成闭包以后，内部的函数对象在其存续过程中，如何维持对这些值的引用、如何对这些值进行读取的机制。即，来自创建内部函数对象的执行环境的活动（可变）对象，会保留在该函数对象的 <code>[[scope]]</code> 属性所引用的作用域链中。直到所有对这个内部函数的引用被释放，这个函数对象才会成为垃圾收集的目标（连同它的作用域链中任何不再需要的对象）。</p>
<p>内部函数自身也可能有内部函数。在通过函数执行返回内部函数构成闭包以后，相应的闭包自身也可能会返回内部函数从而构成它们自己的闭包。每次作用域链嵌套，都会增加由创建内部函数对象的执行环境引发的新活动对象。ECMAScript 规范要求作用域链是临时性的，但对作用域链的长度却没有加以限制。在具体实现中，可能会存在实际的限制，但还没有发现有具体数量的报告。目前来看，嵌套的内部函数所拥有的潜能，仍然超出了使用它们的人的想像能力。</p>
<h2><a name="clClDo" title="clClDo" id="clClDo"></a>通过闭包可以做什么？</h2>
<p><a href="#toc">返回目录</a></p>
<p>Strangely the answer to that appears to be anything and everything.<br />
I am told that closures enable ECMAScript to emulate anything, so the<br />
limitation is the ability to conceive and implement the emulation. That<br />
is a bit esoteric and it is probably better to start with something a<br />
little more practical.</p>
<h3><a name="clSto" title="clSto" id="clSto"></a>例 1：为函数引用设置延时</h3>
<p><a href="#toc">返回目录</a></p>
<p>A common use for a closure is to provide parameters for the execution<br />
of a function prior to the execution of that function. For example,<br />
when a function is to be provided as the first argument to the<br />
<code>setTimout</code> function that is common in web browser<br />
environments.</p>
<p><code>setTimeout</code> schedules the execution of a function (or a<br />
string of javascript source code, but not in this context), provided as<br />
its first argument, after an interval expressed in milliseconds (as its<br />
second argument). If a piece of code wants to use<br />
<code>setTimeout</code> it calls the <code>setTimeout</code> function<br />
and passes a reference to a function object as the first argument and<br />
the millisecond interval as the second, but a reference to a function<br />
object cannot provide parameters for the scheduled execution of that<br />
function.</p>
<p>However, code could call another function that returned a reference to<br />
an inner function object, with that inner function object being passed<br />
by reference to the <code>setTimeout</code> function. The parameters to<br />
be used for the execution of the inner function are passed with the<br />
call to the function that returns it. <code>setTimout</code> executes<br />
the inner function without passing arguments but that inner function<br />
can still access the parameters provided by the call to the outer<br />
function that returned it:-</p>
<pre>
<p class="code">function callLater(paramA, paramB, paramC){
    <span class="commentJS">/* Return a reference to an anonymous inner function created
       with a function expression:-
    */</span>
    return (function(){
        <span class="commentJS">/* This inner function is to be executed with - setTimeout
           - and when it is executed it can read, and act upon, the
           parameters passed to the outer function:-
        */</span>
        paramA[paramB] = paramC;
    });
}
...
/* Call the function that will return a reference to the inner function
   object created in its execution context. Passing the parameters that
   the inner function will use when it is eventually executed as
   arguments to the outer function. The returned reference to the inner
   function object is assigned to a local variable:-
*/
var functRef = callLater(elStyle, "display", "none");
/* Call the setTimeout function, passing the reference to the inner
   function assigned to the - functRef - variable as the first argument:-
*/
hideMenu=setTimeout(functRef, 500);</pre>
<h3><a name="clObjI" title="clObjI" id="clObjI"></a>例 2: 通过对象实例方法关联函数</h3>
<p><a href="#toc">返回目录</a></p>
<p>There are many other circumstances when a reference to a function<br />
object is assigned so that it would be executed at some future time<br />
where it is useful to provide parameters for the execution of that<br />
function that would not be easily available at the time of execution<br />
but cannot be known until the moment of assignment.</p>
<p>One example might be a javascript object that is designed to<br />
encapsulate the interactions with a particular DOM element. It has<br />
<code>doOnClick</code>, <code>doMouseOver</code> and<br />
<code>doMouseOut</code> methods and wants to execute those methods<br />
when the corresponding events are triggered on the DOM element, but<br />
there may be any number of instances of the javascript object created<br />
associated with different DOM elements and the individual object<br />
instances do not know how they will be employed by the code that<br />
instantiated them. The object instances do not know how to reference<br />
themselves globally because they do not know which global variables<br />
(if any) will be assigned references to their instances.</p>
<p>So the problem is to execute an event handling function that has an<br />
association with a particular instance of the javascript object, and<br />
knows which method of that object to call.</p>
<p>The following example uses a small generalised closure based function<br />
that associates object instances with element event handlers.<br />
Arranging that the execution of the event handler calls the specified<br />
method of the object instance, passing the event object and a reference<br />
to the associated element on to the object method and returning the<br />
method&#8217;s return value.</p>
<pre>
<p class="code">/* A general function that associates an object instance with an event
   handler. The returned inner function is used as the event handler.
   The object instance is passed as the - obj - parameter and the name
   of the method that is to be called on that object is passed as the -
   methodName - (string) parameter.
*/
function associateObjWithEvent(obj, methodName){
    /* The returned inner function is intended to act as an event
       handler for a DOM element:-
    */
    return (function(e){
        /* The event object that will have been parsed as the - e -
           parameter on DOM standard browsers is normalised to the IE
           event object if it has not been passed as an argument to the
           event handling inner function:-
        */
        e = e||window.event;
        /* The event handler calls a method of the object - obj - with
           the name held in the string - methodName - passing the now
           normalised event object and a reference to the element to
           which the event handler has been assigned using the - this -
           (which works because the inner function is executed as a
           method of that element because it has been assigned as an
           event handler):-
        */
        return obj[methodName](e, this);
    });
}
/* This constructor function creates objects that associates themselves
   with DOM elements whose IDs are passed to the constructor as a
   string. The object instances want to arrange than when the
   corresponding element triggers onclick, onmouseover and onmouseout
   events corresponding methods are called on their object instance.
*/
function DhtmlObject(elementId){
    /* A function is called that retrieves a reference to the DOM
       element (or null if it cannot be found) with the ID of the
       required element passed as its argument. The returned value
       is assigned to the local variable - el -:-
    */
    var el = getElementWithId(elementId);
    /* The value of - el - is internally type-converted to boolean for
       the - if - statement so that if it refers to an object the
       result will be true, and if it is null the result false. So that
       the following block is only executed if the - el - variable
       refers to a DOM element:-
    */
    if(el){
        /* To assign a function as the element's event handler this
           object calls the - associateObjWithEvent - function
           specifying itself (with the - this - keyword) as the object
           on which a method is to be called and providing the name of
           the method that is to be called. The - associateObjWithEvent
           - function will return a reference to an inner function that
           is assigned to the event handler of the DOM element. That
           inner function will call the required method on the
           javascript object when it is executed in response to
           events:-
        */
        el.onclick = associateObjWithEvent(this, "doOnClick");
        el.onmouseover = associateObjWithEvent(this, "doMouseOver");
        el.onmouseout = associateObjWithEvent(this, "doMouseOut");
        ...
    }
}
DhtmlObject.prototype.doOnClick = function(event, element){
    ... // doOnClick method body.
}
DhtmlObject.prototype.doMouseOver = function(event, element){
    ... // doMouseOver method body.
}
DhtmlObject.prototype.doMouseOut = function(event, element){
    ... // doMouseOut method body.
}</pre>
<p>And so any instances of the <code>DhtmlObject</code> can associate themselves<br />
with the DOM element that they are interested in without any need<br />
to know anything about how they are being employed by other code,<br />
impacting on the global namespace or risking clashes with other<br />
instances of the <code>DhtmlObject</code>.</p>
<p></span></p>
<h3><a name="clEncap" title="clEncap" id="clEncap"></a>例 3：包装相关的功能</h3>
<p><a href="#toc">返回目录</a></p>
<p>Closures can be used to create additional scopes that can be used to<br />
group interrelated and dependent code in a way that minimises the risk<br />
of accidental interaction. Suppose a function is to build a string and<br />
to avoid the repeated concatenation operations (and the creation of<br />
numerous intermediate strings) the desire is to use an array to store<br />
the parts of the string in sequence and then output the results using<br />
the <code>Array.prototype.join</code> method (with an empty string as its argument).<br />
The array is going to act as a buffer for the output, but defining it<br />
locally to the function will result in its re-creation on each<br />
execution of the function, which may not be necessary if the only<br />
variable content of that array will be re-assigned on each function<br />
call.</p>
<p>One approach might make the array a global variable so that it can be<br />
re-used without being re-created. But the consequences of that will be<br />
that, in addition to the global variable that refers to the function<br />
that will use the buffer array, there will be a second global property<br />
that refers to the array itself. The effect is to render the code less<br />
manageable, as, if it is to be used elsewhere, its author has to remember<br />
to include both the function definition and the array definition. It<br />
also makes the code less easy to integrate with other code because<br />
instead of just ensuring that the function name is unique within the<br />
global namespace it is necessary to ensure that the Array on which it<br />
is dependent is using a name that is unique within the global<br />
namespace.</p>
<p>A Closure allows the buffer array to be associated (and neatly<br />
packaged) with the function that is dependent upon it and<br />
simultaneously keep the property name to which the buffer array as<br />
assigned out of the global namespace and free of the risk of name<br />
conflicts and accidental interactions.</p>
<p>The trick here is to create one additional execution context by<br />
executing a function expression in-line and have that function<br />
expression return an inner function that will be the function that is<br />
used by external code. The buffer array is then defined as a local<br />
variable of the function expression that is executed in-line. That only<br />
happens once so the Array is only created once, but is available to<br />
the function that depends on it for repeated use.</p>
<p>The following code creates a function that will return a string of<br />
<span class="initialism"><abbr title="HyperText Mark-up Language">HTML</abbr></span>, much of which is constant, but those constant character<br />
sequences need to be interspersed with variable information provided<br />
as parameter to the function call.</p>
<p>A reference to an inner function object is returned from the in-line<br />
execution of a function expression and assigned to a global variable<br />
so that it can be called as a global function. The buffer array is<br />
defined as a local variable in the outer function expression. It is<br />
not exposed in the global namespace and does not need to be re-created<br />
whenever the function that uses it is called.</p>
<pre>
<p class="code">/* A global variable - getImgInPositionedDivHtml - is declared and
   assigned the value of an inner function expression returned from
   a one-time call to an outer function expression.

   That inner function returns a string of <span class="initialism"><abbr title="HyperText Mark-up Language">HTML</abbr></span> that represents an
   absolutely positioned DIV wrapped round an IMG element, such that
   all of the variable attribute values are provided as parameters
   to the function call:-
*/
var getImgInPositionedDivHtml = (function(){
    /* The - buffAr - Array is assigned to a local variable of the
       outer function expression. It is only created once and that one
       instance of the array is available to the inner function so that
       it can be used on each execution of that inner function.

       Empty strings are used as placeholders for the date that is to
       be inserted into the Array by the inner function:-
    */
    var buffAr = [
        '&lt;div id="',
        '',   //index 1, DIV ID attribute
        '" style="position:absolute;top:',
        '',   //index 3, DIV top position
        'px;left:',
        '',   //index 5, DIV left position
        'px;width:',
        '',   //index 7, DIV width
        'px;height:',
        '',   //index 9, DIV height
        'px;overflow:hidden;\"&gt;&lt;img src=\"',
        '',   //index 11, IMG URL
        '\" width=\"',
        '',   //index 13, IMG width
        '\" height=\"',
        '',   //index 15, IMG height
        '\" alt=\"',
        '',   //index 17, IMG alt text
        '\"&gt;&lt;/div&gt;'
    ];
    /* Return the inner function object that is the result of the
       evaluation of a function expression. It is this inner function
       object that will be executed on each call to -
       getImgInPositionedDivHtml( ... ) -:-
    */
    return (function(url, id, width, height, top, left, altText){
        /* Assign the various parameters to the corresponding
           locations in the buffer array:-
        */
        buffAr[1] = id;
        buffAr[3] = top;
        buffAr[5] = left;
        buffAr[13] = (buffAr[7] = width);
        buffAr[15] = (buffAr[9] = height);
        buffAr[11] = url;
        buffAr[17] = altText;
        /* Return the string created by joining each element in the
           array using an empty string (which is the same as just
           joining the elements together):-
        */
        return buffAr.join('');
    }); //:End of inner function expression.
})();
/*^^- :The inline execution of the outer function expression. */</pre>
<p>If one function was dependent on one (or several) other functions, but<br />
those other functions were not expected to be directly employed by any<br />
other code, then the same technique could be used to group those<br />
functions with the one that was to be publicly exposed. Making a<br />
complex multi-function process into an easily portable and encapsulated<br />
unit of code.</p>
<h3><a name="clOtE" title="clOtE" id="clOtE"></a>其他例子</h3>
<p><a href="#toc">返回目录</a></p>
<p>Probably one of the best known applications of closures is<br />
<a href="http://www.crockford.com/javascript/private.html">Douglas<br />
Crockford&#8217;s technique for the emulation of private instance variables<br />
in ECMAScript objects</a>. Which can be extended to all sorts of<br />
structures of scope contained nested accessibility/visibility, including<br />
<a href="http://www.litotes.demon.co.uk/js_info/private_static.html"><br />
the emulation of private static members for ECMAScript objects</a>.</p>
<p>The possible application of closures are endless, understanding how<br />
they work is probably the best guide to realising how they can be<br />
used.</p>
<h2><a name="clAc" title="clAc" id="clAc"></a>意外的闭包</h2>
<p><a href="#toc">返回目录</a></p>
<p>Rendering any inner function accessible outside of the body of the<br />
function in which it was created will form a closure. That makes<br />
closures very easy to create and one of the consequences is that<br />
javascript authors who do not appreciate closures as a language feature<br />
can observe the use of inner functions for various tasks and employ<br />
inner functions, with no apparent consequences, not realising that<br />
closures are being created or what the implications of doing that are.</p>
<p>Accidentally creating closures can have harmful side effects as the<br />
following section on the IE memory leak problem describes, but they can<br />
also impact of the efficiency of code. It is not the closures<br />
themselves, indeed carefully used they can contribute significantly<br />
towards the creation of efficient code. It is the use of inner<br />
functions that can impact on efficiency.</p>
<p>A common situation is where inner functions are used is as event<br />
handlers for DOM elements. For example the following code might be used<br />
to add an onclick handler to a link element:-</p>
<pre>
<p class="code">/* Define the global variable that is to have its value added to the
   - href - of a link as a query string by the following function:-
*/
var quantaty = 5;
/* When a link passed to this function (as the argument to the function
   call - linkRef -) an onclick event handler is added to the link that
   will add the value of a global variable - quantaty - to the - href -
   of that link as a query string, then return true so that the link
   will navigate to the resource specified by the - href - which will
   by then include the assigned query string:-
*/
function addGlobalQueryOnClick(linkRef){
    /* If the - linkRef - parameter can be type converted to true
       (which it will if it refers to an object):-
    */
    if(linkRef){
        /* Evaluate a function expression and assign a reference to the
           function object that is created by the evaluation of the
           function expression to the onclick handler of the link
           element:-
        */
        linkRef.onclick = function(){
            /* This inner function expression adds the query string to
               the - href - of the element to which it is attached as
               an event handler:-
            */
            this.href += ('?quantaty='+escape(quantaty));
            return true;
        };
    }
}</pre>
<p>Whenever the <code>addGlobalQueryOnClick</code> function is called a<br />
new inner function is created (and a closure formed by its assignment).<br />
From the efficiency point of view that would not be significant if the<br />
<code>addGlobalQueryOnClick</code> function was only called once or<br />
twice, but if the function was heavily employed many distinct function<br />
objects would be created (one for each evaluation of the inner function<br />
expression).</p>
<p>The above code is not taking advantage of the fact that inner functions<br />
are becoming accessible outside of the function in which they are being<br />
created (or the resulting closures). As a result exactly the same effect<br />
could be achieved by defining the function that is to be used as the<br />
event handler separately and then assigning a reference to that<br />
function to the event handling property. Only one function object would<br />
be created and all of the elements that use that event handler would<br />
share a reference to that one function:-</p>
<pre>
<p class="code">/* Define the global variable that is to have its value added to the
   - href - of a link as a query string by the following function:-
*/
var quantaty = 5;
/* When a link passed to this function (as the argument to the function
   call - linkRef -) an onclick event handler is added to the link that
   will add the value of a global variable - quantaty - to the - href -
   of that link as a query string, then return true so that the link
   will navigate to the resource specified by the - href - which will
   by then include the assigned query string:-
*/
function addGlobalQueryOnClick(linkRef){
    /* If the - linkRef - parameter can be type converted to true
       (which it will if it refers to an object):-
    */
    if(linkRef){
        /* Assign a reference to a global function to the event
           handling property of the link so that it becomes the
           element's event handler:-
        */
        linkRef.onclick = forAddQueryOnClick;
    }
}
/* A global function declaration for a function that is intended to act
   as an event handler for a link element, adding the value of a global
   variable to the - href - of an element as an event handler:-
*/
function forAddQueryOnClick(){
    this.href += ('?quantaty='+escape(quantaty));
    return true;
}</pre>
<p>As the inner function in the first version is not being used to exploit<br />
the closures produced by its use, it would be more efficient not to use<br />
an inner function, and thus not repeat the process of creating many<br />
essentially identical function objects.</p>
<p>A similar consideration applies to object constructor functions. It is<br />
not uncommon to see code similar to the following skeleton constructor:-</p>
<pre>
<p class="code">function ExampleConst(param){
    /* Create methods of the object by evaluating function expressions
       and assigning references to the resulting function objects
       to the properties of the object being created:-
    */
    this.method1 = function(){
        ... // method body.
    };
    this.method2 = function(){
        ... // method body.
    };
    this.method3 = function(){
        ... // method body.
    };
    /* Assign the constructor's parameter to a property of the object:-
    */
    this.publicProp = param;
}</pre>
<p>Each time the constructor is used to create an object, with<br />
<code>new ExampleConst(n)</code>, a new set of function objects are<br />
created to act as its methods. So the more object instances that are<br />
created the more function objects are created to go with them.</p>
<p>Douglas Crockford&#8217;s technique for emulating private members on<br />
javascript objects exploits the closure resulting form assigning<br />
references to inner function objects to the public properties of a<br />
constructed object from within its constructor. But if the methods of<br />
an object are not taking advantage of the closure that they will form<br />
within the constructor the creation of multiple function objects for<br />
each object instantiation will make the instantiation process slower<br />
and more resources will be consumed to accommodate the extra function<br />
objects created.</p>
<p>In that case it would be more efficient to create the function object<br />
once and assign references to them to the corresponding properties of<br />
the constructor&#8217;s <code>prototype</code> so they may be shared by all<br />
of the objects created with that constructor:-</p>
<pre>
<p class="code">function ExampleConst(param){
    /* Assign the constructor's parameter to a property of the object:-
    */
    this.publicProp = param;
}
/* Create methods for the objects by evaluating function expressions
   and assigning references to the resulting function objects to the
   properties of the constructor's prototype:-
*/
ExampleConst.prototype.method1 = function(){
    ... // method body.
};
ExampleConst.prototype.method2 = function(){
    ... // method body.
};
ExampleConst.prototype.method3 = function(){
    ... // method body.
};</pre>
<h2><a name="clMem" title="clMem" id="clMem"></a>Internet Explorer 的内存泄漏问题</h2>
<p><a href="#toc">返回目录</a></p>
<p>The Internet Explorer web browser (verified on versions 4 to 6 (6 is<br />
current at the time of writing)) has a fault in its garbage collection<br />
system that prevents it from garbage collecting ECMAScript and some<br />
host objects if those host objects form part of a &#8220;circular&#8221;<br />
reference. The host objects in question are any DOM Nodes (including<br />
the document object and its descendants) and ActiveX objects. If a<br />
circular reference is formed including one or more of them, then<br />
none of the objects involved will be freed until the browser is closed<br />
down, and the memory that they consume will be unavailable to the<br />
system until that happens.</p>
<p>A circular reference is when two or more objects refer to each other in<br />
a way that can be followed and lead back to the starting point. Such<br />
as object 1 has a property that refers to object 2, object 2 has a<br />
property that refers to object 3 and object 3 has a property that<br />
refers back to object 1. With pure ECMAScript objects as soon as no<br />
other objects refer to any of objects 1, 2 or 3 the fact that they only<br />
refer to each other is recognised and they are made available for<br />
garbage collection. But on Internet Explorer, if any of those objects<br />
happen to be a DOM Node or ActiveX object, the garbage collection<br />
cannot see that the circular relationship between them is isolated<br />
from the rest of the system and free them. Instead they all stay in<br />
memory until the browser is closed.</p>
<p>Closures are extremely good at forming circular references. If a<br />
function object that forms a closure is assigned as, for example, and<br />
event handler on a DOM Node, and a reference to that Node is assigned<br />
to one of the Activation/Variable objects in its scope chain then a<br />
circular reference exists.<br />
<span class="scopeCh">DOM_Node.onevent -&gt;</span><span class="scopeCh">function_object.[[scope]] -&gt;</span><span class="scopeCh">scope_chain -&gt;</span><span class="scopeCh">Activation_object.nodeRef -&gt;</span><span class="scopeCh">DOM_Node</span>.</p>
<p>It is very easy to do, and a bit of browsing around a site that forms<br />
such a reference in a piece of code common to each page can consume<br />
most of the systems memory (possibly all).</p>
<p>Care can be taken to avoid forming circular references and remedial<br />
action can be taken when they cannot otherwise be avoided, such as<br />
using IE&#8217;s onunload event to null event handling function<br />
references. Recognising the problem and understanding closures<br />
(and their mechanism) is the key to avoiding this problem with IE.</p>
<p><a href="http://www.jibbering.com/faq/faq_notes/faq_notes.html#toc">comp.lang.javascript FAQ notes T.O.C.</a></p>
<ul>
<li>撰稿 <span class="person">Richard Cornford，</span>2004 年 3 月.</li>
<li>修改建议来自： 
<ul>
<li><span class="person">Martin Honnen</span>.</li>
<li><span class="person">Yann-Erwan Perio (Yep)</span>.</li>
<li><span class="person">Lasse Reichstein Nielsen</span>. (<a href="#clDefN">definition of closure</a>)</li>
<li><span class="person">Mike Scirocco</span>.</li>
<li><span class="person">Dr John Stockton</span>.</li>
</ul>
</li>
</ul>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/51/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/51/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/51/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/51/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/51/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=51&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/08/01/understand-javascript-closures/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>
	</item>
		<item>
		<title>Backreference 与 Backtracking</title>
		<link>http://lisf.wordpress.com/2007/07/31/backreference-and-backtracking/</link>
		<comments>http://lisf.wordpress.com/2007/07/31/backreference-and-backtracking/#comments</comments>
		<pubDate>Tue, 31 Jul 2007 14:56:44 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[正则表达式]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/07/31/backreference-and-backtracking/</guid>
		<description><![CDATA[backreference （反向引用）与 backtracking（回溯） 没有关系，它们是两个不同的概念（虽然从英文字面上看有类似之处，但翻译为中文还是宜将它们区分开来－－这一点余晟老师是正确的）。
反向引用，指的是在正则表达式中通过 \1、\2 等元序列（或者说是引用变量）引用前面分组的匹配项，以匹配相同的内容。（参见分组、反向引用与编号或命名组）
回溯，指的是重复的限定符（+、* ）在匹配模式时使用的一种回溯机制，或者说一种回吐机制（这是我个人的非正式说法）。
举个例子：比如在使用模式 &#34;.*- 匹配字符串 &#34;a-bc&#34; 时，正则表达式引擎首先会匹配模式和字符串开始处的 &#34;，然后模式中以限定符 * 限定的句点会匹配剩下的 a-bc&#34;（因为句点匹配除换行符之外的任何字符，而且默认的重复限定符都是贪婪的，会重复尽可能多次 . 元字符）。此时字符串中已经没有可以匹配的字符了，但模式中还有 &#8211; 这个组件没有匹配。由于使用重复限定符的模式还有回溯机制，所以此时不能承认匹配失败，而是尝试开始回溯－－也就是说此时引擎会逐个让出（give up）业已匹配的字符、首先是最后的 &#34; 与模式中的 &#8211; 进行匹配，结果没有匹配；然后引擎再次让出字符 c 与 &#8211; 匹配，仍然没有匹配，继续让出字符 b，还是没有匹配。而当让出字符 &#8211; 时，结果匹配了。此时模式中的所有组件都成功匹配，整个模式也取得了匹配项－－&#34;a-。这个反向倒溯、回吐的过程就是正则表达式中的回溯。
当然，这是在默认情况下，如果使用非贪婪（或懒惰）匹配限定符，即将模式修改为 &#34;.*?-，那么匹配过程就是：正则表达式引擎用模式中的第一个组件 &#34; 匹配字符串初始的直接量字符 &#34;，结果匹配；然后被 ? 限制的 * 限定符，将尽可能少地重复 . 元字符，因为 * 最少匹配零次，所以引擎首先会尝试重复 . 元字符零次，也就是直接用后面 &#8211; 匹配 a，结果当然不会匹配。但此时引擎不会就此甘休，而是会启动回溯机制，不过这时不是减少匹配字符，而是向前扩展式的“回溯”，即尝试重复一次 . 元字符，结果 . 元字符与 a 匹配，然后再用 &#8211; [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=67&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>backreference （反向引用）与 backtracking（回溯） 没有关系，它们是两个不同的概念（虽然从英文字面上看有类似之处，但翻译为中文还是宜将它们区分开来－－这一点<a target="_blank" href="http://www.luanxiang.org/blog/" title="《精通则表达式（第三版）》译者">余晟老师</a>是正确的）。</p>
<p>反向引用，指的是在正则表达式中通过 \1、\2 等元序列（或者说是引用变量）引用前面分组的匹配项，以匹配相同的内容。（参见<a target="_blank" href="http://www.cn-cuckoo.com/2007/07/25/group-back-reference-and-numberednamed-group-39.html" title="分组、反向引用与编号或命名组">分组、反向引用与编号或命名组</a>）</p>
<p>回溯，指的是重复的限定符（+、* ）在匹配模式时使用的一种回溯机制，或者说一种回吐机制（这是我个人的非正式说法）。</p>
<p>举个例子：比如在使用模式 &quot;.*- 匹配字符串 &quot;a-bc&quot; 时，正则表达式引擎首先会匹配模式和字符串开始处的 &quot;，然后模式中以限定符 * 限定的句点会匹配剩下的 a-bc&quot;（因为句点匹配除换行符之外的任何字符，而且默认的重复限定符都是贪婪的，会重复尽可能多次 . 元字符）。此时字符串中已经没有可以匹配的字符了，但模式中还有 &#8211; 这个组件没有匹配。由于使用重复限定符的模式还有回溯机制，所以此时不能承认匹配失败，而是尝试开始回溯－－也就是说此时引擎会逐个让出（give up）业已匹配的字符、首先是最后的 &quot; 与模式中的 &#8211; 进行匹配，结果没有匹配；然后引擎再次让出字符 c 与 &#8211; 匹配，仍然没有匹配，继续让出字符 b，还是没有匹配。而当让出字符 &#8211; 时，结果匹配了。此时模式中的所有组件都成功匹配，整个模式也取得了匹配项－－&quot;a-。这个反向倒溯、回吐的过程就是正则表达式中的回溯。</p>
<p>当然，这是在默认情况下，如果使用非贪婪（或懒惰）匹配限定符，即将模式修改为 &quot;.*?-，那么匹配过程就是：正则表达式引擎用模式中的第一个组件 &quot; 匹配字符串初始的直接量字符 &quot;，结果匹配；然后被 ? 限制的 * 限定符，将尽可能少地重复 . 元字符，因为 * 最少匹配零次，所以引擎首先会尝试重复 . 元字符零次，也就是直接用后面 &#8211; 匹配 a，结果当然不会匹配。但此时引擎不会就此甘休，而是会启动回溯机制，不过这时不是减少匹配字符，而是向前扩展式的“回溯”，即尝试重复一次 . 元字符，结果 . 元字符与 a 匹配，然后再用 &#8211; 匹配 a 后面的 -，结果匹配了。于是，非贪婪限定符在找到匹配项的前提下重复了尽量少的次数（1次），而且，模式中的所有组件都成功匹配，最终整个模式取得了匹配项－－&quot;a-。</p>
<p>显然，由于在默认情况下限定符为找到合适的匹配项都有可能要回溯，所以回溯存在占用资源的问题。而这个问题在使用嵌套的限定符时会变得非常突出。所谓嵌套的限定符，是指某个组中包含重复性的限定符，而这个组本身也应用了重复性限定符。因此，也就引出了占有式限定符（Possessive Quantifier）和最小化组（Atomic Group）的概念，这两个概念再谈吧，总之一句话说，成功就成功，失败就失败，不允许重复限定符启动回溯机制。</p>
<p>从以上分析可以看出，回溯与反向引用没有关系，至少是没有直接关系的。所以下面微软中文文档中的例子，虽然同时提到了反向引用和回溯，但只是强调一种可能性而已。</p>
<p class="code">Nonbacktracking Lookahead and Lookbehind </p>
<p class="code">Positive lookahead and lookbehind do not backtrack. That is, their contents are treated in the same way as the contents of a nonbacktracking (?&gt; ) group.</p>
<p class="code">Because lookahead and lookbehind are always zero-width, backtracking behavior is visible only when capturing groups appear within positive lookahead and lookbehind. For example, the expression (?=(a*))\1a will never find a match because group 1, which is defined within the lookahead, consumes as many &#8220;a&#8221; characters as there are, then \1a requires one more. Because the lookahead expression is not backtracked, the matching engine does not retry group 1 with fewer &#8220;a&#8221; characters.</p>
<p class="code">For more on grouping, lookahead, and lookbehind constructs, see Grouping Constructs</p>
<p>我的中文译文是：</p>
<p class="code">非回溯的向前查找及向后查找。</p>
<p class="code">肯定式向前查找和向后查找不进行回溯。换句话说，它们的内容〔注1〕被作为一个非回溯（?&gt;）组来处理。</p>
<p class="code">由于向前查找和向后查找都是零宽度〔注2〕，因此只有当肯定式向前查找和向后查找中存在捕获组时回溯行为才是有可能发生的〔注3〕。例如，表达式 (?=(a*))\1a 永远不会找到匹配项，因为在向前查找中定义的组 1 会占有全部的字符 a，而 \1a 同样也需要匹配一个以上的字符 a。由于向前查找表达式是不进行回溯的，所以匹配引擎〔注4〕不会重新尝试让组 1 匹配更少的字符 a〔注5〕。</p>
<p class="code">要了解有关分组、向前查找和向后查找的更多信息，请参考分组构造</p>
<p>注1：指向前查找或向后查找模式。<br />
注2：即只匹配不返回结果，或者说匹配的是位置。<br />
注3：指可能会在后面反向引用该捕获组时触发回溯－－事实上不会发生。<br />
注4：即正则表达式引擎。<br />
注5：即不会（也不可能）进行回溯。</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/67/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/67/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/67/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/67/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/67/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/67/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/67/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/67/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/67/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/67/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/67/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/67/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=67&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/07/31/backreference-and-backtracking/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>
	</item>
		<item>
		<title>推荐两本2008年可能走红的技术书</title>
		<link>http://lisf.wordpress.com/2007/07/31/recommend-two-books/</link>
		<comments>http://lisf.wordpress.com/2007/07/31/recommend-two-books/#comments</comments>
		<pubDate>Tue, 31 Jul 2007 08:03:14 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[原创]]></category>
		<category><![CDATA[好书]]></category>
		<category><![CDATA[转载]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/07/31/recommend-two-books/</guid>
		<description><![CDATA[第一本：《Prototype 和 script.aculo.us》
你不曾知道的 JavaScript 世界！
 http://www.pragmaticprogrammer.com/titles/cppsu/index.html
 
作者：Christophe Porteneuve
页数: 330
ISBN: 1-934356-01-8
日期: 2007年10月
 
Prototype and script.aculo.us
在 Web 2.0 时代的 JavaScript 编程中，还没有因为纠缠不清的跨浏览器细节而感到厌烦吗？投入 Prototype 和 script.aculo.us 的怀抱吧，这两个极其流行的 JavaScript 类库，会让你的 JavaScript 编程体验胜似闲庭信步。不仅对 AJAX 编程的支持独树一帜，而且在拖放、自动完成、高级视觉效果和其他极其有用的特性方面也令人不禁拍案叫绝。通过使用这两个库，你要做的事只剩写一两行脚本，而且这些脚本看起来比 Ruby 的代码有过之而不无及!
关于本书
Web 应用程序的种类越来越丰富，而且日益具有了更高的交互性。但是，JavaScript、DOM、CSS 和一大堆的 Web 标准虽然相当复杂，却仍然不能保证结果被浏览器兼容。
而 Prototype 和 script.aculo.us 类库这两个有待发掘的宝库，却能够轻松地抚平浏览器间的种种差异，使得实现最常用的功能变成了小事一桩。通过学习本书，你可以迅速地掌握如何发挥这两个卓而不群的类库的强大威力。
深入 Prototype 中，你会发现，这个类库把 JavaScript 武装得如此强大，甚至会有人误以为是 Ruby 的代码。遍历 DOM、处理事件、运筹 AJAX 以及从根本上简化大多数的脚本编写量－－这些在 Prototype 中都成为了一件轻而易举的事。
在开发基于高级界面功能的应用时，script.aculo.us 使得每一位 Web 开发者得以梦想成真－－无论是需要创建能够自动完成的文本输入框，还是实现适当的文本编辑器，还是提供自定义的拖放功能，或者是通过视觉效果吸引用户的眼球，亦或是提高构建 DOM 片段的效率，应有尽有－－而且也都是通过轻量级代码实现。
本书会引导你深入研究这些功能的全部细节，通过融合不同的服务器端技术，如 PHP、vanilla Ruby 和 [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=65&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>第一本：《Prototype 和 script.aculo.us》<br />
你不曾知道的 JavaScript 世界！<br />
 <a href="http://www.pragmaticprogrammer.com/titles/cppsu/index.html">http://www.pragmaticprogrammer.com/titles/cppsu/index.html</a><br />
 <br />
作者：Christophe Porteneuve<br />
页数: 330<br />
ISBN: 1-934356-01-8<br />
日期: 2007年10月<br />
 <br />
Prototype and script.aculo.us<br />
在 Web 2.0 时代的 JavaScript 编程中，还没有因为纠缠不清的跨浏览器细节而感到厌烦吗？投入 Prototype 和 script.aculo.us 的怀抱吧，这两个极其流行的 JavaScript 类库，会让你的 JavaScript 编程体验胜似闲庭信步。不仅对 AJAX 编程的支持独树一帜，而且在拖放、自动完成、高级视觉效果和其他极其有用的特性方面也令人不禁拍案叫绝。通过使用这两个库，你要做的事只剩写一两行脚本，而且这些脚本看起来比 Ruby 的代码有过之而不无及!</p>
<p>关于本书<br />
Web 应用程序的种类越来越丰富，而且日益具有了更高的交互性。但是，JavaScript、DOM、CSS 和一大堆的 Web 标准虽然相当复杂，却仍然不能保证结果被浏览器兼容。</p>
<p>而 Prototype 和 script.aculo.us 类库这两个有待发掘的宝库，却能够轻松地抚平浏览器间的种种差异，使得实现最常用的功能变成了小事一桩。通过学习本书，你可以迅速地掌握如何发挥这两个卓而不群的类库的强大威力。</p>
<p>深入 Prototype 中，你会发现，这个类库把 JavaScript 武装得如此强大，甚至会有人误以为是 Ruby 的代码。遍历 DOM、处理事件、运筹 AJAX 以及从根本上简化大多数的脚本编写量－－这些在 Prototype 中都成为了一件轻而易举的事。</p>
<p>在开发基于高级界面功能的应用时，script.aculo.us 使得每一位 Web 开发者得以梦想成真－－无论是需要创建能够自动完成的文本输入框，还是实现适当的文本编辑器，还是提供自定义的拖放功能，或者是通过视觉效果吸引用户的眼球，亦或是提高构建 DOM 片段的效率，应有尽有－－而且也都是通过轻量级代码实现。</p>
<p>本书会引导你深入研究这些功能的全部细节，通过融合不同的服务器端技术，如 PHP、vanilla Ruby 和 Ruby On Rails，运用大量的实例来示范这些功能的方方面面。有能力的用户还可以学习到这些类库的设计原理，以及如何根据自己的需要发布并扩展这些类库。<br />
第二本：《Prototype 和 Scriptaculous实战》<br />
<a href="http://www.manning.com/crane3/">http://www.manning.com/crane3/</a></p>
<p>Dave Crane and Bear Bibeault with Tom Locke<br />
出版日期：2007年3月<br />
页数：544<br />
ISBN: 1-933988-03-7</p>
<p>本书简介</p>
<p>Common Ajax tasks should be easy, and with Prototype and Scriptaculous they are. Prototype and Scriptaculous are libraries of reusable JavaScript code that simplify Ajax development. Prototype provides helpful methods and objects that extend JavaScript in a safe, consistent way. Its clever Ajax request model simplifies cross-browser development. Scriptaculous, which is based on Prototype, offers handy pre-fabricated widgets for rich UI development.</p>
<p>Prototype and Scriptaculous in Action is a comprehensive, practical guide that walks you feature-by-feature through the two libraries. First, you抣l use Scriptaculous to make easy but powerful UI improvements. Then you抣l dig into Prototype抯 elegant and sparse syntax. See how a few characters of Prototype code can save a dozen lines of JavaScript. By applying these techniques, you can concentrate on the function and flow of your application instead of the coding details. This book is written for web developers with a working knowledge of JavaScript.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/65/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/65/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/65/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/65/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/65/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=65&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/07/31/recommend-two-books/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>
	</item>
		<item>
		<title>《精通正则表达式》书评</title>
		<link>http://lisf.wordpress.com/2007/07/31/book-review-about-mastery-regular-expressions/</link>
		<comments>http://lisf.wordpress.com/2007/07/31/book-review-about-mastery-regular-expressions/#comments</comments>
		<pubDate>Tue, 31 Jul 2007 04:23:23 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[好书]]></category>
		<category><![CDATA[正则表达式]]></category>
		<category><![CDATA[转载]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/07/31/book-review-about-mastery-regular-expressions/</guid>
		<description><![CDATA[作者：孟岩（原文） 
IT产业新技术日新月异，令人目不暇接，然而在这其中，真正称得上伟大东西的却寥寥无几。1998年，被誉为“软件世界的爱迪生”，发明了 BSD、 TCP/IP、csh、vi 和 NFS 的 SUN 首席科学家 Bill Joy 曾经不无调侃地说，在计算机体系结构领域里，缓存是唯一称得上伟大的思想，其他的一切发明和技术不过是在不同场景下应用这一思想而已。在计算机软件领域里，情形也大体相似。如果罗列这个领域中的伟大发明，我相信绝不会超过二十项。在这个名单当中，当然应该包括分组交换网络、Web、Lisp、哈希算法、UNIX、编译技术、关系模型、面向对象、XML 这些大名鼎鼎的家伙，而正则表达式也绝对不应该被漏掉。正则表达式具有伟大技术发明的一切特点，它简单，优美，功能强大，妙用无穷。对于很多实际工作来讲，正则表达式简直是灵丹妙药，能够成百倍地提高开发效率和程序质量。CSDN 的创始人蒋涛先生在早年开发专业软件产品时，就曾经体验过这一工具的巨大威力，并且一直印象深刻。而我的一位从事网络编辑工作的朋友，最近也领略了正则表达式的威力——他用 Perl 开发了一个不足 20 行的小程序，使用正则表达式将一项原本每天耗用10个人时的工作在一分钟之内自动完成。而正则表达式在生物信息学和人类基因图谱的研究中所发挥的关键作用，更是被传为佳话。无论对于软件开发者，还是从事其他知识工作的专业人士，正则表达式都是最有利的工具之一。
所谓正则表达式，就是一种描述字符串结构模式的形式化表达方法。在发展的初期，这套方法仅限于描述正则文本，故此得名“正则表达式（regular expression）”。随着正则表达式研究的深入和发展，特别是 Perl 语言的实践和探索，正则表达式的能力已经大大突破了传统的、数学上的限制，成为威力巨大的实用工具，在几乎所有主流语言中获得支持。为什么正则表达式具有如此巨大的魅力？一方面，因为正则表达式处理的对象是字符串，或者抽象地说，是一个对象序列，而这恰恰是当今计算机体系的本质数据结构，我们围绕计算机所做的大多数工作，都归结为在这个序列上的操作，因此，正则表达式用途广阔。另 一方面，与大多数其他技术不同，正则表达式具有超强的结构描述能力，而在计算机中，正是不同的结构把无差别的字节组织成千差万别的软件对象，再组合成为无所不能的软件系统，因此，描述了结构，就等于描述了系统。在这方面，正则表达式的地位是独特的。正因为这两点，在现在的软件开发和日常数据处理工作中，正则表达式已经是必不可少的工具。如果一个开发工具不支持正则表达式，那它就会被视为玩具语言，如果一个编辑器不支持正则表达式，那它就会被称为阳春应用。 连人们原本并不指望应用正则表达式的商用数据库，各家厂商也竞相以支持正则表达式为卖点。正则表达式的声势之隆，是毋庸置疑的。
非常奇怪的是，这样一个了不起的技术，在我国却并没有得到充分推广。以其价值而言，正则表达式不但值得每一个专业程序员掌握，而且值得所有知识工作者去了 解。然而现实情况是，不但一般知识工作者大多闻所未闻，很多专业程序员也视之为畏途。为什么会出现这种情况呢？原因有二。其一，正则表达式产生和发展在 UNIX 文化体系之中，而我国软件开发社群的知识结构长期受到微软的决定，UNIX 文化影响甚微。在 2002 年推出 .NET 平台之前，微软在其各项主流平台、产品与开发工具当中，均未对正则表达式给予足够重视，相应地，我们的开发者们对正则表达式也就知之不多。第二，也是更重要的原因，就是正则表达式并不是那么好掌握的，在通向驾驭正则表达式强大力量的道路上，还是有那么几只拦路虎的，而要打虎过岗，不但要花点功夫，还要有正确的方法。
学习正则表达式，入门不难，看一些例子，试着模仿模仿，就可以粗通，并且在工作中解决不少问题。然而大部分学习者也就就此止步，他们对自己说：“正则表达式不过如此，我就学到这里了，以后现用现学就行了”。他们以为自己可以像学习其他技术一样，在实践中逐渐提高正则表达式的应用水平。然而事实上，正则表达式并不是每天都会用到，而其密码般的形象，随着时间的推移很容易被忘记，所以经常发生的情况是，开发者对于正则表达式的记忆迅速消退，每次遇到新的问题， 都要查资料，重新唤回记忆，对于稍微复杂一点的问题，只好求助于现成的解决方案。反反复复，长期如此，不但应用水平难以明显提升，而且逐渐对这项技术产生一定的恐惧感和厌烦情绪。这还只是应用阶段，正则表达式应用的高级阶段，要求开发者此外还必须充分理解正则表达式的能力范围，能够将一些正则表达式技术组合应用，达成超乎一般想像的效果。为了高效、正确地解决实际问题，有的时候甚至要求深入理解正则表达式的原理，甚至对于如何实现正则表达式引擎都有所了解，在此基础上，规避陷阱，优化设计，提高程序执行效率。要达到这样的程度，不经过系统的学习是不可能的。
系统学习正则表达式并不是一件容易的事情，仅仅通过阅读一些“HOW TO”的快餐式的文章是不行的，必须有更完整、更系统的资料指导学习。如果你在国外技术社区里询问如何才能系统学习正则表达式，几乎所有的领域专家都会向你推荐一本书——Jeffrey Friedl 的《精通正则表达式》，也就是本书。
这本《精通正则表达式》是系统学习正则表达式的唯一最权威著作。可以说，在今天，如果想理解和掌握正则表达式，想要建立关于这一技术的完整概念体系，想充分发挥其巨大能量，这本书几乎是无法绕开的必经之路。甚至可以说，如果你没有读过这本书，那么你对于正则表达式的理解和应用能力一定达不到升堂入室的程度。本书第一版出于十年之前，自那时起就成为正则表达式领域最全面、最受欢迎的代表著作，数以万计的读者通过这本书掌握了正则表达式，成为行家里手。在任何时候，任何地方，只要提到正则表达式著作，人们都会提到这本书。这本书的质量之高，声誉之盛，使得几乎没有人企图挑战它的地位，从而在正则表达式图书领域形成独特的“一夫当关”的局面，称其为正则表达式圣经，绝对当之无愧。
为什么这本书能够表现得如此出色？我认为这其中有三个原因。其一，作者本人具有多年程序开发经验，理论基础深厚，实战经验丰富，对正则表达式这个主题透彻理解，因此在技术上得心应手，底气十足，对于技术上的难点不回避、不含糊。作者高超的技术水平是本书质量的强大保证。其二，作者思路对头，素材组织得当，用例丰富。正则表达式根植于数学理论，却又能在日常俗事上发挥巨大的效用。写这种类型的技术，思路稍微一偏差，就可能走歪路，不是太理论，就是太琐碎，不是太枯燥，就是太浅薄，实在很难把握。作者清楚地认识到，这本书的读者不是计算机科学家，但也不是满足于“知其然而不知其所以然”的快餐式代码小子，而是具有一定理论素养，却又始终以实践为本的专业开发者。他们需要的是面向实践的理论和思想，是实实在在的实战能力，只有满足这种需要，才能够真正打动读者。 通读此书，可以说作者对这一路线的把握十分成功，保证了内容大方向的正确。其三，这本书的写法独具匠心，堪称典范。技术图书的主要使命是传播专业知识。而专业知识分为框架性知识和具体知识。框架性知识需要通过系统的阅读和学习掌握，而大量的具体知识，则主要通过日常工作的积累以及随用随查的的学习来逐渐填充起来。本书前六章，以顺序式记述的方式，将正则表达式的系统知识娓娓道来，读者像看故事书似的就建立起整个正则表达式的基本知识体系。而后面的内容，则是方便实际开发中频繁查阅之用，包括各大主流语言对正则表达式的支持细节，包含有大量案例。这样的写法，完全符合一般人学习的特点，因此书读起来非常惬意，非常有趣，而用的时候查起来又非常方便。这样的著述风格，实在值得学习。
读者可以在没有任何正则表达式的基础上开始阅读此书，只要勤动脑，加强理解，适当动手练习，将能够在不长的时间里掌握正则表达式的思想和技术精华，这一点已经被很多人验证过，我本人也是这本书的受益者之一。正因为这本书独一无二的地位和高度的可读性，也因为正则表达式作为一项了不起的技术发明所具有的巨大威力，我非常希望更多的读者能够通过认真地学习本书而掌握这一强大技术，并享受阅读的乐趣。
       <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=64&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>作者：孟岩（<a target="_blank" href="http://blog.csdn.net/myan/archive/2007/07/23/1703404.aspx" title="《精通则表达式》书评">原文</a>） </p>
<p>IT产业新技术日新月异，令人目不暇接，然而在这其中，真正称得上伟大东西的却寥寥无几。1998年，被誉为“软件世界的爱迪生”，发明了 BSD、 TCP/IP、csh、vi 和 NFS 的 SUN 首席科学家 Bill Joy 曾经不无调侃地说，在计算机体系结构领域里，缓存是唯一称得上伟大的思想，其他的一切发明和技术不过是在不同场景下应用这一思想而已。在计算机软件领域里，情形也大体相似。如果罗列这个领域中的伟大发明，我相信绝不会超过二十项。在这个名单当中，当然应该包括分组交换网络、Web、Lisp、哈希算法、UNIX、编译技术、关系模型、面向对象、XML 这些大名鼎鼎的家伙，而正则表达式也绝对不应该被漏掉。正则表达式具有伟大技术发明的一切特点，它简单，优美，功能强大，妙用无穷。对于很多实际工作来讲，正则表达式简直是灵丹妙药，能够成百倍地提高开发效率和程序质量。CSDN 的创始人蒋涛先生在早年开发专业软件产品时，就曾经体验过这一工具的巨大威力，并且一直印象深刻。而我的一位从事网络编辑工作的朋友，最近也领略了正则表达式的威力——他用 Perl 开发了一个不足 20 行的小程序，使用正则表达式将一项原本每天耗用10个人时的工作在一分钟之内自动完成。而正则表达式在生物信息学和人类基因图谱的研究中所发挥的关键作用，更是被传为佳话。无论对于软件开发者，还是从事其他知识工作的专业人士，正则表达式都是最有利的工具之一。</p>
<p>所谓正则表达式，就是一种描述字符串结构模式的形式化表达方法。在发展的初期，这套方法仅限于描述正则文本，故此得名“正则表达式（regular expression）”。随着正则表达式研究的深入和发展，特别是 Perl 语言的实践和探索，正则表达式的能力已经大大突破了传统的、数学上的限制，成为威力巨大的实用工具，在几乎所有主流语言中获得支持。为什么正则表达式具有如此巨大的魅力？一方面，因为正则表达式处理的对象是字符串，或者抽象地说，是一个对象序列，而这恰恰是当今计算机体系的本质数据结构，我们围绕计算机所做的大多数工作，都归结为在这个序列上的操作，因此，正则表达式用途广阔。另 一方面，与大多数其他技术不同，正则表达式具有超强的结构描述能力，而在计算机中，正是不同的结构把无差别的字节组织成千差万别的软件对象，再组合成为无所不能的软件系统，因此，描述了结构，就等于描述了系统。在这方面，正则表达式的地位是独特的。正因为这两点，在现在的软件开发和日常数据处理工作中，正则表达式已经是必不可少的工具。如果一个开发工具不支持正则表达式，那它就会被视为玩具语言，如果一个编辑器不支持正则表达式，那它就会被称为阳春应用。 连人们原本并不指望应用正则表达式的商用数据库，各家厂商也竞相以支持正则表达式为卖点。正则表达式的声势之隆，是毋庸置疑的。</p>
<p>非常奇怪的是，这样一个了不起的技术，在我国却并没有得到充分推广。以其价值而言，正则表达式不但值得每一个专业程序员掌握，而且值得所有知识工作者去了 解。然而现实情况是，不但一般知识工作者大多闻所未闻，很多专业程序员也视之为畏途。为什么会出现这种情况呢？原因有二。其一，正则表达式产生和发展在 UNIX 文化体系之中，而我国软件开发社群的知识结构长期受到微软的决定，UNIX 文化影响甚微。在 2002 年推出 .NET 平台之前，微软在其各项主流平台、产品与开发工具当中，均未对正则表达式给予足够重视，相应地，我们的开发者们对正则表达式也就知之不多。第二，也是更重要的原因，就是正则表达式并不是那么好掌握的，在通向驾驭正则表达式强大力量的道路上，还是有那么几只拦路虎的，而要打虎过岗，不但要花点功夫，还要有正确的方法。</p>
<p>学习正则表达式，入门不难，看一些例子，试着模仿模仿，就可以粗通，并且在工作中解决不少问题。然而大部分学习者也就就此止步，他们对自己说：“正则表达式不过如此，我就学到这里了，以后现用现学就行了”。他们以为自己可以像学习其他技术一样，在实践中逐渐提高正则表达式的应用水平。然而事实上，正则表达式并不是每天都会用到，而其密码般的形象，随着时间的推移很容易被忘记，所以经常发生的情况是，开发者对于正则表达式的记忆迅速消退，每次遇到新的问题， 都要查资料，重新唤回记忆，对于稍微复杂一点的问题，只好求助于现成的解决方案。反反复复，长期如此，不但应用水平难以明显提升，而且逐渐对这项技术产生一定的恐惧感和厌烦情绪。这还只是应用阶段，正则表达式应用的高级阶段，要求开发者此外还必须充分理解正则表达式的能力范围，能够将一些正则表达式技术组合应用，达成超乎一般想像的效果。为了高效、正确地解决实际问题，有的时候甚至要求深入理解正则表达式的原理，甚至对于如何实现正则表达式引擎都有所了解，在此基础上，规避陷阱，优化设计，提高程序执行效率。要达到这样的程度，不经过系统的学习是不可能的。</p>
<p>系统学习正则表达式并不是一件容易的事情，仅仅通过阅读一些“HOW TO”的快餐式的文章是不行的，必须有更完整、更系统的资料指导学习。如果你在国外技术社区里询问如何才能系统学习正则表达式，几乎所有的领域专家都会向你推荐一本书——Jeffrey Friedl 的《精通正则表达式》，也就是本书。</p>
<p>这本《精通正则表达式》是系统学习正则表达式的唯一最权威著作。可以说，在今天，如果想理解和掌握正则表达式，想要建立关于这一技术的完整概念体系，想充分发挥其巨大能量，这本书几乎是无法绕开的必经之路。甚至可以说，如果你没有读过这本书，那么你对于正则表达式的理解和应用能力一定达不到升堂入室的程度。本书第一版出于十年之前，自那时起就成为正则表达式领域最全面、最受欢迎的代表著作，数以万计的读者通过这本书掌握了正则表达式，成为行家里手。在任何时候，任何地方，只要提到正则表达式著作，人们都会提到这本书。这本书的质量之高，声誉之盛，使得几乎没有人企图挑战它的地位，从而在正则表达式图书领域形成独特的“一夫当关”的局面，称其为正则表达式圣经，绝对当之无愧。</p>
<p>为什么这本书能够表现得如此出色？我认为这其中有三个原因。其一，作者本人具有多年程序开发经验，理论基础深厚，实战经验丰富，对正则表达式这个主题透彻理解，因此在技术上得心应手，底气十足，对于技术上的难点不回避、不含糊。作者高超的技术水平是本书质量的强大保证。其二，作者思路对头，素材组织得当，用例丰富。正则表达式根植于数学理论，却又能在日常俗事上发挥巨大的效用。写这种类型的技术，思路稍微一偏差，就可能走歪路，不是太理论，就是太琐碎，不是太枯燥，就是太浅薄，实在很难把握。作者清楚地认识到，这本书的读者不是计算机科学家，但也不是满足于“知其然而不知其所以然”的快餐式代码小子，而是具有一定理论素养，却又始终以实践为本的专业开发者。他们需要的是面向实践的理论和思想，是实实在在的实战能力，只有满足这种需要，才能够真正打动读者。 通读此书，可以说作者对这一路线的把握十分成功，保证了内容大方向的正确。其三，这本书的写法独具匠心，堪称典范。技术图书的主要使命是传播专业知识。而专业知识分为框架性知识和具体知识。框架性知识需要通过系统的阅读和学习掌握，而大量的具体知识，则主要通过日常工作的积累以及随用随查的的学习来逐渐填充起来。本书前六章，以顺序式记述的方式，将正则表达式的系统知识娓娓道来，读者像看故事书似的就建立起整个正则表达式的基本知识体系。而后面的内容，则是方便实际开发中频繁查阅之用，包括各大主流语言对正则表达式的支持细节，包含有大量案例。这样的写法，完全符合一般人学习的特点，因此书读起来非常惬意，非常有趣，而用的时候查起来又非常方便。这样的著述风格，实在值得学习。</p>
<p>读者可以在没有任何正则表达式的基础上开始阅读此书，只要勤动脑，加强理解，适当动手练习，将能够在不长的时间里掌握正则表达式的思想和技术精华，这一点已经被很多人验证过，我本人也是这本书的受益者之一。正因为这本书独一无二的地位和高度的可读性，也因为正则表达式作为一项了不起的技术发明所具有的巨大威力，我非常希望更多的读者能够通过认真地学习本书而掌握这一强大技术，并享受阅读的乐趣。</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/64/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/64/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/64/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/64/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/64/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=64&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/07/31/book-review-about-mastery-regular-expressions/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>
	</item>
		<item>
		<title>正则表达式的敏感性和特殊性</title>
		<link>http://lisf.wordpress.com/2007/07/28/sensitivity-and-specificity/</link>
		<comments>http://lisf.wordpress.com/2007/07/28/sensitivity-and-specificity/#comments</comments>
		<pubDate>Sat, 28 Jul 2007 14:07:42 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[正则表达式]]></category>
		<category><![CDATA[转载]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/07/28/sensitivity-and-specificity/</guid>
		<description><![CDATA[敏感性和特殊性这两个概念来源于像统计学和流行病学这样的学科中的量化标准。宽泛地说，敏感性可以用实际找到的匹配项中的正确匹配项数除以在匹配全部相关字符序列的情况下应该找到的匹配项数来度量。而特殊性则可以用实际找到的匹配项中的正确匹配项数除以找到的匹配项总数来表示。在使用正则表达式时，敏感性越高，则表明找到的真正匹配项数量越接近要找的全部匹配项；而特殊性越高，则表明找到的匹配项中正确的匹配项越多。
匹分敏感性和特殊性的关键是要明确三个数量：“正确的”、“找到的”和“要找的”。根据上面的定义，敏感性是指用“正确的”除以“要找的”，这个比例越高说明正则表达式越敏感－－比如要找 100 个，实际找到了 150 个，而其中有 100 个是正确的，那么敏感性就是 100%；而特殊性则是指用“正确的”除以“找到的”，这个比例越高说明正则表达式越特殊&#60;即更具有针对性&#62;－－比如上面说找到的 150 个当中有 100 个是正确的，那么特殊性就是 66.67%。所以可以理解为敏感性是从量的角度衡量匹配目标的完成情况，而特殊性则是从质的角度来衡量匹配目标的完成情况。
试验一下：电子邮件地址
1. 打开 PowerGrep，在 Search 文本区域中输入模式 \w*(?&#60;=\w)\.?\w+@.* 。
2. 在 Folder 文本框中输入文件夹名 C:\BRegExp\Ch09 。如果你把下载的测试文件放在其他地方，请修改这里的路径。
3. 在 File mask 文本框中输入文件名 emailornotemail.txt，并单击 Search 按钮。
4. 在 Results 区域中观察结果，如下图所示。

这是一次进步，而模式也更加具有特殊性。因为这次没有匹配第 1、2、7 和 8 行中不想要的字符序列。然而，第 3 行中的字符序列 John@somewhere.invalid，并不是一个有效的电子邮件地址。
可以通过将电子邮件地址的域名部分更加特殊化来排除这个不想要的匹配项。众所周知，所有的域名都是字母字符序列后跟一个句点字符，然后跟着三（com、net、org 或 biz）或四（info）个字母字符。在本例中，我们先不考虑 example.co.uk 这样的域名。那么，下面的模式可以作为与刚刚描述的结构对应的一个适当的模式：
\w+\.\w{3,4}
其中 \w+ 甚至会匹配单个字符的域名（.com、.net 和 .org 域名允许使用单个字符）。而 \. 转义序列匹配一个句点字符，\w{3,4} 则匹配三或四个字母字符。
将这个模式与前面使用的模式组合起来就是：
\w*(?&#60;=\w)\.?\w+@\w+\.\w{3,4}
5. 在 Search [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=57&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p style="margin:6pt 0 6pt 9pt;" class="b0Ctrl0"><span style="font-family:宋体;"><font size="2">敏感性和特殊性这两个概念来源于像统计学和流行病学这样的学科中的量化标准。宽泛地说，敏感性可以用实际找到的匹配项中的正确匹配项数除以在匹配全部相关字符序列的情况下应该找到的匹配项数来度量。而特殊性则可以用实际找到的匹配项中的正确匹配项数除以找到的匹配项总数来表示。在使用正则表达式时，敏感性越高，则表明找到的真正匹配项数量越接近要找的全部匹配项；而特殊性越高，则表明找到的匹配项中正确的匹配项越多。</font></span></p>
<p style="margin:6pt 0 6pt 9pt;" class="b0Ctrl0"><font size="2"><span style="font-family:宋体;">匹分敏感性和特殊性的关键是要明确三个数量：</span><span><font face="Courier New">“</font></span><span style="font-family:宋体;">正确的</span><span><font face="Courier New">”</font></span><span style="font-family:宋体;">、</span><span><font face="Courier New">“</font></span><span style="font-family:宋体;">找到的</span><span><font face="Courier New">”</font></span><span style="font-family:宋体;">和</span><span><font face="Courier New">“</font></span><span style="font-family:宋体;">要找的</span><span><font face="Courier New">”</font></span><span style="font-family:宋体;">。根据上面的定义，敏感性是指用</span><span><font face="Courier New">“</font></span><span style="font-family:宋体;">正确的</span><span><font face="Courier New">”</font></span><span style="font-family:宋体;">除以</span><span><font face="Courier New">“</font></span><span style="font-family:宋体;">要找的</span><span><font face="Courier New">”</font></span><span style="font-family:宋体;">，这个比例越高说明正则表达式越敏感－－比如要找</span><span><font face="Courier New"> 100 </font></span><span style="font-family:宋体;">个，实际找到了</span><span><font face="Courier New"> 150 </font></span><span style="font-family:宋体;">个，而其中有</span><span><font face="Courier New"> 100 </font></span><span style="font-family:宋体;">个是正确的，那么敏感性就是</span><span><font face="Courier New"> 100%</font></span><span style="font-family:宋体;">；而特殊性则是指用</span><span><font face="Courier New">“</font></span><span style="font-family:宋体;">正确的</span><span><font face="Courier New">”</font></span><span style="font-family:宋体;">除以</span><span><font face="Courier New">“</font></span><span style="font-family:宋体;">找到的</span><span><font face="Courier New">”</font></span><span style="font-family:宋体;">，这个比例越高说明正则表达式越特殊</span><span><font face="Courier New">&lt;</font></span><span style="font-family:宋体;">即更具有针对性</span><span><font face="Courier New">&gt;</font></span><span style="font-family:宋体;">－－比如上面说找到的</span><span><font face="Courier New"> 150 </font></span><span style="font-family:宋体;">个当中有</span><span><font face="Courier New"> 100 </font></span><span style="font-family:宋体;">个是正确的，那么特殊性就是</span><span><font face="Courier New"> 66.67%</font></span><span style="font-family:宋体;">。所以可以理解为敏感性是从量的角度衡量匹配目标的完成情况，而特殊性则是从质的角度来衡量匹配目标的完成情况。</span></font></p>
<p style="background:#548dd4;margin:0;" class="b3Ctrl3"><span style="font-family:'微软雅黑','sans-serif';"><font color="#ffffff">试验一下：电子邮件地址</font></span></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">1. </font></span><span style="font-family:楷体_GB2312;">打开</span><span><font face="Courier New"> PowerGrep</font></span><span style="font-family:楷体_GB2312;">，在</span><span><font face="Courier New"> Search </font></span><span style="font-family:楷体_GB2312;">文本区域中输入模式</span><span><font face="Courier New"> \w*(?&lt;=\w)\.?\w+@.* </font></span><span style="font-family:楷体_GB2312;">。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">2. </font></span><span style="font-family:楷体_GB2312;">在</span><span><font face="Courier New"> Folder </font></span><span style="font-family:楷体_GB2312;">文本框中输入文件夹名</span><span><font face="Courier New"> C:\BRegExp\Ch09 </font></span><span style="font-family:楷体_GB2312;">。如果你把下载的测试文件放在其他地方，请修改这里的路径。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">3. </font></span><span style="font-family:楷体_GB2312;">在</span><span><font face="Courier New"> File mask </font></span><span style="font-family:楷体_GB2312;">文本框中输入文件名</span><span><font face="Courier New"> <a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/emailornotemail.txt" title="emailornotemail.txt">emailornotemail.txt</a></font></span><span style="font-family:楷体_GB2312;">，并单击</span><span><font face="Courier New"> Search </font></span><span style="font-family:楷体_GB2312;">按钮。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">4. </font></span><span style="font-family:楷体_GB2312;">在</span><span><font face="Courier New"> Results </font></span><span style="font-family:楷体_GB2312;">区域中观察结果，如下图所示。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span style="font-family:楷体_GB2312;"><a rel="attachment wp-att-59" href="http://www.cn-cuckoo.com/2007/07/28/sensitivity-and-specificity-58.html/email_1gif/" title="email_1.gif"><img src="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/email_1.gif" alt="email_1.gif" /></a></span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span style="font-family:楷体_GB2312;">这是一次进步，而模式也更加具有特殊性。因为这次没有匹配第</span><span><font face="Courier New"> 1</font></span><span style="font-family:楷体_GB2312;">、</span><span><font face="Courier New">2</font></span><span style="font-family:楷体_GB2312;">、</span><span><font face="Courier New">7 </font></span><span style="font-family:楷体_GB2312;">和</span><span><font face="Courier New"> 8 </font></span><span style="font-family:楷体_GB2312;">行中不想要的字符序列。然而，第</span><span><font face="Courier New"> 3 </font></span><span style="font-family:楷体_GB2312;">行中的字符序列</span><span><font face="Courier New"> John@somewhere.invalid</font></span><span style="font-family:楷体_GB2312;">，并不是一个有效的电子邮件地址。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span style="font-family:楷体_GB2312;">可以通过将电子邮件地址的域名部分更加特殊化来排除这个不想要的匹配项。众所周知，所有的域名都是字母字符序列后跟一个句点字符，然后跟着三（</span><span><font face="Courier New">com</font></span><span style="font-family:楷体_GB2312;">、</span><span><font face="Courier New">net</font></span><span style="font-family:楷体_GB2312;">、</span><span><font face="Courier New">org </font></span><span style="font-family:楷体_GB2312;">或</span><span><font face="Courier New"> biz</font></span><span style="font-family:楷体_GB2312;">）或四（</span><span><font face="Courier New">info</font></span><span style="font-family:楷体_GB2312;">）个字母字符。在本例中，我们先不考虑</span><span><font face="Courier New"> example.co.uk </font></span><span style="font-family:楷体_GB2312;">这样的域名。那么，下面的模式可以作为与刚刚描述的结构对应的一个适当的模式：</span></font></p>
<p style="background:#c6d9f1;margin:0;" class="b9Ctrl9"><span><font size="2" face="Courier New">\w+\.\w{3,4}</font></span></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span style="font-family:楷体_GB2312;">其中</span><span><font face="Courier New"> \w+ </font></span><span style="font-family:楷体_GB2312;">甚至会匹配单个字符的域名（</span><span><font face="Courier New">.com</font></span><span style="font-family:楷体_GB2312;">、</span><span><font face="Courier New">.net </font></span><span style="font-family:楷体_GB2312;">和</span><span><font face="Courier New"> .org </font></span><span style="font-family:楷体_GB2312;">域名允许使用单个字符）。而</span><span><font face="Courier New"> \. </font></span><span style="font-family:楷体_GB2312;">转义序列匹配一个句点字符，</span><span><font face="Courier New">\w{3,4} </font></span><span style="font-family:楷体_GB2312;">则匹配三或四个字母字符。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><span style="font-family:楷体_GB2312;"><font size="2">将这个模式与前面使用的模式组合起来就是：</font></span></p>
<p style="background:#c6d9f1;margin:0;" class="b9Ctrl9"><span><font size="2" face="Courier New">\w*(?&lt;=\w)\.?\w+@\w+\.\w{3,4}</font></span></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">5. </font></span><span style="font-family:楷体_GB2312;">在</span><span><font face="Courier New"> Search </font></span><span style="font-family:楷体_GB2312;">文本区域中输入模式</span><font face="Courier New"> <strong><span>\w*(?&lt;=\w)\.?\w+@\w+\.\w{3,4}</span></strong></font><span style="font-family:楷体_GB2312;">，并单击</span><span><font face="Courier New"> Search </font></span><span style="font-family:楷体_GB2312;">按钮。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">6. </font></span><span style="font-family:楷体_GB2312;">观察结果。注意第</span><span><font face="Courier New"> 3 </font></span><span style="font-family:楷体_GB2312;">行中不想要的匹配项这次没有匹配。然而，在第</span><span><font face="Courier New"> 6 </font></span><span style="font-family:楷体_GB2312;">行中一个前面没有提及的问题却浮出水面。第</span><span><font face="Courier New"> 6 </font></span><span style="font-family:楷体_GB2312;">行中的电子邮件地址中包含两个</span><span><font face="Courier New"> @ </font></span><span style="font-family:楷体_GB2312;">字符，而这是不允许的。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span style="font-family:楷体_GB2312;">要解决这个问题一种方法是使用向前查找限定在匹配第一个</span><span><font face="Courier New"> @ </font></span><span style="font-family:楷体_GB2312;">字符后，不允许后面出现另一个</span><span><font face="Courier New"> @ </font></span><span style="font-family:楷体_GB2312;">字符。如果我们继续假设在电子邮件地址中只允许使用字母字符，那么就可以通过向前查找来限定在第一次匹配非字母字符或句点字符之前只有一个</span><font face="Courier New"> <span>@ </span></font><span style="font-family:楷体_GB2312;">字符。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><span style="font-family:楷体_GB2312;"><font size="2">可以通过下面的模式实现这种限定：</font></span></p>
<p style="background:#c6d9f1;margin:0;" class="b9Ctrl9"><span><font size="2" face="Courier New">\w*(?&lt;=\w)\.?\w+@(?=[\w\.]+\W)\w+\.\w{3,4}</font></span></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">7. </font></span><span style="font-family:楷体_GB2312;">在</span><span><font face="Courier New"> Search </font></span><span style="font-family:楷体_GB2312;">文本区域中把模式修改为</span><span><font face="Courier New"> \w*(?&lt;=\w)\.?\w+@(?=[\w\.]+\W)\w+\.\w{3,4}</font></span><span style="font-family:楷体_GB2312;">，并单击</span><span><font face="Courier New"> Search </font></span><span style="font-family:楷体_GB2312;">按钮。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">8. </font></span><span style="font-family:楷体_GB2312;">观察结果。结果如下图</span><span style="font-family:楷体_GB2312;">所示。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span style="font-family:楷体_GB2312;"><a rel="attachment wp-att-60" href="http://www.cn-cuckoo.com/2007/07/28/sensitivity-and-specificity-58.html/email_2gif/" title="email_2.gif"><img src="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/email_2.gif" alt="email_2.gif" /></a></span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span style="font-family:楷体_GB2312;">遗憾的是，向前查找并没有解决第</span><span><font face="Courier New"> 3 </font></span><span style="font-family:楷体_GB2312;">行和第</span><span><font face="Courier New"> 6 </font></span><span style="font-family:楷体_GB2312;">行中不想要匹配的问题。我们还需要限定模式要匹配的是一行中的全部文本。换句话说，要添加</span><span><font face="Courier New"> ^ </font></span><span style="font-family:楷体_GB2312;">元字符指定一行的开始位置和</span><span><font face="Courier New"> $ </font></span><span style="font-family:楷体_GB2312;">元字符指定该行的结束位置。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">9. </font></span><span style="font-family:楷体_GB2312;">在</span><span><font face="Courier New"> Search </font></span><span style="font-family:楷体_GB2312;">文本区域中把模式修改为</span><span><font face="Courier New"> ^\w*(?&lt;=\w)\.?\w+@(?=[\w\.]+\W)\w+\.\w{3,4}$</font></span><span style="font-family:楷体_GB2312;">，并单击</span><span><font face="Courier New"> Search </font></span><span style="font-family:楷体_GB2312;">按钮。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span><font face="Courier New">10. </font></span><span style="font-family:楷体_GB2312;">观察结果。结果如下图</span><span style="font-family:楷体_GB2312;">所示。</span></font></p>
<p style="margin:0 0 6pt 18pt;" class="b4Ctrl4"><font size="2"><span style="font-family:楷体_GB2312;"><a rel="attachment wp-att-61" href="http://www.cn-cuckoo.com/2007/07/28/sensitivity-and-specificity-58.html/email_3gif/" title="email_3.gif"><img src="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/email_3.gif" alt="email_3.gif" /></a></span></font></p>
<p style="margin:6pt 0 6pt 9pt;" class="b0Ctrl0"><font size="2"><span style="font-family:宋体;">令人高兴的是，这次成功地排除了第</span><span><font face="Courier New"> 3 </font></span><span style="font-family:宋体;">行和第</span><span><font face="Courier New"> 6 </font></span><span style="font-family:宋体;">行中不想要的匹配项。至少在这个简单的测试数据文件中，我们获得了</span><span><font face="Courier New"> 100% </font></span><span style="font-family:宋体;">的敏感性和</span><span><font face="Courier New"> 100% </font></span><span style="font-family:宋体;">特殊性。</span></font></p>
<p style="margin:6pt 0 6pt 18pt;" class="b7Ctrl7"><em><font size="2"><span style="font-family:幼圆;">敏感性和特殊性这两个概念来源于量化研究的科学，比如统计学和流行病学。在这两种学科中，都是用数字来表示敏感性和特殊性的，而且通常使用百分比。因此，对于前面的例子来说，因为使用第一个正则表达式模式时找到了所有正确的电子邮件地址，所以敏感性达到了</span><span><font face="Arial"> 100%</font></span><span style="font-family:幼圆;">；而因为</span><span><font face="Arial"> 10 </font></span><span style="font-family:幼圆;">个匹配结果中的</span><span><font face="Arial"> 6 </font></span><span style="font-family:幼圆;">个（从不是有效的电子邮件地址的角度来说）都是错误的匹配，所以特殊性仅有</span><span><font face="Arial"> 40%</font></span><span style="font-family:幼圆;">。而到了该例子的最后，修改后正则表达式的特殊性也上升到了</span><span><font face="Arial"> 100%</font></span><span style="font-family:幼圆;">。</span></font></em></p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/57/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/57/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/57/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/57/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/57/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=57&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/07/28/sensitivity-and-specificity/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>

		<media:content url="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/email_1.gif" medium="image">
			<media:title type="html">email_1.gif</media:title>
		</media:content>

		<media:content url="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/email_2.gif" medium="image">
			<media:title type="html">email_2.gif</media:title>
		</media:content>

		<media:content url="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/email_3.gif" medium="image">
			<media:title type="html">email_3.gif</media:title>
		</media:content>
	</item>
		<item>
		<title>正则表达式中的位置元字符</title>
		<link>http://lisf.wordpress.com/2007/07/27/the-position-metacharacter-of-regular-expressions/</link>
		<comments>http://lisf.wordpress.com/2007/07/27/the-position-metacharacter-of-regular-expressions/#comments</comments>
		<pubDate>Fri, 27 Jul 2007 14:08:40 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[正则表达式]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/07/27/the-position-metacharacter-of-regular-expressions/</guid>
		<description><![CDATA[^－－匹配字符串（或多行模式下行）的开始位置；
$－－匹配字符串（或多行模式行下）的结束位置；
\b－－匹配位于 \w 字符和 \W 字符之间的位置（又称词边界）。
\B－－匹配位于字符之间的位置（即非 \w 字符和 \W 字符之间的位置）。
\A－－匹配一个字符串的开始位置。其行为不受多行模式的影响（PHP）。
\z－－匹配一个字符串的结束位置。其行为不受多行模式的影响（PHP）。
对于测试字符串 look at this. 而言，在多行模式下，正则表达式 this$ 不会匹配其结尾处的 this，因为 $ 匹配行结尾的位置，而 this 中最后一个字符 s 之后并不是一个换行符，而是一个句点字符。若要匹配这个 this，必须使用正则表达式 this\b ，\b 匹配字母字符和非字母字符之间的位置，而 this 的最后一个字符 s 和其后面的句点字符之间的位置恰好是这样一个位置。
对于 \B 元字符，其用途如下：
如果你想匹配 golden 中的 old 就可以使用模式 \Bold\B ，这个模式匹配一个字符之间的位置后跟字符序列 o、l、d，后跟另一个字符之间的位置。也就是说，\B 不会匹配位于字符和非字符之间的位置，所以模式 \Bold\B 匹配的字符序列 old 一定是被包含在一个单词（或字符串）中间。 
       <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=49&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>^－－匹配字符串（或多行模式下行）的开始位置；</p>
<p>$－－匹配字符串（或多行模式行下）的结束位置；</p>
<p>\b－－匹配位于 \w 字符和 \W 字符之间的位置（又称词边界）。</p>
<p>\B－－匹配位于字符之间的位置（即非 \w 字符和 \W 字符之间的位置）。</p>
<p>\A－－匹配一个字符串的开始位置。其行为不受多行模式的影响（PHP）。</p>
<p>\z－－匹配一个字符串的结束位置。其行为不受多行模式的影响（PHP）。</p>
<p>对于测试字符串 look at this. 而言，在多行模式下，正则表达式 this$ 不会匹配其结尾处的 this，因为 $ 匹配行结尾的位置，而 this 中最后一个字符 s 之后并不是一个换行符，而是一个句点字符。若要匹配这个 this，必须使用正则表达式 this\b ，\b 匹配字母字符和非字母字符之间的位置，而 this 的最后一个字符 s 和其后面的句点字符之间的位置恰好是这样一个位置。</p>
<p>对于 \B 元字符，其用途如下：</p>
<p>如果你想匹配 golden 中的 old 就可以使用模式 \Bold\B ，这个模式匹配一个字符之间的位置后跟字符序列 o、l、d，后跟另一个字符之间的位置。也就是说，\B 不会匹配位于字符和非字符之间的位置，所以模式 \Bold\B 匹配的字符序列 old 一定是被包含在一个单词（或字符串）中间。 </p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/49/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/49/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/49/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/49/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/49/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=49&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/07/27/the-position-metacharacter-of-regular-expressions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>
	</item>
		<item>
		<title>Perl 中的内置变量</title>
		<link>http://lisf.wordpress.com/2007/07/27/the-inline-variables-of-perl/</link>
		<comments>http://lisf.wordpress.com/2007/07/27/the-inline-variables-of-perl/#comments</comments>
		<pubDate>Fri, 27 Jul 2007 03:15:24 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[Perl]]></category>
		<category><![CDATA[新知]]></category>
		<category><![CDATA[转载]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/07/27/the-inline-variables-of-perl/</guid>
		<description><![CDATA[这篇文章是从网络中转载的，据说是来自“来源：黑客基地 作者：黑客基地”。如果您是原作者，请联系本站（lsf@cn-cuckoo.com）以便更正，谢谢！
$- 当前页可打印的行数,属于Perl格式系统的一部分
$! 根据上下文内容返回错误号或者错误串
$&#8221; 列表分隔符
$# 打印数字时默认的数字输出格式
$ Perl解释器的进程ID
$% 当前输出通道的当前页号
$Content$amp; 与上个格式匹配的字符串
$( 当前进程的组ID
$) 当前进程的有效组ID
$* 设置1表示处理多行格式.现在多以/s和/m修饰符取代之
$, 当前输出字段分隔符
$. 上次阅读的文件的当前输入行号
$/ 当前输入记录分隔符,默认情况是新行
$: 字符设置,此后的字符串将被分开,以填充连续的字段
$; 在仿真多维数组时使用的分隔符
$? 返回上一个外部命令的状态
$@ Perl解释器从eval语句返回的错误消息
$[ 数组中第一个元素的索引号
$\ 当前输出记录的分隔符
$] Perl解释器的子版本号
$^ 当前通道最上面的页面输出格式名字
$^A 打印前用于保存格式化数据的变量
$^D调试标志的值
$^E在非UNIX环境中的操作系统扩展错误信息
$^F最大的文件捆述符数值
$^H由编译器激活的语法检查状态
$^I内置控制编辑器的值
$^L发送到输出通道的走纸换页符
$^M备用内存池的大小
$^O操作系统名
$^P指定当前调试值的内部变量
$^R正则表达式块的上次求值结果
$^S当前解释器状态
$^T从新世纪开始算起,脚步本以秒计算的开始运行的时间
$^W警告开关的当前值
$^X Perl二进制可执行代码的名字
$_ 默认的输入/输出和格式匹配空间
$　 控制对当前选择的输出文件句柄的缓冲
$~ 当前报告格式的名字
       <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=48&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>这篇文章是从网络中转载的，据说是来自“来源：黑客基地 作者：黑客基地”。如果您是原作者，请联系本站（lsf@cn-cuckoo.com）以便更正，谢谢！</p>
<p class="code">$- 当前页可打印的行数,属于Perl格式系统的一部分<br />
$! 根据上下文内容返回错误号或者错误串<br />
$&#8221; 列表分隔符<br />
$# 打印数字时默认的数字输出格式<br />
$ Perl解释器的进程ID<br />
$% 当前输出通道的当前页号<br />
$Content$amp; 与上个格式匹配的字符串<br />
$( 当前进程的组ID<br />
$) 当前进程的有效组ID<br />
$* 设置1表示处理多行格式.现在多以/s和/m修饰符取代之<br />
$, 当前输出字段分隔符<br />
$. 上次阅读的文件的当前输入行号<br />
$/ 当前输入记录分隔符,默认情况是新行<br />
$: 字符设置,此后的字符串将被分开,以填充连续的字段<br />
$; 在仿真多维数组时使用的分隔符<br />
$? 返回上一个外部命令的状态<br />
$@ Perl解释器从eval语句返回的错误消息<br />
$[ 数组中第一个元素的索引号<br />
$\ 当前输出记录的分隔符<br />
$] Perl解释器的子版本号<br />
$^ 当前通道最上面的页面输出格式名字<br />
$^A 打印前用于保存格式化数据的变量<br />
$^D调试标志的值<br />
$^E在非UNIX环境中的操作系统扩展错误信息<br />
$^F最大的文件捆述符数值<br />
$^H由编译器激活的语法检查状态<br />
$^I内置控制编辑器的值<br />
$^L发送到输出通道的走纸换页符<br />
$^M备用内存池的大小<br />
$^O操作系统名<br />
$^P指定当前调试值的内部变量<br />
$^R正则表达式块的上次求值结果<br />
$^S当前解释器状态<br />
$^T从新世纪开始算起,脚步本以秒计算的开始运行的时间<br />
$^W警告开关的当前值<br />
$^X Perl二进制可执行代码的名字<br />
$_ 默认的输入/输出和格式匹配空间<br />
$　 控制对当前选择的输出文件句柄的缓冲<br />
$~ 当前报告格式的名字</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/48/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/48/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/48/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/48/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/48/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=48&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/07/27/the-inline-variables-of-perl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>
	</item>
		<item>
		<title>术语 variable substitution 的译法</title>
		<link>http://lisf.wordpress.com/2007/07/27/how-to-translate-term-variable-substitution/</link>
		<comments>http://lisf.wordpress.com/2007/07/27/how-to-translate-term-variable-substitution/#comments</comments>
		<pubDate>Fri, 27 Jul 2007 01:15:02 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[正则表达式]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/07/27/how-to-translate-term-variable-substitution/</guid>
		<description><![CDATA[Perl 正则表达式中可以使用变量，就和在双引号中可以插入变量一样。比如：
在字符串中插入变量
print &#8220;$myPattern is found in $myTestString&#8221;;
在模式中插入变量
/${myPattern}ll/;（一对正斜杠是模式的默认定界符）
《BRE》中把在模式中插入的变量称为 variable substitution，开始我采用的译法是“变量置入”，后来想改为“置入变量”。比如：“使用置入变量匹配”。但在网上没有搜索到“变量置入”或“置入变量”这样的译法，倒是有人使用“变量替换”－－也就是 variable substitution 的直译。虽然使用“变量替换”可以同网上已有的译法一致，但我还是觉得有点不妥。理由如下：
我们常说“使用……”，“使用”后面的省略号应该是名词，以构成动名词结构。而“变量替换”是动名词倒置，“替换”虽具有名词性质，但与“变量”组合起来意思显示不够明确－－因为容易让人理解为“变量替换过程”。而事实上，使用的是变量，而不是使用替换过程。
所以，我觉得应该在“置入变量”、“替换变量”甚至“插入变量”中选择一个。这样当在书中其他内容中提及“使用置入变量”、“使用替换变量”甚至“使用插入变量”时，意思更明确，不致混淆。
       <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=46&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Perl 正则表达式中可以使用变量，就和在双引号中可以插入变量一样。比如：</p>
<p>在字符串中插入变量</p>
<p class="code">print &#8220;$myPattern is found in $myTestString&#8221;;</p>
<p>在模式中插入变量</p>
<p class="code">/${myPattern}ll/;（一对正斜杠是模式的默认定界符）</p>
<p>《BRE》中把在模式中插入的变量称为 variable substitution，开始我采用的译法是“变量置入”，后来想改为“置入变量”。比如：“使用置入变量匹配”。但在网上没有搜索到“变量置入”或“置入变量”这样的译法，倒是有人使用“变量替换”－－也就是 variable substitution 的直译。虽然使用“变量替换”可以同网上已有的译法一致，但我还是觉得有点不妥。理由如下：</p>
<p>我们常说“使用……”，“使用”后面的省略号应该是名词，以构成动名词结构。而“变量替换”是动名词倒置，“替换”虽具有名词性质，但与“变量”组合起来意思显示不够明确－－因为容易让人理解为“变量替换过程”。而事实上，使用的是变量，而不是使用替换过程。</p>
<p>所以，我觉得应该在“置入变量”、“替换变量”甚至“插入变量”中选择一个。这样当在书中其他内容中提及“使用置入变量”、“使用替换变量”甚至“使用插入变量”时，意思更明确，不致混淆。</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/46/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/46/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/46/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/46/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/46/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=46&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/07/27/how-to-translate-term-variable-substitution/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>
	</item>
		<item>
		<title>Math、Group 和 Capture</title>
		<link>http://lisf.wordpress.com/2007/07/26/math-group-and-capture/</link>
		<comments>http://lisf.wordpress.com/2007/07/26/math-group-and-capture/#comments</comments>
		<pubDate>Thu, 26 Jul 2007 04:38:06 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[正则表达式]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/07/26/math-group-and-capture/</guid>
		<description><![CDATA[.NET 平台中的正则表达式包含在 System.Text.RegularExpressions 命名空间中。该命名空间中的类主要有：

Regex
MatchCollection
Match
GroupCollection
Group
CaptureCollection
Capture

其中，Regex 类是核心。
Regex.Match() 方法返回一个包含（或不包含）匹配项信息的 Match 对象；
Regex.Matches() 方法则返回包含所有匹配项的 MatchCollection （Match 对象的集合）对象；
Match.Groups 属性中包含着 GroupCollection （Group 对象的集合）；
Group.Captures 属性中包含着 CaptureCollection（Capture 对象的集合）。
而 MatchCollection、GroupCollection 和 CaptureCollection 中包含的 Match、Group 和 Capture 对象可以通过 For Each 语句来枚举。
下面的示例代码涉及到了以上所有的类、方法和属性的使用：

Imports System.Text.RegularExpressions
Module Module1
  Sub Main()
    Dim myRegex = New Regex(“([A-Z])+(d)+”)
    Console.WriteLine(“Enter a string on the following line:”)
    Dim inputString = Console.ReadLine()
    Dim myMatchCollection = myRegex.Matches(inputString)
    Console.WriteLine()
    Console.WriteLine(“There are {0} matches.”, myMatchCollection.Count)
    Console.WriteLine()
    Dim myMatch As [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=43&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><a rel="attachment wp-att-47" href="http://www.cn-cuckoo.com/?attachment_id=47" title="Match-Group-Capture"></a>.NET 平台中的正则表达式包含在 System.Text.RegularExpressions 命名空间中。该命名空间中的类主要有：</p>
<ul>
<li>Regex</li>
<li>MatchCollection</li>
<li>Match</li>
<li>GroupCollection</li>
<li>Group</li>
<li>CaptureCollection</li>
<li>Capture</li>
</ul>
<p>其中，Regex 类是核心。</p>
<p>Regex.Match() 方法返回一个包含（或不包含）匹配项信息的 Match 对象；</p>
<p>Regex.Matches() 方法则返回包含所有匹配项的 MatchCollection （Match 对象的集合）对象；</p>
<p>Match.Groups 属性中包含着 GroupCollection （Group 对象的集合）；</p>
<p>Group.Captures 属性中包含着 CaptureCollection（Capture 对象的集合）。</p>
<p>而 MatchCollection、GroupCollection 和 CaptureCollection 中包含的 Match、Group 和 Capture 对象可以通过 For Each 语句来枚举。</p>
<p>下面的示例代码涉及到了以上所有的类、方法和属性的使用：</p>
<pre>
<p class="code">Imports System.Text.RegularExpressions
Module Module1
  Sub Main()
    <strong>Dim myRegex = New Regex(“([A-Z])+(d)+”)</strong>
    Console.WriteLine(“Enter a string on the following line:”)
    Dim inputString = Console.ReadLine()
    <strong>Dim myMatchCollection = myRegex.Matches(inputString)</strong>
    Console.WriteLine()
    Console.WriteLine(“There are {0} matches.”, myMatchCollection.Count)
    Console.WriteLine()
    Dim myMatch As Match
    Dim myGroupCollection As GroupCollection
    Dim myGroup As Group
    For Each myMatch In myMatchCollection
      Console.WriteLine(“At position {0}, the match ‘{1}’ was found”,
        myMatch.Index, myMatch.ToString)
      Console.WriteLine(“This match has {0} groups.”,
        myMatch.Groups.Count)
      <strong>myGroupCollection = myMatch.Groups</strong>
      For Each myGroup In myGroupCollection
        <strong>Dim myCaptureCollection As CaptureCollection = myGroup.Captures</strong>
        Dim myCapture As Capture
        Console.WriteLine(“Group containing ‘{0}’ found at position
          ‘{1}’.”, myGroup.Value, myGroup.Index)
        For Each myCapture In myCaptureCollection
          Console.WriteLine(“ Capture: ‘{0}’ at position ‘{1}’.”,
            myCapture.Value, myCapture.Index)
        Next
      Next
      Console.WriteLine()
    Next
  Console.WriteLine()
  Console.WriteLine(“Press Return to close this application.”)
  Console.ReadLine()
End Sub
End Module</pre>
<p>在命令行中执行以上代码，并根据提示输入测试字符串：ABC1 A123（注意中间的空格），按回车后观察如下图所示的执行结果：<br />
<a rel="attachment wp-att-47" href="http://www.cn-cuckoo.com/?attachment_id=47" title="Match-Group-Capture"><img src="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/bre_vbnet.gif" alt="Match-Group-Capture" /></a></p>
<p>更直观的示意图如下所示：</p>
<p><a rel="attachment wp-att-48" href="http://www.cn-cuckoo.com/2007/07/26/math-group-and-capture-42.html/bre_vbnet2gif/" title="bre_vbnet2.gif"><img src="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/bre_vbnet2.gif" alt="bre_vbnet2.gif" /></a></p>
<p>能够反映动态匹配、组、和捕获过程的示意图如下：</p>
<p><a rel="attachment wp-att-50" href="http://www.cn-cuckoo.com/2007/07/26/math-group-and-capture-42.html/match-group-capturenet3gif/" title="match-group-capturenet3.gif"><img src="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/match-group-capturenet3.gif" alt="match-group-capturenet3.gif" /></a></p>
<p>原理剖析：</p>
<p>本例中使用的正则表达式模式是：([A-Z])+(\d)+。这是一个精心设计的模式，为什么不把 + 限定放在分组的圆括号中呢？目的就是为了验证对多个匹配的字符创建多个分组和多个捕获的效果。如果把模式修改为 ([A-Z]+)(\d+) 那结果就完全不一样了－－对我们理解组和捕获之间的关系就没有帮助了（若不理解，请继续看下面的分析）。</p>
<p>.NET 中对正则表达式匹配项进行了细分，即分为匹配（由 Match 对象表示）、组（由 Group 对象表示）和捕获（由 Capture 对象来表示）三个层次。这三个层次是“父子”关系，或者说具有包含关系，即一个匹配中包含所有组，一个组中包含所有捕获。当然，捕获是最小单位，可以最小化到捕获一个字符－－比如本例模式中的 ([A-Z]) 这个组每次就能捕获到一个大写字母（如果有）。这种关系通过上面第二幅插图能够看得非常清楚。</p>
<p>在匹配项是 ABC1 时，匹配（Match）中包含着 ABC1 这个字符序列，而与该匹配对应的第0组（Group[0]）中包含同样的字符序列，与第0组对应的第0个捕获（Capture[0]）中也是如此。而与第一个分组对应的第1组（Group[1]）会根据圆括号中的模式 [A-Z] 匹配三次－－分别是 A、B 和 C。与此同时，将捕获到的字符分别保存在 Capture[0]、Capture[1] 和 Capturep[2] 中。注意，虽然捕获到了三个字符，但在第1组中每次都会用新捕获的字符替换上一次捕获的字符（即将上一次保存在 Group[1] 中的值丢弃。如上面第三幅插图所示），最终当匹配到数字 1 之前的位置时，Group[1] 中保存的是大写 C。这就是根据多个匹配项，创建多个分组和多个捕获的过程。</p>
<p>由此可见，在依据同一分组中的模式进行多次匹配时，只有通过 Capture 才能保存每次匹配的结果。这正是 .NET 中对文本操作精细化的一个解决方案。虽然通过多创建分组也能够使组中保存更少的字符（甚至单个字符），但毕竟不如增加一个新的 Capture 概念来得更直接（容易理解）、也更方便（例如可以通过编程遍历）。</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/43/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/43/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/43/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/43/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/43/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/43/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/43/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/43/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=43&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/07/26/math-group-and-capture/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>

		<media:content url="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/bre_vbnet.gif" medium="image">
			<media:title type="html">Match-Group-Capture</media:title>
		</media:content>

		<media:content url="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/bre_vbnet2.gif" medium="image">
			<media:title type="html">bre_vbnet2.gif</media:title>
		</media:content>

		<media:content url="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/07/match-group-capturenet3.gif" medium="image">
			<media:title type="html">match-group-capturenet3.gif</media:title>
		</media:content>
	</item>
		<item>
		<title>Mastering Regular Expressions，3rd</title>
		<link>http://lisf.wordpress.com/2007/07/26/mastering-regular-expressions/</link>
		<comments>http://lisf.wordpress.com/2007/07/26/mastering-regular-expressions/#comments</comments>
		<pubDate>Thu, 26 Jul 2007 03:06:36 +0000</pubDate>
		<dc:creator>lisf</dc:creator>
				<category><![CDATA[原创]]></category>
		<category><![CDATA[好书]]></category>
		<category><![CDATA[正则表达式]]></category>
		<category><![CDATA[转载]]></category>

		<guid isPermaLink="false">http://lisf.wordpress.com/2007/07/26/mastering-regular-expressions/</guid>
		<description><![CDATA[《Mastering Regular Expressions,3rd》（以下简称《MRE3》）这本书的中译版也要上市了，中文名叫《精通正则表达式》，由电子工业出版社的博文视点推出。而《Beginning Regular Expressions》（以下简称《BRE》）可能还要等四个月以上才能面世。
本文的目的是就这两本“国内首册”正则表达式技术书的引进版作一比较。
首先，来看一下《MRE3》的英文目录（PDF下载）：
Preface
1 Introduction to Regular Expressions
    Solving Real Problems
    Regular Expressions as a Language
        The Filename Analogy
        The Language Analogy
    The Regular-Expression Frame of Mind
        If You Have Some Regular-Expression Experience
        Searching Text Files: Egrep
    Egrep Metacharacters
        Start and End of the Line
        Character Classes
        Matching Any Character with Dot
        Alternation
        Ignoring Differences in Capitalization
        Word Boundaries
        In a Nutshell
        Optional Items
        Other Quantifiers: Repetition
        Parentheses and Backreferences
        The Great Escape
    Expanding [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=42&subd=lisf&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><a target="_blank" href="http://www.oreilly.com/catalog/regex3/index.html" title="Master Regular Expressions,3rd">《Mastering Regular Expressions,3rd》</a>（以下简称《MRE3》）这本书的中译版也要上市了，中文名叫<a target="_blank" href="http://www.china-pub.com/computers/common/info.asp?id=35269" title="精通则表达式">《精通正则表达式》</a>，由电子工业出版社的博文视点推出。而<a target="_blank" href="http://www.cn-cuckoo.com/bre" title="则表达式入门经典">《Beginning Regular Expressions》</a>（以下简称《BRE》）可能还要等四个月以上才能面世。</p>
<p>本文的目的是就这两本“国内首册”正则表达式技术书的引进版作一比较。</p>
<p>首先，来看一下《MRE3》的英文目录（<a href="http://www.oreilly.com/catalog/regex3/toc.pdf" title="Mastering Regular Expressions, Third Edition TOC">PDF下载</a>）：</p>
<p>Preface<br />
1 Introduction to Regular Expressions<br />
    Solving Real Problems<br />
    Regular Expressions as a Language<br />
        The Filename Analogy<br />
        The Language Analogy<br />
    The Regular-Expression Frame of Mind<br />
        If You Have Some Regular-Expression Experience<br />
        Searching Text Files: Egrep<br />
    Egrep Metacharacters<br />
        Start and End of the Line<br />
        Character Classes<br />
        Matching Any Character with Dot<br />
        Alternation<br />
        Ignoring Differences in Capitalization<br />
        Word Boundaries<br />
        In a Nutshell<br />
        Optional Items<br />
        Other Quantifiers: Repetition<br />
        Parentheses and Backreferences<br />
        The Great Escape<br />
    Expanding the Foundation<br />
        Linguistic Diversification<br />
        The Goal of a Regular Expression<br />
        A Few More Examples<br />
        Regular Expression Nomenclature<br />
        Improving on the Status Quo<br />
        Summary<br />
    Personal Glimpses<br />
2 Extended Introductory Examples<br />
    About the Examples<br />
        A Short Introduction to Perl<br />
    Matching Text with Regular Expressions<br />
        Toward a More Real-World Example<br />
        Side Effects of a Successful Match<br />
        Intertwined Regular Expressions<br />
        Inter mission<br />
    Modifying Text with Regular Expressions<br />
        Example: Form Letter<br />
        Example: Prettifying a Stock Price<br />
        Automated Editing<br />
        A Small Mail Utility<br />
        Adding Commas to a Number with Lookaround<br />
        Text-to-HTML Conversion<br />
        That Doubled-Word Thing<br />
3 Over view of Regular Expression Features and Flavors<br />
    A Casual Stroll Across the Regex Landscape<br />
        The Origins of Regular Expressions<br />
        At a Glance<br />
    Care and Handling of Regular Expressions<br />
        Integrated Handling<br />
        Procedural and Object-Oriented Handling<br />
        A Search-and-Replace Example<br />
        Search and Replace in Other Languages<br />
        Care and Handling: Summary<br />
    Strings, Character Encodings, and Modes<br />
        Strings as Regular Expressions<br />
        Character-Encoding Issues<br />
        Unicode<br />
        Regex Modes and Match Modes<br />
    Common Metacharacters and Features<br />
        Character Representations<br />
        Character Classes and Class-Like Constructs<br />
        Anchors and Other “Zero-Width Assertions”<br />
        Comments and Mode Modifiers<br />
        Grouping, Capturing, Conditionals, and Control<br />
    Guide to the Advanced Chapters<br />
4 The Mechanics of Expression Processing<br />
    Start Your Engines!<br />
        Two Kinds of Engines<br />
        New Standards<br />
        Regex Engine Types<br />
        From the Department of Redundancy Department<br />
        Testing the Engine Type<br />
    Match Basics<br />
        About the Examples<br />
        Rule 1: The Match That Begins Earliest Wins<br />
        Engine Pieces and Parts<br />
        Rule 2: The Standard Quantifiers Are Greedy<br />
    Regex-Directed Versus Text-Directed<br />
        NFA Engine: Regex-Directed<br />
        DFA Engine: Text-Dir ected<br />
        First Thoughts: NFA and DFA in Comparison<br />
    Backtracking<br />
        A Really Crummy Analogy<br />
        Two Important Points on Backtracking<br />
        Saved States<br />
        Backtracking and Greediness<br />
    More About Greediness and Backtracking<br />
        Problems of Greediness<br />
        Multi-Character “Quotes”<br />
        Using Lazy Quantifiers<br />
        Greediness and Laziness Always Favor a Match<br />
        The Essence of Greediness, Laziness, and Backtracking<br />
        Possessive Quantifiers and Atomic Grouping<br />
        Possessive Quantifiers, ?+, ++, ++, and {m,n}+<br />
        The Backtracking of Lookaround<br />
        Is Alternation Greedy?<br />
        Taking Advantage of Ordered Alternation<br />
    NFA, DFA, and POSIX<br />
        “The Longest-Leftmost”<br />
        POSIX and the Longest-Leftmost Rule<br />
        Speed and Efficiency<br />
        Summary: NFA and DFA in Comparison<br />
    Summary<br />
5 Practical Regex Techniques<br />
    Regex Balancing Act<br />
    A Few Short Examples<br />
        Continuing with Continuation Lines<br />
        Matching an IP Addr ess<br />
        Working with Filenames<br />
        Matching Balanced Sets of Parentheses<br />
        Watching Out for Unwanted Matches<br />
        Matching Delimited Text<br />
        Knowing Your Data and Making Assumptions<br />
        Stripping Leading and Trailing Whitespace<br />
    HTML-Related Examples<br />
        Matching an HTML Tag<br />
        Matching an HTML Link<br />
        Examining an HTTP URL<br />
        Validating a Hostname<br />
        Plucking Out a URL in the Real World<br />
    Extended Examples<br />
        Keeping in Sync with Your Data<br />
        Parsing CSV Files<br />
6 Crafting an Efficient Expression<br />
    A Sobering Example<br />
        A Simple Change—Placing Your Best Foot Forward<br />
        Efficiency Versus Correctness<br />
        Advancing Further—Localizing the Greediness<br />
        Reality Check<br />
    A Global View of Backtracking<br />
        More Work for a POSIX NFA<br />
        Work Required During a Non-Match<br />
        Being More Specific<br />
        Alternation Can Be Expensive<br />
    Benchmarking<br />
        Know What You’r e Measuring<br />
        Benchmarking with PHP<br />
        Benchmarking with Java<br />
        Benchmarking with VB.NET<br />
        Benchmarking with Ruby<br />
        Benchmarking with Python<br />
        Benchmarking with Tcl<br />
    Common Optimizations<br />
        No Free Lunch<br />
        Everyone’s Lunch is Different<br />
        The Mechanics of Regex Application<br />
        Pre-Application Optimizations<br />
        Optimizations with the Transmission<br />
        Optimizations of the Regex Itself<br />
    Techniques for Faster Expressions<br />
        Common Sense Techniques<br />
        Expose Literal Text<br />
        Expose Anchors<br />
        Lazy Versus Greedy: Be Specific<br />
        Split Into Multiple Regular Expressions<br />
        Mimic Initial-Character Discrimination<br />
        Use Atomic Grouping and Possessive Quantifiers<br />
        Lead the Engine to a Match<br />
    Unrolling the Loop<br />
        Method 1: Building a Regex From Past Experiences<br />
        The Real “Unrolling-the-Loop” Pattern<br />
        Method 2: A Top-Down View<br />
        Method 3: An Internet Hostname<br />
        Observations<br />
        Using Atomic Grouping and Possessive Quantifiers<br />
        Short Unrolling Examples<br />
        Unrolling C Comments<br />
    The Freeflowing Regex<br />
        A Helping Hand to Guide the Match<br />
        A Well-Guided Regex is a Fast Regex<br />
        Wrapup<br />
    In Summary: Think!<br />
7 Perl<br />
    Regular Expressions as a Language Component<br />
        Perl’s Greatest Strength<br />
        Perl’s Greatest Weakness<br />
    Perl’s Regex Flavor<br />
        Regex Operands and Regex Literals<br />
        How Regex Literals Are Parsed<br />
        Regex Modifiers<br />
    Regex-Related Perlisms<br />
        Expression Context<br />
        Dynamic Scope and Regex Match Effects<br />
        Special Variables Modified by a Match<br />
    The qr/˙˙˙/ Operator and Regex Objects<br />
        Building and Using Regex Objects<br />
        Viewing Regex Objects<br />
        Using Regex Objects for Efficiency<br />
    The Match Operator<br />
        Match’s Regex Operand<br />
        Specifying the Match Target Operand<br />
        Different Uses of the Match Operator<br />
        Iterative Matching: Scalar Context, with /g<br />
        The Match Operator’s Environmental Relations<br />
    The Substitution Operator<br />
        The Replacement Operand<br />
        The /e Modifier<br />
        Context and Return Value<br />
    The Split Operator<br />
        Basic Split<br />
        Returning Empty Elements<br />
        Split’s Special Regex Operands<br />
        Split’s Match Operand with Capturing Parentheses<br />
    Fun with Perl Enhancements<br />
        Using a Dynamic Regex to Match Nested Pairs<br />
        Using the Embedded-Code Construct<br />
        Using local in an Embedded-Code Construct<br />
        A Warning About Embedded Code and my Variables<br />
        Matching Nested Constructs with Embedded Code<br />
        Overloading Regex Literals<br />
        Problems with Regex-Literal Overloading<br />
        Mimicking Named Capture<br />
    Perl Efficiency Issues<br />
        “Ther e’s Mor e Than One Way to Do It”<br />
        Regex Compilation, the /o Modifier, qr/˙˙˙/, and Efficiency<br />
        Understanding the “Pre-Match” Copy<br />
        The Study Function<br />
        Benchmarking<br />
        Regex Debugging Information<br />
    Final Comments<br />
8 Java<br />
    Java’s Regex Flavor<br />
        Java Support for \p{˙˙˙} and \P{˙˙˙}<br />
        Unicode Line Terminators<br />
    Using java.util.regex<br />
    The Pattern.compile() Factory<br />
        Pattern’s matcher method<br />
    The Matcher Object<br />
        Applying the Regex<br />
        Querying Match Results<br />
        Simple Search and Replace<br />
        Advanced Search and Replace<br />
        In-Place Search and Replace<br />
        The Matcher’s Region<br />
        Method Chaining<br />
        Methods for Building a Scanner<br />
        Other Matcher Methods<br />
    Other Pattern Methods<br />
        Pattern’s split Method, with One Argument<br />
        Pattern’s split Method, with Two Arguments<br />
    Additional Examples<br />
        Adding Width and Height Attributes to Image Tags<br />
        Validating HTML with Multiple Patterns Per Matcher<br />
        Parsing Comma-Separated Values (CSV) Text<br />
    Java Version Differences<br />
        Differences Between 1.4.2 and 1.5.0<br />
        Differences Between 1.5.0 and 1.6<br />
9 .NET<br />
    .NET’s Regex Flavor<br />
        Additional Comments on the Flavor<br />
    Using .NET Regular Expressions<br />
        Regex Quickstart<br />
        Package Overview<br />
        Core Object Overview<br />
    Core Object Details<br />
        Creating Regex Objects<br />
        Using Regex Objects<br />
        Using Match Objects<br />
        Using Group Objects<br />
    Static “Convenience” Functions<br />
        Regex Caching<br />
    Support Functions<br />
    Advanced .NET<br />
        Regex Assemblies<br />
        Matching Nested Constructs<br />
        Capture Objects<br />
10 PHP<br />
    PHP’s Regex Flavor<br />
    The Preg Function Interface<br />
        “Pattern” Arguments<br />
    The Preg Functions<br />
        preg_match<br />
        preg_matchRall<br />
        preg_replace<br />
        preg_replaceRcallback<br />
        preg_split<br />
        preg_grep<br />
        preg_quote<br />
    “Missing” Preg Functions<br />
        preg_regex_to_pattern<br />
        Syntax-Checking an Unknown Pattern Argument<br />
        Syntax-Checking an Unknown Regex<br />
    Recursive Expressions<br />
        Matching Text with Nested Parentheses<br />
        No Backtracking Into Recursion<br />
        Matching a Set of Nested Parentheses<br />
    PHP Efficiency Issues<br />
        The S Pattern Modifier: “Study”<br />
    Extended Examples<br />
        CSV Parsing with PHP<br />
        Checking Tagged Data for Proper Nesting<br />
Index</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/lisf.wordpress.com/42/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/lisf.wordpress.com/42/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/lisf.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/lisf.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/lisf.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/lisf.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/lisf.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/lisf.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/lisf.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/lisf.wordpress.com/42/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/lisf.wordpress.com/42/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/lisf.wordpress.com/42/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=lisf.wordpress.com&blog=829250&post=42&subd=lisf&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://lisf.wordpress.com/2007/07/26/mastering-regular-expressions/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/f225dfc5180b6ae93d054de8c06b08dc?s=96&#38;d=identicon" medium="image">
			<media:title type="html">lisf</media:title>
		</media:content>
	</item>
	</channel>
</rss>