<?xml version="1.0" encoding="UTF-8" ?>















<rss version="2.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">

<channel>
<title><![CDATA[考拉]]> </title>
<description>
<![CDATA[大胆假设，小心求证！]]>
</description>
<link>http://joyjiang.blog.bokee.net/</link>
<language>zh-cn</language>
<creator>joyjiang</creator>
<pubDate>Wed, 31 Aug 2005 12:03:33 CST </pubDate>
<generatorAgent rdf:resource="http://www.bokee.net"/>
<ttl>5</ttl>

<item>
<title>投名状</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/1306774.html</link>
<description>
<![CDATA[<p>投名状到底是为了谁?</p>
<p>自己,别人?</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/1306774.html</guid>
<subject>思想</subject>
<author>joyjiang</author>
<category>思想</category>
<pubDate>Fri, 28 Dec 2007 15:15:08 CST </pubDate>
</item>

<item>
<title>Javascript 闭包</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/988604.html</link>
<description>
<![CDATA[<ul>
    <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clIntro">简介</a> </li>
    <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clResO">基于对象的属性名解析</a>
    <ul>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clResA">值的指定</a> </li>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clResR">值的读取</a> </li>
    </ul>
    </li>
    <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clIRExSc">标识符解析、执行环境和作用域链</a>
    <ul>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clExCon">执行环境</a> </li>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clScCh">作用域链与 [[scope]]</a> </li>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clIdRes">标识符解析</a> </li>
    </ul>
    </li>
    <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clClose">闭包</a>
    <ul>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clAtGb">自动垃圾收集</a> </li>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clFrmC">构成闭包</a> </li>
    </ul>
    </li>
    <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clClDo">通过闭包可以做什么？</a>
    <ul>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clSto">例 1：为函数引用设置延时</a> </li>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clObjI">例 2：通过对象实例方法关联函数</a> </li>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clEncap">例 3：包装相关的功能</a> </li>
        <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clOtE">其他例子</a> </li>
    </ul>
    </li>
    <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clAc">意外的闭包</a> </li>
    <li><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#clMem">Internet Explorer 的内在泄漏问题</a> </li>
</ul>
<h2><a id="clIntro" name="clIntro">简介</a></h2>
<blockquote cite="http://groups.google.com/groups?selm=wu535hos.fsf@hotpop.com"><dl><dt><a id="clDefN" name="clDefN">Closure</a> </dt><dd>所谓&ldquo;闭包&rdquo;，指的是一个拥有许多变量和绑定了这些变量的环境的表达式（通常是一个函数），因而这些变量也是该表达式的一部分。 </dd><dd></dd></dl></blockquote>
<p>闭包是 ECMAScript （JavaScript）最强大的特性之一，但用好闭包的前提是必须理解闭包。闭包的创建相对容易，人们甚至会在不经意间创建闭包，但这些无意创建的闭包却存在潜在的危害，尤其是在比较常见的浏览器环境下。如果想要扬长避短地使用闭包这一特性，则必须了解它们的工作机制。而闭包工作机制的实现很大程度上有赖于标识符（或者说对象属性）解析过程中作用域的角色。 </p>
<p>关于闭包，最简单的描述就是 ECMAScript 允许使用内部函数－－即函数定义和函数表达式位于另一个函数的函数体内。而且，这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时，就会形成闭包。也就是说，内部函数会在外部函数返回后被执行。而当这个内部函数执行时，它仍然必需访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明（最初时）的值是外部函数返回时的值，但也会受到内部函数的影响。 </p>
<p>遗憾的是，要适当地理解闭包就必须理解闭包背后运行的机制，以及许多相关的技术细节。虽然本文的前半部分并没有涉及 ECMA 262 规范指定的某些算法，但仍然有许多无法回避或简化的内容。对于个别熟悉对象属性名解析的人来说，可以跳过相关的内容，但是除非你对闭包也非常熟悉，否则最好是不要跳下面几节。 </p>
<h2><a id="clResO" name="clResO">对象属性名解析</a></h2>
<p>ECMAScript 认可两类对象：原生（Native）对象和宿主（Host）对象，其中宿主对象包含一个被称为内置对象的原生对象的子类（ECMA 262 3rd Ed Section 4.3）。原生对象属于语言，而宿主对象由环境提供，比如说可能是文档对象、DOM 等类似的对象。 </p>
<p>原生对象具有松散和动态的命名属性（对于某些实现的内置对象子类别而言，动态性是受限的－－但这不是太大的问题）。对象的命名属性用于保存值，该值可以是指向另一个对象（Objects）的引用（在这个意义上说，函数也是对象），也可以是一些基本的数据类型，比如：String、Number、Boolean、Null 或 Undefined。其中比较特殊的是 Undefined 类型，因为可以给对象的属性指定一个 Undefined 类型的值，而不会删除对象的相应属性。而且，该属性只是保存着 <code>undefined</code> 值。 </p>
<p>下面简要介绍一下如何设置和读取对象的属性值，并最大程度地体现相应的内部细节。 </p>
<h3><a id="clResA" name="clResA">值的赋予</a></h3>
<p>对象的命名属性可以通过为该命名属性赋值来创建，或重新赋值。即，对于： </p>
<pre>var objectRef = new Object(); <span class="commentJS">//创建一个普通的 javascript 对象。</span>
</pre>
<p>可以通过下面语句来创建名为 &ldquo;testNumber&rdquo; 的属性： </p>
<pre>objectRef.testNumber = 5;
<span class="commentJS">/* - 或- */</span>
objectRef[&quot;testNumber&quot;] = 5;
</pre>
<p>在赋值之前，对象中没有&ldquo;testNumber&rdquo; 属性，但在赋值后，则创建一个属性。之后的任何赋值语句都不需要再创建这个属性，而只会重新设置它的值： </p>
<pre>objectRef.testNumber = 8;
<span class="commentJS">/* - 或- */</span>
objectRef[&quot;testNumber&quot;] = 8;
</pre>
<p>稍后我们会介绍，Javascript 对象都有原型（prototypes）属性，而这些原型本身也是对象，因而也可以带有命名的属性。但是，原型对象命名属性的作用并不体现在赋值阶段。同样，在将值赋给其命名属性时，如果对象没有该属性则会创建该命名属性，否则会重设该属性的值。 </p>
<h3><a id="clResR" name="clResR">值的读取</a></h3>
<p>当读取对象的属性值时，原型对象的作用便体现出来。如果对象的原型中包含属性访问器（property accessor）所使用的属性名，那么该属性的值就会返回： </p>
<pre><span class="commentJS">/* 为命名属性赋值。如果在赋值前对象没有相应的属性，那么赋值后就会得到一个：*/</span>
objectRef.testNumber = 8;

<span class="commentJS">/* 从属性中读取值 */</span>

var val = objectRef.testNumber;
<span class="commentJS">/* 现在， - val - 中保存着刚赋给对象命名属性的值 8*/</span>
   </pre>
<p>而且，由于所有对象都有原型，而原型本身也是对象，所以原型也可能有原型，这样就构成了所谓的原型链。原型链终止于链中原型为 null 的对象。<code>Object</code> 构造函数的默认原型就有一个 null 原型，因此： </p>
<pre>var objectRef = new Object(); <span class="commentJS">//创建一个普通的 JavaScript 对象。</span>
</pre>
<p>创建了一个原型为 <code>Object.prototype</code> 的对象，而该原型自身则拥有一个值为 null 的原型。也就是说，<code>objectRef</code> 的原型链中只包含一个对象－－ <code>Object.prototype</code>。但对于下面的代码而言： </p>
<pre><span class="commentJS">/* 创建 - MyObject1 - 类型对象的函数*/</span>
function MyObject1(formalParameter){
    <span class="commentJS">/* 给创建的对象添加一个名为 - testNumber - 
       的属性并将传递给构造函数的第一个参数指定为该属性的值：*/</span>
    this.testNumber = formalParameter;
}

<span class="commentJS">/* 创建 - MyObject2 - 类型对象的函数*/</span>
function MyObject2(formalParameter){
   <span class="commentJS">/* 给创建的对象添加一个名为 - testString - 
      的属性并将传递给构造函数的第一个参数指定为该属性的值：*/</span>
    this.testString = formalParameter;
}

<span class="commentJS">
/* 接下来的操作用 MyObject1 类的实例替换了所有与 MyObject2 
类的实例相关联的原型。而且，为 MyObject1 构造函数传递了参数
- 8 - ，因而其 - testNumber - 属性被赋予该值：*/</span>
MyObject2.prototype = new MyObject1( 8 );

<span class="commentJS">
/* 最后，将一个字符串作为构造函数的第一个参数，
创建一个 - MyObject2 - 的实例，并将指向该对象的
引用赋给变量 - objectRef - ：*/</span>

var objectRef = new MyObject2( &quot;String_Value&quot; );
</pre>
<p>被变量 <code>objectRef</code> 所引用的 <code>MyObject2</code> 的实例拥有一个原型链。该链中的第一个对象是在创建后被指定给 <code>MyObject2</code> 构造函数的 prototype 属性的 <code>MyObject1</code> 的一个实例。<code>MyObject1</code> 的实例也有一个原型，即与 <code>Object.prototype</code> 所引用的对象对应的默认的 Object 对象的原型。最后， <code>Object.prototype</code> 有一个值为 null 的原型，因此这条原型链到此结束。 </p>
<p>当某个属性访问器尝试读取由 <code>objectRef</code> 所引用的对象的属性值时，整个原型链都会被搜索。在下面这种简单的情况下： </p>
<pre>var val = objectRef.testString;
</pre>
<p>因为 <code>objectRef</code> 所引用的 <code>MyObject2</code> 的实例有一个名为&ldquo;testString&rdquo;的属性，因此被设置为&ldquo;String_Value&rdquo;的该属性的值被赋给了变量 <code>val</code>。但是： </p>
<pre>var val = objectRef.testNumber;
</pre>
<p>则不能从 <code>MyObject2</code> 实例自身中读取到相应的命名属性值，因为该实例没有这个属性。然而，变量 <code>val</code> 的值仍然被设置为 <code>8</code>，而不是未定义－－这是因为在该实例中查找相应的命名属性失败后，解释程序会继续检查其原型对象。而该实例的原型对象是 <code>MyObject1</code> 的实例，这个实例有一个名为&ldquo;testNumber&rdquo;的属性并且值为 <code>8</code>，所以这个属性访问器最后会取得值 <code>8</code>。而且，虽然 <code>MyObject1</code> 和 <code>MyObject2</code> 都没有定义 <code>toString</code> 方法，但是当属性访问器通过 <code>objectRef</code> 读取 <code>toString</code> 属性的值时： </p>
<pre>var val = objectRef.toString;
</pre>
<p>变量 <code>val</code> 也会被赋予一个函数的引用。这个函数就是在 <code>Object.prototype</code> 的 <code>toString</code> 属性中所保存的函数。之所以会返回这个函数，是因为发生了搜索<code>objectRef</code> 原型链的过程。当在作为对象的 <code>objectRef</code> 中发现没有&ldquo;toString&rdquo;属性存在时，会搜索其原型对象，而当原型对象中不存在该属性时，则会继续搜索原型的原型。而原型链中最终的原型是 <code>Object.prototype</code>，这个对象确实有一个 <code>toString</code> 方法，因此该方法的引用被返回。 </p>
<p>最后： </p>
<pre>var val = objectRef.madeUpProperty;
</pre>
<p>返回 <code>undefined</code>，因为在搜索原型链的过程中，直至 <code>Object.prototype</code> 的原型－－null，都没有找到任何对象有名为&ldquo;madeUpPeoperty&rdquo;的属性，因此最终返回 <code>undefined</code>。 </p>
<p>不论是在对象或对象的原型中，读取命名属性值的时候只返回首先找到的属性值。而当为对象的命名属性赋值时，如果对象自身不存在该属性则创建相应的属性。 </p>
<p>这意味着，如果执行像 <code>objectRef.testNumber = 3</code> 这样一条赋值语句，那么这个 <code>MyObject2</code> 的实例自身也会创建一个名为&ldquo;testNumber&rdquo;的属性，而之后任何读取该命名属性的尝试都将获得相同的新值。这时候，属性访问器不会再进一步搜索原型链，但 <code>MyObject1</code> 实例值为 <code>8</code> 的&ldquo;testNumber&rdquo;属性并没有被修改。给 <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 id="clIRExSc" name="clIRExSc">标识符解析、执行环境和作用域链</a></h2>
<h3><a id="clExCon" name="clExCon">执行环境</a></h3>
<p>执行环境是 ECMAScript 规范（ECMA 262 第 3 版）用于定义 ECMAScript 实现必要行为的一个抽象的概念。对如何实现执行环境，规范没有作规定。但由于执行环境中包含引用规范所定义结构的相关属性，因此执行环境中应该保有（甚至实现）带有属性的对象－－即使属性不是公共属性。 </p>
<p>所有 JavaScript 代码都是在一个执行环境中被执行的。全局代码（作为内置的 JS 文件执行的代码，或者 <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr>HTML</span> 页面加载的代码）是在我将称之为&ldquo;全局执行环境&rdquo;的执行环境中执行的，而对函数的每次调用（有可能是作为构造函数）同样有关联的执行环境。通过 <code>eval</code> 函数执行的代码也有截然不同的执行环境，但因为 JavaScript 程序员在正常情况下一般不会使用 <code>eval</code>，所以这里不作讨论。有关执行环境的详细说明请参阅 ECMA 262（第 3 版）第 10.2 节。 </p>
<p>当调用一个 JavaScript 函数时，该函数就会进入相应的执行环境。如果又调用了另外一个函数（或者递归地调用同一个函数），则又会创建一个新的执行环境，并且在函数调用期间执行过程都处于该环境中。当调用的函数返回后，执行过程会返回原始执行环境。因而，运行中的 JavaScript 代码就构成了一个执行环境栈。 </p>
<p>在创建执行环境的过程中，会按照定义的先后顺序完成一系列操作。首先，在一个函数的执行环境中，会创建一个&ldquo;活动&rdquo;对象。活动对象是规范中规定的另外一种机制。之所以称之为对象，是因为它拥有可访问的命名属性，但是它又不像正常对象那样具有原型（至少没有预定义的原型），而且不能通过 JavaScript 代码直接引用活动对象。 </p>
<p>为函数调用创建执行环境的下一步是创建一个 <code>arguments</code> 对象，这是一个类似数组的对象，它以整数索引的数组成员一一对应地保存着调用函数时所传递的参数。这个对象也有 <code>length</code> 和 <code>callee</code> 属性（这两个属性与我们讨论的内容无关，详见规范）。然后，会为活动对象创建一个名为&ldquo;arguments&rdquo;的属性，该属性引用前面创建的 <code>arguments</code> 对象。 </p>
<p>接着，为执行环境分配作用域。作用域由对象列表（链）组成。每个函数对象都有一个内部的 <code>[[scope]]</code> 属性（该属性我们稍后会详细介绍），这个属性也由对象列表（链）组成。指定给一个函数调用执行环境的作用域，由该函数对象的 <code>[[scope]]</code> 属性所引用的对象列表（链）组成，同时，活动对象被添加到该对象列表的顶部（链的前端）。 </p>
<p>之后会发生由 ECMA 262 中所谓&ldquo;可变&rdquo;对象完成的&ldquo;变量实例化&rdquo;的过程。只不过此时使用活动对象作为可变对象（这里很重要，请注意：它们是同一个对象）。此时会将函数的形式参数创建为可变对象命名属性，如果调用函数时传递的参数与形式参数一致，则将相应参数的值赋给这些命名属性（否则，会给命名属性赋 <code>undefined</code> 值）。对于定义的内部函数，会以其声明时所用名称为可变对象创建同名属性，而相应的内部函数则被创建为函数对象并指定给该属性。变量实例化的最后一步是将在函数内部声明的所有局部变量创建为可变对象的命名属性。 </p>
<p>根据声明的局部变量创建的可变对象的属性在变量实例化过程会被赋予 <code>undefined</code> 值。在执行函数体内的代码、并计算相应的赋值表达式之前不会对局部变量执行真正的实例化。 </p>
<p>事实上，拥有 <code>arguments</code> 属性的活动对象和拥有与函数局部变量对应的命名属性的可变对象是同一个对象。因此，可以将标识符 <code>arguments</code> 作为函数的局部变量来看待。 </p>
<a name="note1"></a><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#">回到顶部</a>
<p class="note">最后，要为使用 <code>this</code> 关键字而赋值。如果所赋的值引用一个对象，那么前缀以 <code>this</code> 关键字的属性访问器就是引用该对象的属性。如果所赋（内部）值是 null，那么 <code>this</code> 关键字则引用全局对象。<br />（Finally a value is assigned for use with the <code>this</code> keyword. If the value assigned refers to an object then property accessors prefixed with the <code>this</code> keyword reference properties of that object. If the value assigned (internally) is null then the <code>this</code> keyword will refer to the global object.） </p>
<p>创建全局执行环境的过程会稍有不同，因为它没有参数，所以不需要通过定义的活动对象来引用这些参数。但全局执行环境也需要一个作用域，而它的作用域链实际上只由一个对象－－全局对象－－组成。全局执行环境也会有变量实例化的过程，它的内部函数就是涉及大部分 JavaScript 代码的、常规的顶级函数声明。而且，在变量实例化过程中全局对象就是可变对象，这就是为什么全局性声明的函数是全局对象属性的原因。全局性声明的变量同样如此。 </p>
<p>全局执行环境也会使用 <code>this</code> 对象来引用全局对象。 </p>
<h3><a id="clScCh" name="clScCh">作用域链与 [[scope]]</a></h3>
<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>
<pre>function exampleFunction(formalParameter){
    ...   <span class="commentJS">// 函数体内的代码</span>
}
</pre>
<p>当为创建全局执行环境而进行变量实例化时，会根据上面的函数声明创建相应的函数对象。因为全局执行环境的作用域链中只包含全局对象，所以它就给自己创建的、并以名为&ldquo;exampleFunction&rdquo;的属性引用的这个函数对象的内部 <code>[[scope]]</code> 属性，赋予了只包含全局对象的作用域链。 </p>
<p>当在全局环境中计算函数表达式时，也会发生类似的指定作用域链的过程：- </p>
<pre>var exampleFuncRef = function(){
    ...   <span class="commentJS">// 函数体代码</span>
}
</pre>
<p>在这种情况下，不同的是在全局执行环境的变量实例化过程中，会先为全局对象创建一个命名属性。而在计算赋值语句之前，暂时不会创建函数对象，也不会将该函数对象的引用指定给全局对象的命名属性。但是，最终还是会在全局执行环境中创建这个函数对象（当计算函数表达式时。译者注），而为这个创建的函数对象的 <code>[[scope]]</code> 属性指定的作用域链中仍然只包含全局对象。 </p>
<p>内部的函数声明或表达式会导致在包含它们的外部函数的执行环境中创建相应的函数对象，因此这些函数对象的作用域链会稍微复杂一些。在下面的代码中，先定义了一个带有内部函数声明的外部函数，然后调用外部函数： </p>
<pre>function exampleOuterFunction(formalParameter){
    function exampleInnerFuncitonDec(){
        ... <span class="commentJS">// 内部函数体代码</span>
    }
    ...  <span class="commentJS">// 其余的外部函数体代码</span>
}

exampleOuterFunction( 5 );
</pre>
<p>与外部函数声明对应的函数对象会在全局执行环境的变量实例化过程中被创建。因此，外部函数对象的 <code>[[scope]]</code> 属性中会包含一个只有全局对象的&ldquo;单项目&rdquo;作用域链。 </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>
<pre><span class="commentJS">/* 创建全局变量 - y - 它引用一个对象：- */</span>
var y = {x:5}; <span class="commentJS">// 带有一个属性 - x - 的对象直接量</span>
function exampleFuncWith(){
    var z;
    <span class="commentJS">/* 将全局对象 - y - 引用的对象添加到作用域链的前端：- */</span>
    with(y){
        <span class="commentJS">/* 对函数表达式求值，以创建函数对象并将该函数对象的引用指定给局部变量 - z - :-  */</span>
        z = function(){
            ... <span class="commentJS">// 内部函数表达式中的代码;</span>
        }
    }
    ... 
}

<span class="commentJS">/* 执行 - exampleFuncWith - 函数:- */</span>
exampleFuncWith();
</pre>
<p>在调用 <code>exampleFuncWith</code> 函数所创建的执行环境中包含一个由其活动对象后跟全局对象构成的作用域链。而在执行 <code>with</code> 语句时，又会把全局变量 <code>y</code> 引用的对象添加到这个作用域链的前端。在对其中的函数表达式求值的过程中，所创建函数对象的 <code>[[scope]]</code> 属性与创建它的执行环境的作用域保持一致－－即，该属性会引用一个由对象 <code>y</code> 后跟调用外部函数时所创建执行环境的活动对象，后跟全局对象的作用域链。 </p>
<p>当与 <code>with</code> 语句相关的语句块执行结束时，执行环境的作用域得以恢复（<code>y</code> 会被移除），但是已经创建的函数对象（<code>z</code>。译者注）的 <code>[[scope]]</code> 属性所引用的作用域链中位于最前面的仍然是对象 <code>y</code>。 </p>
<h3><a id="clIdRes" name="clIdRes">标识符解析</a></h3>
<p>标识符是沿作用域链逆向解析的。ECMA 262 将 <code>this</code> 归类为关键字而不是标识符，是不合理的。因为解析 <code>this</code> 值时始终要根据使用它的执行环境来判断，而与作用域链无关。 </p>
<p>标识符解析从作用域链中的第一个对象开始。检查该对象中是否包含与标识符对应的属性名。因为作用域链是一条对象链，所以这个检查过程也会包含相应对象的原型链（如果有）。如果没有在作用域链的第一个对象中发现相应的值，解析过程会继续搜索下一个对象。这样依次类推直至找到作用域链中包含以标识符为属性名的对象为止，也有可能在作用域链的所有对象中都没有发现该标识符。 </p>
<p>当基于对象使用属性访问器时，也会发生与上面相同的标识符解析过程。当属性访问器中有相应的属性可以替换某个对象时，这个属性就成为表示该对象的标识符，该对象在作用域链中的位置进而被确定。全局对象始终都位于作用域链的尾端。 </p>
<p>因为与函数调用相关的执行环境将会把活动（可变）对象添加到作用域链的前端，所以在函数体内使用的标识符会首先检查自己是否与形式参数、内部函数声明的名称或局部变量一致。这些都可以由活动（可变）对象的命名属性来确定。 </p>
<h2><a id="clClose" name="clClose">闭包</a></h2>
<h3><a id="clAtGb" name="clAtGb">自动垃圾收集</a></h3>
<p>ECMAScript 要求使用自动垃圾收集机制。但规范中并没有详细说明相关的细节，而是留给了实现来决定。但据了解，相当一部分实现对它们的垃圾收集操作只赋予了很低的优先级。但是，大致的思想都是相同的，即如果对象不再&ldquo;可引用（由于不存在对它的引用，使执行代码无法再访问到它）&rdquo;时，该对象就成为垃圾收集的目标。因而，在将来的某个时刻会将这个对象销毁并将它所占用的一切资源释放，以便操作系统重新利用。 </p>
<p>正常情况下，当退出一个执行环境时就会满足类似的条件。此时，作用域链结构中的活动（可变）对象以及在该执行环境中创建的任何对象－－包括函数对象，都不再&ldquo;可引用&rdquo;，因此将成为垃圾收集的目标。 </p>
<h3><a id="clFrmC" name="clFrmC">构成闭包</a></h3>
<p>闭包是通过在对一个函数调用的执行环境中返回一个函数对象构成的。比如，在对函数调用的过程中，将一个对内部函数对象的引用指定给另一个对象的属性。或者，直接将这样一个（内部）函数对象的引用指定给一个全局变量、或者一个全局性对象的属性，或者一个作为参数以引用方式传递给外部函数的对象。例如：- </p>
<pre>function exampleClosureForm(arg1, arg2){
    var localVar = 8;
    function exampleReturned(innerArg){
        return ((arg1 + arg2)/(innerArg + localVar));
    }
    <span class="commentJS">/* 返回一个定义为 exampleReturned 的内部函数的引用 -:-  */</span>
    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;对象为 &quot;ActOuter1&quot;）。 </p>
<p>如果再次调用 <code>exampleClosureForm</code> 函数，如：- </p>
<pre>var secondGlobalVar = exampleClosureForm(12, 3);
</pre>
<p>- 则会创建一个新的执行环境和一个新的活动对象。而且，会返回一个新的函数对象，该函数对象的 <code>[[scope]]</code> 属性引用的作用域链与前一次不同，因为这一次的作用域链中包含着第二个执行环境的活动对象，而这个活动对象的属性 <code>arg1</code> 值为 <code>12</code> 而属性 <code>arg2</code> 值为 <code>3</code>。（为方便起见，我们将在后面的讨论中，称这个活动&lt;可变&gt;对象为 &quot;ActOuter2&quot;）。 </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>－－时的情形。此时，会创建一个新的执行环境和相应的活动对象（我们将称之为&ldquo;ActInner1&rdquo;），并把该活动对象添加到执行的函数对象的 <code>[[scope]]</code> 属性所引用的作用域链的前端。ActInner1 会带有一个属性 <code>innerArg</code>，根据传递的形式参数，其值被指定为 <code>2</code>。这个新执行环境的作用域链变成： <font face="Courier"><span class="scopeCh">ActInner1-&gt;</span><span class="scopeCh">ActOuter1-&gt;</span><span class="scopeCh">全局对象</span></font>. </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> 是 <code>4</code> 而 <code>localVar</code> 是 <code>8</code>。最后，函数调用返回 <code>((2 + 2)/(2 + 8))</code>。 </p>
<p>现在再来看一看由 <code>secondGlobalVar</code> 引用的同一个函数对象的执行情况，比如 <code>secondGlobalVar(5)</code>。我们把这次创建的新执行环境的活动对象称为 &ldquo;ActInner2&rdquo;，相应的作用域链就变成了：<font face="Courier"><span class="scopeCh">ActInner2-&gt;</span><span class="scopeCh">ActOuter2-&gt;</span><span class="scopeCh">全局对象</span></font>。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 id="clClDo" name="clClDo">通过闭包可以做什么？</a></h2>
<p>对这个问题的回答可能会令你惊讶－－闭包什么都可以做。据我所知，闭包使得 ECMAScript 能够模仿任何事物，因此局限性在于设计和实现要模仿事物的能力。只是从字面上看可能会觉得这么说很深奥，下面我们就来看一些更有实际意义的例子。 </p>
<h3><a id="clSto" name="clSto">例 1：为函数引用设置延时</a></h3>
<p>闭包的一个常见用法是在执行函数之前为要执行的函数提供参数。例如：将函数作为 <code>setTimout</code> 函数的第一个参数，这在 Web 浏览器的环境下是非常常见的一种应用。 </p>
<p><code>setTimeout</code> 用于有计划地执行一个函数（或者一串 JavaScript 代码，不是在本例中），要执行的函数是其第一个参数，其第二个参数是以毫秒表示的执行间隔。也就是说，当在一段代码中使用 <code>setTimeout</code> 时，要将一个函数的引用作为它的第一个参数，而将以毫秒表示的时间值作为第二个参数。但是，传递函数引用的同时无法为计划执行的函数提供参数。 </p>
<p>然而，可以在代码中调用另外一个函数，由它返回一个对内部函数的引用，再把这个对内部函数对象的引用传递给 <code>setTimeout</code> 函数。执行这个内部函数时要使用的参数在调用返回它的外部函数时传递。这样，<code>setTimeout</code> 在执行这个内部函数时，不用传递参数，但该内部函数仍然能够访问在调用返回它的外部函数时传递的参数： </p>
<pre>function callLater(paramA, paramB, paramC){
    <span class="commentJS">/* 返回一个由函数表达式创建的匿名内部函数的引用:- */
</span>
    return (function(){
        <span class="commentJS">/* 这个内部函数将通过 - setTimeout - 执行，
         而且当它执行时它会读取并按照传递给
         外部函数的参数行事：
        */</span>
        paramA[paramB] = paramC;
    });
}

...

<span class="commentJS">/* 调用这个函数将返回一个在其执行环境中创建的内部函数对象的引用。
 传递的参数最终将作为外部函数的参数被内部函数使用。
 返回的对内部函数的引用被赋给一个全局变量:-
*/
</span>
var functRef = callLater(elStyle, &quot;display&quot;, &quot;none&quot;);
<span class="commentJS">/* 调用 setTimeout 函数，将赋给变量 - functRef - 
的内部函数的引用作为传递的第一个参数:- */ 
</span>
hideMenu=setTimeout(functRef, 500);
</pre>
<h3><a id="clObjI" name="clObjI">例 2: 通过对象实例方法关联函数</a></h3>
<a name="note2"></a><a href="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#">回到顶部</a>
<p class="note">在另外一些情况下，当指定一个对函数对象的引用，以便在将来某一时刻执行它时，为执行该函数提供相应的参数同样是很有用的。但这个函数在执行时未必有效，而且在赋值之前处于未知状态。<br />（There are many other circumstances when a reference to a function object is assigned so that it would be executed at some future time where it is useful to provide parameters for the execution of that function that would not be easily available at the time of execution but cannot be known until the moment of assignment.）</p>
<p>一个相关的例子是，用 JavaScript 对象来封装与特定 DOM 元素的交互。这个 JavaScript 对象具有 <code>doOnClick</code>、<code>doMouseOver</code> 和 <code>doMouseOut</code> 方法，并且当用户在该特定的 DOM 元素中触发了相应的事件时要执行这些方法。不过，可能会创建与不同的 DOM 元素关联的任意数量的 JavaScript 对象，而且每个对象实例并不知道实例化它们的代码将会如何操纵它们（即注册事件处理函数与定义相应的事件处理函数分离。译者注）。这些对象实例并不知道如何在全局环境中引用它们自身，因为它们不知道将会指定哪个全局变量（如果有）引用它们的实例。 </p>
<p>因而问题可以归结为执行一个与特定的 JavaScript 对象关联的事件处理函数，并且要知道调用该对象的哪个方法。</p>
<p>下面这个例子使用了一个基于一般化的闭包构建的函数，该函数会将对象实例与 DOM 元素事件关联起来，安排执行事件处理程序时调用对象实例的指定方法，给对象的指定方法传递的参数是事件对象和与元素关联的引用，该函数返回执行相应方法后的返回值。 </p>
<pre><span class="commentJS">/* 一个关联对象实例和事件处理器的函数。 
它返回的内部函数被用作事件处理器。对象实例以 - obj - 参数表示， 
而在该对象实例中调用的方法名则以 - methodName - （字符串）参数表示。 
*/ 
</span>
function associateObjWithEvent(obj, methodName){
    <span class="commentJS">/* 下面这个返回的内部函数将作为一个 DOM 元素的事件处理器*/ 
</span>
    return (function(e){
        <span class="commentJS"> /* 在支持标准 DOM 规范的浏览器中，事件对象会被解析为参数 - e - ， 
           若没有正常解析，则使用 IE 的事件对象来规范化事件对象。 
        */ 
</span>
        e = e||window.event;
        <span class="commentJS">/* 事件处理器通过保存在字符串 - methodName - 中的方法名调用了对象 
      - obj - 的一个方法。并传递已经规范化的事件对象和触发事件处理器的元素 
      的引用 - this - （之所以 this 有效是因为这个内部函数是作为该元素的方法执行的） 
     */ 
</span>
        return obj[methodName](e, this);
    });
}

<span class="commentJS">/* 这个构造函数用于创建将自身与 DOM 元素关联的对象， 
   DOM 元素的 ID 作为构造函数的字符串参数。 
   所创建的对象会在相应的元素触发 onclick、 
   onmouseover 或 onmouseout 事件时， 
   调用相应的方法。 
*/ 
</span>
function DhtmlObject(elementId){
    <span class="commentJS">/* 调用一个返回 DOM 元素（如果没找到返回 null）引用的函数， 
   必需的参数是 ID。 将返回的值赋给局部变量 - el -。 
    */ </span>
    var el = getElementWithId(elementId);
    <span class="commentJS"> /* - el - 值会在内部通过类型转换变为布尔值，以便 - if - 语句加以判断。 
   因此，如果它引用一个对象结果将返回 true，如果是 null 则返回 false。 
   下面的代码块只有当 - el - 变量返回一个 DOM 元素时才会被执行。 
    */</span>
    if(el){
        <span class="commentJS">/* 为给元素的事件处理器指定一个函数，该对象调用了 
     - associateObjWithEvent - 函数。 
     同时对象将自身（通过 - this - 关键字）作为调用方法的对象， 
     并提供了调用的方法名称。 - associateObjWithEvent - 函数会返回 
     一个内部函数，该内部函数被指定为 DOM 元素的事件处理器。 
     在响应事件时，执行这个内部函数就会调用必要的方法。 
     */</span>
        el.onclick = associateObjWithEvent(this, &quot;doOnClick&quot;);
        el.onmouseover = associateObjWithEvent(this, &quot;doMouseOver&quot;);
        el.onmouseout = associateObjWithEvent(this, &quot;doMouseOut&quot;);
        ...
    }
}
DhtmlObject.prototype.doOnClick = function(event, element){
    ... <span class="commentJS">// doOnClick 方法体。</span>.
}
DhtmlObject.prototype.doMouseOver = function(event, element){
    ... <span class="commentJS">// doMouseOver 方法体。</span>
}
DhtmlObject.prototype.doMouseOut = function(event, element){
    ... <span class="commentJS">// doMouseOut 方法体。</span>
}
</pre>
<p>这样，<code>DhtmlObject</code> 的任何实例都会将自身与相应的 DOM 元素关联起来，而这些 DOM 元素不必知道其他代码如何操纵它们（即当触发相应事件时，会执行什么代码。译者注），也不必理会全局命名空间的影响以及与 <code>DhtmlObject</code> 的其他实例间存在冲突的危险。 </p>
<h3><a id="clEncap" name="clEncap">例 3：包装相关的功能</a></h3>
<p>闭包可以用于创建额外的作用域，通过该作用域可以将相关的和具有依赖性的代码组织起来，以便将意外交互的风险降到最低。假设有一个用于构建字符串的函数，为了避免重复性的连接操作（和创建众多的中间字符串），我们的愿望是使用一个数组按顺序来存储字符串的各个部分，然后再使用 <code>Array.prototype.join</code> 方法（以空字符串作为其参数）输出结果。这个数组将作为输出的缓冲器，但是将数组作为函数的局部变量又会导致在每次调用函数时都重新创建一个新数组，这在每次调用函数时只重新指定数组中的可变内容的情况下并不是必要的。 </p>
<p>一种解决方案是将这个数组声明为全局变量，这样就可以重用这个数组，而不必每次都建立新数组。但这个方案的结果是，除了引用函数的全局变量会使用这个缓冲数组外，还会多出一个全局属性引用数组自身。如此不仅使代码变得不容易管理，而且，如果要在其他地方使用这个数组时，开发者必须要再次定义函数和数组。这样一来，也使得代码不容易与其他代码整合，因为此时不仅要保证所使用的函数名在全局命名空间中是唯一的，而且还要保证函数所依赖的数组在全局命名空间中也必须是唯一的。 </p>
<p>而通过闭包可以使作为缓冲器的数组与依赖它的函数关联起来（优雅地打包），同时也能够维持在全局命名空间外指定的缓冲数组的属性名，免除了名称冲突和意外交互的危险。 </p>
<p>其中的关键技巧在于通过执行一个单行（in-line）函数表达式创建一个额外的执行环境，而将该函数表达式返回的内部函数作为在外部代码中使用的函数。此时，缓冲数组被定义为函数表达式的一个局部变量。这个函数表达式只需执行一次，而数组也只需创建一次，就可以供依赖它的函数重复使用。 </p>
<p>下面的代码定义了一个函数，这个函数用于返回一个 <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr>HTML</span> 字符串，其中大部分内容都是常量，但这些常量字符序列中需要穿插一些可变的信息，而可变的信息由调用函数时传递的参数提供。 </p>
<p>通过执行单行函数表达式返回一个内部函数，并将返回的函数赋给一个全局变量，因此这个函数也可以称为全局函数。而缓冲数组被定义为外部函数表达式的一个局部变量。它不会暴露在全局命名空间中，而且无论什么时候调用依赖它的函数都不需要重新创建这个数组。 </p>
<pre><span class="commentJS">/* 声明一个全局变量 - getImgInPositionedDivHtml - 
并将一次调用一个外部函数表达式返回的内部函数赋给它。

   这个内部函数会返回一个用于表示绝对定位的 DIV 元素
   包围着一个 IMG 元素 的 <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr>HTML</span> 字符串，这样一来，
   所有可变的属性值都由调用该函数时的参数提供：
*/</span>
var getImgInPositionedDivHtml = (function(){
    <span class="commentJS">/* 外部函数表达式的局部变量 - buffAr - 保存着缓冲数组。
    这个数组只会被创建一次，生成的数组实例对内部函数而言永远是可用的
    因此，可供每次调用这个内部函数时使用。

    其中的空字符串用作数据占位符，相应的数据
    将由内部函数插入到这个数组中：
    */</span>
    var buffAr = [
        '&lt;div id=&quot;',
        '',   <span class="commentJS">//index 1, DIV ID 属性</span>
        '&quot; style=&quot;position:absolute;top:',
        '',   <span class="commentJS">//index 3, DIV 顶部位置</span>
        'px;left:',
        '',   <span class="commentJS">//index 5, DIV 左端位置</span>
        'px;width:',
        '',   <span class="commentJS">//index 7, DIV 宽度</span>
        'px;height:',
        '',   <span class="commentJS">//index 9, DIV 高度</span>
        'px;overflow:hidden;\&quot;&gt;&lt;img src=\&quot;',
        '',   <span class="commentJS">//index 11, IMG URL</span>
        '\&quot; width=\&quot;',
        '',   <span class="commentJS">//index 13, IMG 宽度</span>
        '\&quot; height=\&quot;',
        '',   <span class="commentJS">//index 15, IMG 调蓄</span>
        '\&quot; alt=\&quot;',
        '',   <span class="commentJS">//index 17, IMG alt 文本内容</span>
        '\&quot;&gt;&lt;\/div&gt;'
    ];
    <span class="commentJS">/* 返回作为对函数表达式求值后结果的内部函数对象。
	这个内部函数就是每次调用执行的函数
	- getImgInPositionedDivHtml( ... ) -
    */</span>
    return (function(url, id, width, height, top, left, altText){
        <span class="commentJS">/* 将不同的参数插入到缓冲数组相应的位置：
        */</span>
        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;
        <span class="commentJS">/* 返回通过使用空字符串（相当于将数组元素连接起来）
		连接数组每个元素后形成的字符串：
        */</span>
        return buffAr.join('');
    }); <span class="commentJS">//:内部函数表达式结束。</span>
})();
<span class="commentJS">/*^^- :单行外部函数表达式。*/</span>
</pre>
<p>如果一个函数依赖于另一（或多）个其他函数，而其他函数又没有必要被其他代码直接调用，那么可以运用相同的技术来包装这些函数，而通过一个公开暴露的函数来调用它们。这样，就将一个复杂的多函数处理过程封装成了一个具有移植性的代码单元。 </p>
<h3><a id="clOtE" name="clOtE">其他例子</a></h3>
<p>有关闭包的一个可能是最广为人知的应用是 <a href="http://www.crockford.com/javascript/private.html">Douglas Crockford's technique for the emulation of private instance variables in ECMAScript objects</a>。这种应用方式可以扩展到各种嵌套包含的可访问性（或可见性）的作用域结构，包括 <a href="http://www.litotes.demon.co.uk/js_info/private_static.html">the emulation of private static members for ECMAScript objects</a>。 </p>
<p>闭包可能的用途是无限的，可能理解其工作原理才是把握如何使用它的最好指南。 </p>
<h2><a id="clAc" name="clAc">意外的闭包</a></h2>
<p>在创建可访问的内部函数的函数体之外解析该内部函数就会构成闭包。这表明闭包很容易创建，但这样一来可能会导致一种结果，即没有认识到闭包是一种语言特性的 JavaScript 作者，会按照内部函数能完成多种任务的想法来使用内部函数。但他们对使用内部函数的结果并不明了，而且根本意识不到创建了闭包，或者那样做意味着什么。 </p>
<p>正如下一节谈到 IE 中内存泄漏问题时所提及的，意外创建的闭包可能导致严重的负面效应，而且也会影响到代码的性能。问题不在于闭包本身，如果能够真正做到谨慎地使用它们，反而会有助于创建高效的代码。换句话说，使用内部函数会影响到效率。 </p>
<p>使用内部函数最常见的一种情况就是将其作为 DOM 元素的事件处理器。例如，下面的代码用于向一个链接元素添加 onclick 事件处理器： </p>
<pre><span class="commentJS">/* 定义一个全局变量，通过下面的函数将它的值
   作为查询字符串的一部分添加到链接的 - href - 中：
*/</span>
var quantaty = 5;
<span class="commentJS">/* 当给这个函数传递一个链接（作为函数中的参数 - linkRef -）时，
   会将一个 onclick 事件处理器指定给该链接，该事件处理器
   将全局变量 - quantaty - 的值作为字符串添加到链接的 - href - 
   属性中，然后返回 true 使该链接在单击后定位到由  - href - 
   属性包含的查询字符串指定的资源：
*/</span>
function addGlobalQueryOnClick(linkRef){
    <span class="commentJS">/* 如果可以将参数 - linkRef - 通过类型转换为 ture
      （说明它引用了一个对象）：
    */</span>
    if(linkRef){
        <span class="commentJS">/* 对一个函数表达式求值，并将对该函数对象的引用
           指定给这个链接元素的 onclick 事件处理器：
        */</span>
        linkRef.onclick = function(){
            <span class="commentJS">/* 这个内部函数表达式将查询字符串
               添加到附加事件处理器的元素的 - href - 属性中：
            */</span>
            this.href += ('?quantaty='+escape(quantaty));
            return true;
        };
    }
}
</pre>
<p>无论什么时候调用 <code>addGlobalQueryOnClick</code> 函数，都会创建一个新的内部函数（通过赋值构成了闭包）。从效率的角度上看，如果只是调用一两次 <code>addGlobalQueryOnClick</code> 函数并没有什么大的妨碍，但如果频繁使用该函数，就会导致创建许多截然不同的函数对象（每对内部函数表达式求一次值，就会产生一个新的函数对象）。 </p>
<p>上面例子中的代码没有关注内部函数在创建它的函数外部可以访问（或者说构成了闭包）这一事实。实际上，同样的效果可以通过另一种方式来完成。即单独地定义一个用于事件处理器的函数，然后将该函数的引用指定给元素的事件处理属性。这样，只需创建一个函数对象，而所有使用相同事件处理器的元素都可以共享对这个函数的引用： </p>
<pre><span class="commentJS">/* 定义一个全局变量，通过下面的函数将它的值
   作为查询字符串的一部分添加到链接的 - href - 中：
*/</span>
var quantaty = 5;

<span class="commentJS">/* 当把一个链接（作为函数中的参数 - linkRef -）传递给这个函数时，
   会给这个链接添加一个 onclick 事件处理器，该事件处理器会
   将全局变量  - quantaty - 的值作为查询字符串的一部分添加到
   链接的 - href -  中，然后返回 true，以便单击链接时定位到由
   作为 - href - 属性值的查询字符串所指定的资源：
*/</span>
function addGlobalQueryOnClick(linkRef){
    <span class="commentJS">/* 如果 - linkRef - 参数能够通过类型转换为 true
    （说明它引用了一个对象）：
    */</span>
    if(linkRef){
        <span class="commentJS">/* 将一个对全局函数的引用指定给这个链接
           的事件处理属性，使函数成为链接元素的事件处理器：
        */</span>
        linkRef.onclick = forAddQueryOnClick;
    }
}
<span class="commentJS">/* 声明一个全局函数，作为链接元素的事件处理器，
   这个函数将一个全局变量的值作为要添加事件处理器的
   链接元素的  - href - 值的一部分：
*/</span>
function forAddQueryOnClick(){
    this.href += ('?quantaty='+escape(quantaty));
    return true;
}
</pre>
<p>在上面例子的第一个版本中，内部函数并没有作为闭包发挥应有的作用。在那种情况下，反而是不使用闭包更有效率，因为不用重复创建许多本质上相同的函数对象。 </p>
<p>类似地考量同样适用于对象的构造函数。与下面代码中的构造函数框架类似的代码并不罕见： </p>
<pre>function ExampleConst(param){
    <span class="commentJS">/* 通过对函数表达式求值创建对象的方法，
       并将求值所得的函数对象的引用赋给要创建对象的属性：
    */</span>
    this.method1 = function(){
        ... <span class="commentJS">// 方法体。</span>
    };
    this.method2 = function(){
        ... <span class="commentJS">// 方法体。</span>
    };
    this.method3 = function(){
        ... <span class="commentJS">// 方法体。</span>
    };
    <span class="commentJS">/* 把构造函数的参数赋给对象的一个属性：
    */</span>
    this.publicProp = param;
}
</pre>
<p>每当通过 <code>new ExampleConst(n)</code> 使用这个构造函数创建一个对象时，都会创建一组新的、作为对象方法的函数对象。因此，创建的对象实例越多，相应的函数对象也就越多。 </p>
<p>Douglas Crockford 提出的模仿 JavaScript 对象私有成员的技术，就利用了将对内部函数的引用指定给在构造函数中构造对象的公共属性而形成的闭包。如果对象的方法没有利用在构造函数中形成的闭包，那么在实例化每个对象时创建的多个函数对象，会使实例化过程变慢，而且将有更多的资源被占用，以满足创建更多函数对象的需要。 </p>
<p>这那种情况下，只创建一次函数对象，并把它们指定给构造函数 <code>prototype</code> 的相应属性显然更有效率。这样一来，它们就能被构造函数创建的所有对象共享了： </p>
<pre>function ExampleConst(param){
    <span class="commentJS">/* 将构造函数的参数赋给对象的一个属性：
    */</span>
    this.publicProp = param;
}
<span class="commentJS">/* 通过对函数表达式求值，并将结果函数对象的引用
   指定给构造函数原型的相应属性来创建对象的方法：
*/</span>
ExampleConst.prototype.method1 = function(){
    ... <span class="commentJS">// 方法体。</span>
};
ExampleConst.prototype.method2 = function(){
    ... <span class="commentJS">// 方法体。</span>
};
ExampleConst.prototype.method3 = function(){
    ... <span class="commentJS">// 方法体。</span>
};

</pre>
<h2><a id="clMem" name="clMem">Internet Explorer 的内在泄漏问题 </a></h2>
<p>Internet Explorer Web 浏览器（在 IE 4 到 IE 6 中核实）的垃圾收集系统中存在一个问题，即如果 ECMAScript 和某些宿主对象构成了 &quot;循环引用&quot;，那么这些对象将不会被当作垃圾收集。此时所谓的宿主对象指的是任何 DOM 节点（包括 document 对象及其后代元素）和 ActiveX 对象。如果在一个循环引用中包含了一或多个这样的对象，那么这些对象直到浏览器关闭都不会被释放，而它们所占用的内存同样在浏览器关闭之前都不会交回系统重用。 </p>
<p>当两个或多个对象以首尾相连的方式相互引用时，就构成了循环引用。比如对象 1 的一个属性引用了对象 2 ，对象 2 的一个属性引用了对象 3，而对象 3 的一个属性又引用了对象 1。对于纯粹的 ECMAScript 对象而言，只要没有其他对象引用对象 1、2、3，也就是说它们只是相互之间的引用，那么仍然会被垃圾收集系统识别并处理。但是，在 Internet Explorer 中，如果循环引用中的任何对象是 DOM 节点或者 ActiveX 对象，垃圾收集系统则不会发现它们之间的循环关系与系统中的其他对象是隔离的并释放它们。最终它们将被保留在内存中，直到浏览器关闭。 </p>
<p>闭包非常容易构成循环引用。如果一个构成闭包的函数对象被指定给，比如一个 DOM 节点的事件处理器，而对该节点的引用又被指定给函数对象作用域中的一个活动（或可变）对象，那么就存在一个循环引用。<font face="Courier"><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></font>。形成这样一个循环引用是轻而易举的，而且稍微浏览一下包含类似循环引用代码的网站（通常会出现在网站的每个页面中），就会消耗大量（甚至全部）系统内存。 </p>
<p>多加注意可以避免形成循环引用，而在无法避免时，也可以使用补偿的方法，比如使用 IE 的 onunload 事件来来清空（null）事件处理函数的引用。时刻意识到这个问题并理解闭包的工作机制是在 IE 中避免此类问题的关键。 </p>
<p id="rToc"><a href="http://www.jibbering.com/faq/faq_notes/faq_notes.html#toc">comp.lang.javascript FAQ notes T.O.C.</a></p>
<ul style="MARGIN-TOP: 2.5em; LIST-STYLE-TYPE: none">
    <li>撰稿 <span class="person">Richard Cornford</span>，2004 年 3 月 </li>
    <li>修改建议来自：<br />
    <ul style="LIST-STYLE-TYPE: none">
        <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="http://www.cn-cuckoo.com/wordpress/wp-content/uploads/2007/08/JavaScriptClosures.html#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>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/988604.html</guid>
<subject></subject>
<author>joyjiang</author>
<category></category>
<pubDate>Mon, 27 Aug 2007 14:59:03 CST </pubDate>
</item>

<item>
<title>谈谈涨价的问题</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/907346.html</link>
<description>
<![CDATA[<p>最近好像什么东西都在涨价,房价就不说了,不涨是不可能的.最近我知道的,涨价的东西有:鸡蛋,煎包,兰州拉面,陈生记(预涨期,告示已经贴出来了),方便面.还有什么涨价了,我就不是很清楚了,毕竟得到的途径有限,但是从我得到的涨价的东西可以看出:涨价的都是日常相关的--食品.这种东西的涨价说明了什么?我们每天的消费开支增加了!也就是说,我们的生活水平降低了,为什么会这样呢?涨价的原因我不是很清楚,因为我对经济不了解,这个要听相关的专家来说说了,但是我觉得有一点是肯定的,国家肯定会重视这个问题的,房价高,没有房子住?OK,我们可以租房.但是吃饭这个东西就不好说了,现在有些困难家庭已经无法承受这个涨价了.国家也是应该好好想想办法来处理一下了.和谐社会这个口号的确是很好的,但是我感觉离实现还有很大一段距离,而且比共产主义还要遥远...</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/907346.html</guid>
<subject>社会</subject>
<author>joyjiang</author>
<category>社会</category>
<pubDate>Sat, 28 Jul 2007 22:59:59 CST </pubDate>
</item>

<item>
<title>谈谈房子的问题</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/907318.html</link>
<description>
<![CDATA[<p>感觉房地产的问题现在好像都成了每天必谈的话题了,我个人认为国家即使出台相关的政策也绝对不是直接打到房产商的软肋的,这个可以从众多出台的条例可以看出,如果政府真的想打击的话,肯定是能够打击的,为什么没有那么做呢?说明两者之间存在着一定的利益关系,所以政府没有下狠手.再着,房地产本来就是国家的一个重要收入来源,如果涨幅不大的话,那么怎么体现出经济的增长呢?第三,现在已经有些通货膨胀了,物价都在上涨,所以房价&quot;适当的&quot;上涨也是不可避免的.总之,房子的价格是肯定不会下跌的,而且只会上涨,如果有人觉得对房价不满的,那这个我想是没有办法的事情,毕竟现在的社会还不是那么和谐的(所以要建设和谐社会啊),想买房子的话,还是多赚钱吧,这个才是正道,社会的中心还是经济啊!介于以上的看法,我现在准备去学习去了,目标很简单--赚钱,只是为了以后有自己有套房子.注:以上只是个人看法,如果不赞同的话可以说出自己的观点,大家可以一起探讨的.</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/907318.html</guid>
<subject>房地产</subject>
<author>joyjiang</author>
<category>房地产</category>
<pubDate>Sat, 28 Jul 2007 22:45:48 CST </pubDate>
</item>

<item>
<title>专家称房价上涨直接剥夺白领阶层的再发展能力</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/889203.html</link>
<description>
<![CDATA[房价的上涨最直接剥夺的就是白领阶层的再发展能力！昨日华南师范大学副教授唐昊做客岭南大讲坛&bull;公众论坛。他提出，这些潜在的中产阶层要把以后几十年打工的收入拿出来买房。这些收入本来可以用来再教育或者投资或者创业。所以房价透支的不仅仅是钱，而是透支这些人不断往上发展的路径，对他们的发展权是一种明目张胆的伤害。
<p><br />　　公众论坛是面向公众、服务社会，以普及社会科学知识、弘扬人文精神为宗旨的一个理论宣传和文化传播的平台，是由省委宣传部、省社会科学界联合会举办、本报承办的论坛。昨日邀请的嘉宾唐昊是华南师范大学政治与行政学院副教授、暨南大学国际关系学博士。</p>
<p><br />　　唐昊是从&ldquo;黑砖窑&rdquo;开始聊起的。他说，今天的社会讲到公平和发展，脑海里会浮现出一系列与之相反的负面事件。孙志刚事件凸显了中国社会缺乏生存权的问题，最近发生的黑砖窑事件不但是缺乏一种生存和自由权，也预示了一个群体、甚至可以说这个社会的大部分群体在发展权上被不公平对待。</p>
<p><br />　　谈到公平发展权，就不能不提现在大家都很敏感的房价问题。唐昊认为，&ldquo;房价的上涨在今天来讲，最直接剥夺的就是白领阶层的再发展能力！&rdquo;他说，尽管大家认为现在房价很高了，但如果你实在买不起的话，可能也不会觉得这个价格特别的高，或者对这个概念不敏感；正是因为我们处在能够买得起、又很心痛的这样一个区间，才会对房价这么敏感。这些人是谁呢？是中国社会这20多年以来培养起来的潜在的中产阶级，或者说白领阶层、小资。</p>
<p><br />　　&ldquo;真正的穷人，不要说房价在1万元／平方米，降到6000元／平方米、2000元／平方米也买不起。更高收入的人，则不在乎房价是1万元／平方米或者1.8万元／平方米。&rdquo;这样情况下，房价伤害的是特定一群人。这些人是整个社会发展的中坚，处在创造最大价值的年龄段。但同时他们上有4位老人，下有一个小孩，所有的担子压在他们的身上。而房价的上升，让他们把以后几十年打工的收入都要拿出来买房。这些收入本来可以用做他们自己再教育，或者读一个新的学位，或者投资，或者参与小型的企业创业。&ldquo;房价透支的不仅仅是钱，而是透支这些人不断往上发展的路径，对这些人的发展权是一种明目张胆的伤害。&rdquo; (田霜月、张丹)</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/889203.html</guid>
<subject></subject>
<author>joyjiang</author>
<category></category>
<pubDate>Mon, 23 Jul 2007 08:56:53 CST </pubDate>
</item>

<item>
<title>又加息了</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/884303.html</link>
<description>
<![CDATA[<p>工资涨了,房价涨了,房价涨幅比工资的涨幅真的要多很多啊</p>
<p>贫富差距进一步拉大,资本主义征兆开始显现</p>
<p>现在总是感觉美国的资本主义更像社会主义,而中国的社会主义却更像资本主义</p>
<p>想不明白</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/884303.html</guid>
<subject></subject>
<author>joyjiang</author>
<category></category>
<pubDate>Fri, 20 Jul 2007 19:49:03 CST </pubDate>
</item>

<item>
<title>中国队怎么越打越差了?</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/879540.html</link>
<description>
<![CDATA[<p>昨天晚上看了中国队对阵乌兹别克斯坦的比赛,真是太离谱了,竟然是0-3!</p>
<p>如果中国能够像对伊朗一样,打半场的好球,即使输了也好想一点的,真是失望!</p>
<p>试看我们的球队,其实我们不缺乏人才,为什么会打成这样呢?看来管理层应该有不可推卸的责任</p>
<p>比赛中,我发现中国队有个严重的问题,一旦丢了球,球队就乱了.本来最后还有15分钟的机会可能把比赛扳平的,然而在丢球之后,队员们就开始乱踢,完全没有了章法,不会去想着要控制比赛的节奏,都是一味的为了追赶,为了进球,这样急躁的心态还怎么可能扳平比赛呢.看来心理素质也是中国队的一个严重问题!</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/879540.html</guid>
<subject>日志</subject>
<author>joyjiang</author>
<category>日志</category>
<pubDate>Thu, 19 Jul 2007 09:45:12 CST </pubDate>
</item>

<item>
<title>Flex+Ruby</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/876924.html</link>
<description>
<![CDATA[<p>这样的搭配是否真的可行?</p>
<p>现在ruby对企业级开发的支持是否都已经满足?分布式的支持?</p>
<p>flex的表现层应该很强大,但是页面的自动调整能力是否和javascript可比?</p>
<p>和后台的交互应该问题不大的,因为都是xml的数据,但是也会涉及到效率的问题.</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/876924.html</guid>
<subject></subject>
<author>joyjiang</author>
<category></category>
<pubDate>Wed, 18 Jul 2007 11:06:50 CST </pubDate>
</item>

<item>
<title>选择</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/872934.html</link>
<description>
<![CDATA[<p>是做技术还是做业务?</p>
<p>这是个问题</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/872934.html</guid>
<subject>思想</subject>
<author>joyjiang</author>
<category>思想</category>
<pubDate>Mon, 16 Jul 2007 17:24:12 CST </pubDate>
</item>

<item>
<title>RESTful Rails Development （未完成）</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/748316.html</link>
<description>
<![CDATA[<div class="item_content">
<p>HTTP除了GET和POST还能做很多，事实上过去的日子里好多Web开发者已经忘记了这一点。但是如果你考虑到浏览器只支持GET和POST请求的话，这就不让人惊奇了。</p>
<p>GET和POST是客户端向服务器端发送HTTP请求时的方法，HTTP也知道PUT和DELETE方法来创建和删除服务器上的资源。</p>
<p>我们的目标是让开发者在开发的时候能更多的考虑HTTP的PUT和DELETE方法。我们把使用PUT和DELETE以及GET和POST开发的方法并称为REST。Rails1.2支持这种技术。</p>
<p>我们先简单的介绍一下REST概念和背景。使用脚手架，生成一个控制器和模型帮助我们进行RESTful开发。在这个技术的基础上，下一章展示了REST工作所需要的路由的配置和调整。本章给了读者一些在开发REST过程中的建议并解释了资源如何在不违反REST-URLs原则的情况下嵌套在父子关系中。指南的最后是关于REST和AJAX，RESTful应用的测试和客户端部分如何使用REST的<em>ActiveResource</em>的介绍。</p>
<p><strong>但在我们开始之前：你至少得有一点Rails的基础知识。</strong></p>
<p>我们先来建立一个Rails应用：</p>
<pre><code>rails ontrack
</code></pre>
<p>接下来用你熟悉的办法创建数据库并建立库project。如果你不是Rails1.2以上版本，那么请先升级你的Rails。</p>
<p>使用脚手架可以很容易开始REST的编程之旅，它可以接受项目资源名称和模型属性的对应类型。这些属性是为了产生数据库migration脚本，也给视图创建合适的域。注意下面这句命令在scaffold和resource之间的_可不是笔误，而是必须要写的。</p>
<pre><code>ruby script/generate scaffold_resource project name:string desc:text
</code></pre>
<p>我们不仅仅生成了model，controller，views也生成了一个<em>migration</em>脚本和<em>map.resources :projects</em>在<em>config/routes.rb</em>中。</p>
<p>REST是由Rails中的controller和model组成的。模型是一个普通的<em>ActiveRecord</em>类，继承自<em>ActiveRecord::Base</em>。</p>
<pre><code>class Project &lt; ActiveRecord::Base
end
</code></pre>
<p>没什么新东西，但是不要忘了生成数据库的表。</p>
<pre><code>rake db:migrate
</code></pre>
<p>生成的<em>ProjectsController</em>是一个用来操作资源的<em>CRUD-Controller</em>。这意味这控制器为CRUD操作的每一个动作指定了精确的资源类型：</p>
<p>app/controllers/projects controller.rb</p>
<pre><code>class ProjectsController &lt; ApplicationController
  # GET /projects
  # GET /projects.xml
  def index...
  # GET /projects/1
  # GET /projects/1.xml
  def show...
  # GET /projects/new
  def new...
  # GET /projects/1;edit
  def edit...
  # POST /projects
  # POST /projects.xml
  def create...
  # PUT /projects/1
  # PUT /projects/1.xml
  def update...
  end
  # DELETE /projects/1
  # DELETE /projects/1.xml
  def destroy...
end
</code></pre>
<p>这里你并没有看到什么新的代码，有4个动作分别是创造, 检索, 更新和删除项目。他们都用注解标明了他们请求Url 所采用的HTTP 动作。这些就是REST-URL&rsquo;s，我们接下来就重点了解下这里。</p>
<p>REST-URLs和我们习惯了的Rails传统方式：控制器，动作名称，模型id不同，（例如：/projects/show/1）。他们只包括控制器名字被资源的id：</p>
<pre><code>/projects/1
</code></pre>
<p>因为没有动作名称了，访问者也不知道对资源进行了什么操作。但我们怎么知道是该列出还是应该删除资源呢？答案是根据请求URL的HTTP方法来判断。下表列出了四个HTTP动词对应的REST-URLs和每个请求所对应的动作：</p>
<p><img title="HTTP动作对应的REST-URLs" alt="HTTP动作对应的REST-URLs" src="http://www.176so.com/images/blog/REST01.gif" /></p>
<p>除了POST动作之外所有的URL都是相同的，因为在创建一个新的记录的时候是不需要id的。仅由HTTP动作来决定哪个actin动作会发生，URL也符合了DRY原则，地址资源代替了动作。在浏览器的地址栏总是显示http://localhost:3000/projects/1。</p>
<p>浏览器不支持PUT 和 DELETE。Rails提供一个特别的辅助方法来创建删除的链接：用POST请求向服务器发送一个隐藏域来标明DELETE。PUT请求也用同样的方法。</p>
<p>我们看到REST动作由REST-URL和HTTP动作组合而成。一个REST动作针对不同的客户端类型采用不同的响应格式。典型的客户端类型是浏览器用户，而web service客户希望服务器用XML和RSS来应答。</p>
<p>客户端使用<em>respond_to</em>格式化请求，已经由脚手架在CRUD动作中生成了。下面的代码以show动作为例，展示了如何使用<em>respond_to</em>:</p>
<p>app/controllers/projects controller.rb</p>
<pre><code># GET /projects/1
# GET /projects/1.xml
def show
  @project = Project.find(params[:id])
    respond_to do |format|
    format.html # show.rhtml
    format.xml { render :xml =&gt; @project.to_xml }
  end
end
</code></pre>
<p><em>respond_to</em>随后的代码块根据具体命令来格式化。例子中的代代码块处理两个格式：HTML和XML。依据客户端的应答模式，来执行随后的代码。在HTML情况下, 发送默认的show.rhtml视图。如果响应是XML, 被请求的资源将被转换成XML 和然后将被返回客户端。</p>
<p><em>respond_to</em>可接受两个不同的方法：<em>respond_to</em>分析接受数据的<em>HTTP-Header</em>的请求格式或者请求格式被附加在URL中。</p>
<p>第一个方法在<em>HTTP-Header</em>中接受HTTP动作。利用curl这个HTTP命令行工具非常容易设置<em>HTTP-Header</em>。但是在这之前我们先启动Web server：</p>
<pre><code>&gt; ruby script/server webrick
=&gt; Booting WEBrick...
=&gt; Rails application started on http://0.0.0.0:3000
=&gt; Ctrl-C to shutdown server; call with &mdash;help for options
[2006-12-30 18:10:50] INFO WEBrick 1.3.1
[2006-12-30 18:10:50] INFO ruby 1.8.4 (2005-12-24) [i686-darwin8.6.1]
[2006-12-30 18:10:50] INFO WEBrick::HTTPServer#start: pid=4709 port=3000
</code></pre>
<p>然后我们用浏览器浏览一个项目。</p>
<p><img title="效果" alt="效果" src="http://www.176so.com/images/blog/REST02.gif" /></p>
<p>curl这个命令用XML格式请求了id是1的项目资源：</p>
<pre><code>&gt; curl -H &quot;Accept: application/xml&quot; -i -X GET http://localhost:3000/projects/1
=&gt;
HTTP/1.1 200 OK
Connection: close
Date: Sat, 30 Dec 2006 17:31:50 GMT
Set-Cookie: _session_id=4545eabd9d1bebde367ecbadf015bcc2; path=/
Status: 200 OK
Cache-Control: no-cache
Server: Mongrel 0.3.13.4
Content-Type: application/xml; charset=utf-8
Content-Length: 160
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project&gt;
  &lt;desc&gt;Future of Online Marketing&lt;/desc&gt;
  &lt;id type=&quot;integer&quot;&gt;1&lt;/id&gt;
  &lt;name&gt;Wunderloop&lt;/name&gt;
&lt;/project&gt;
</code></pre>
<p>Rails的路由为这个动作调用了show这个动作。因为我们的XML希望在<em>HTTP-Accept-Field</em>方法中执行format.xml代码块。被请求资源转换成XML格式来应答。</p>
<p>Curl不仅能测试不同的<em>response-formats</em>，也能发送浏览器不支持的HTTP方法。下面的代码请求删除id是1的资源：</p>
<pre><code>&gt; curl -X DELETE http://localhost:3000/projects/1
=&gt;
&lt;html&gt;&lt;body&gt;You are being
&lt;a href=&quot;http://localhost:3000/projects&quot;&gt;redirected&lt;/a&gt;.
&lt;/body&gt;&lt;/html&gt;
</code></pre>
<p>这次请求使用HTTP的DELETE方法。Rails根据HTTP动作调用了<em>ProjectsControllers</em>的destroy动作。注意，两次请求的URL都是相同的。唯一的区别是HTTP 动作。</p>
<p>第二个方法通过在URL中附加请求格式。如果刚才你没删除id是1的数据的话，你可以直接在浏览器中请求XML数据：</p>
<pre><code>http://localhost:3000/projects/1.xml
</code></pre>
<p>到目前为止我们初步了解了REST是怎么工作的。</p>
<p>&nbsp;</p>
<div class="item_content">
<p>Views用来连接应用程序和用户。用户通过网站界面的超链接和按钮与应用程序进行交互。传统上Rails开发者使用<em>link_to</em>方法来生成超链接，这个方法可以接受由controller，action等其它参数组成的一个hash：</p>
<pre><code>link_to :controller =&gt; &quot;projects&quot;, :action =&gt; &quot;show&quot;, :id =&gt; project
=&gt;
&lt;a href=&quot;/projects/show/1&quot;&gt;Show&lt;/a&gt;
</code></pre>
<p>传统的<em>link_to</em>方法在我们的REST体系中无法很好的工作，因为REST不喜欢在URL中包含actions。现在最重要的就是在超链接和按钮的URL中提供HTTP动作。</p>
<p>Rails早就想到了这一点，我们仍然用<em>link_to</em>方法来生成链接。但是，使用Path这个方法来替换原来hash。<em>link_to</em>方法利用Path来给超链接的href属性赋值。像刚才那个例子，我们给<em>ProjectsController</em>创建了一个链接到show action的超链接，我们用<em>project_path(:id)</em>来代替controller，action和project-id：</p>
<pre><code>link_to &quot;Show&quot;, project_path(project)
=&gt;
&lt;a href=&quot;/projects/1&quot;&gt;Show&lt;/a&gt;
</code></pre>
<p>传统的<em>link_to</em>方法的href属性包括控制器controller和动作action。而我们使用<em>project_path</em>创建的HTML标签仅包含controller和project_id，因此它是一个典型的<em>REST-URL</em>。因为超链接默认提交的是GET请求，Rails会根据这个HTTP动作判断出应该调用show action。下图列出了Rails中七个标准的path方法。</p>
<p><img title="Standard Path-Method" alt="Standard Path-Method" src="http://www.176so.com/images/blog/REST03.gif" /></p>
<p>每一个path方法都包含一个HTTP动作，这意味着在点击超链接和按钮的时候HTTP动作也一并发送到了服务器端。有一些请求(show，update)在默认的情况下就可以利用正确的HTTP动作来传送(GET，POST)。但由于浏览器不支持PUT和DELETE动作，所以其它的请求(create，delete)只能利用一些特殊的方式(比如使用隐藏的域)来提交。</p>
<p><strong>New and Edit</strong></p>
<p>点击New传递给服务器端的动作是GET。下面的例子展示了，利用path生成的超链接new是由<em>ProjectsController</em>和<em>new</em>这个action组成的。</p>
<pre><code>link_to &quot;New&quot;, new_project_path
=&gt;
&lt;a href=&quot;/projects/new&quot;&gt;New&lt;/a&gt;
</code></pre>
<p>它和我们的REST观点有什么违背的地方么？可能第一眼看来的确是不同。但是如果仔细看看，<em>new</em>并不是一个REST/CRUD action。<em>new</em>只是一个准备性的action，当<em>new</em>的表单被提交的时候真正的CRUD action <em>create</em>才被执行。我们不需要传递id因为这里还没有一条记录。没有id的URL并不是一个REST-URL，因为REST-URLs总是有一个明显的资源地址(通常情况就是后面跟一个id)。所以这个<em>new</em> action 只是为了显示<em>new</em>这个表单。</p>
<p>同样，对于<em>edit_project_path</em>来说：虽然它提到了资源id，但是它也只是为了<em>update</em>这个真实的CRUD action做准备而已。只有当<em>edit</em>的表单被提交的时候，真正的CRUD action才发生。<em>edit_project_path</em>和<em>new_project_path</em>之间的区别是前者需要被操作的资源id。常规的REST是id在控制器之后：/projects/1。如果只向服务器提交GET动作，那么routed会转到<em>show</em> action。为了防止发生这种情况，<em>edit_project_path</em>简单的扩充了生成的href属性，下面是最后生成的html：</p>
<pre><code>link_to &quot;Edit&quot;, edit_project_path(project)
=&gt;
&lt;a href=&quot;/projects/1;edit&quot;&gt;Edit&lt;/a&gt;
</code></pre>
<p>上面我们了解了<em>new</em>和<em>edit</em>所生成的URL，他们都不是真正的REST/CRUD actions。</p>
<p><strong>Path-Methods in forms: Create and Update</strong></p>
<p>传统的表单由<em>form_tag</em>和<em>form_for</em>生成：</p>
<pre><code>&lt;% form_for :project, @project, :url =&gt; { :action =&gt; &quot;create&quot; } do |f| %&gt;
...
&lt;% end %&gt;
</code></pre>
<p>在典型的REST应用中，<em>:url-hash</em>被path方法替代：</p>
<pre><code>project_path for the new form and
project_path(:id) for the edit form
</code></pre>
<p><strong>The New Form</strong></p>
<p>表单使用标准的POST方法向服务器提交数据。<em>project_path</em>请求的路径 /projects 中不包含id，被请求的<em>create</em> action使用POST方法。</p>
<pre><code>form_for(:project, :url =&gt; projects_path) do |f| ...
=&gt;
&lt;form action=&quot;/projects&quot; method=&quot;post&quot;&gt;
</code></pre>
<p><strong>The Edit Form</strong></p>
<p>在REST思想中，<em>update</em>使用PUT动作。但浏览器不理解PUT和DELETE动作。Rails的解决方法是在form for中增加了一个:html-hash的键。</p>
<pre><code>form_for(:project, :url =&gt; project_path(@project), :html =&gt; { :method =&gt; :put }) do |f| ...
=&gt;
&lt;form action=&quot;/projects/1&quot; method=&quot;post&quot;&gt;
&lt;div style=&quot;margin:0;padding:0&quot;&gt;
  &lt;input name=&quot;_method&quot; type=&quot;hidden&quot; value=&quot;put&quot; /&gt;
&lt;/div&gt;
</code></pre>
<p>Rails提交了一个隐藏的域包含了PUT这个动作。Rails根据这个域的值调用<em>update</em>这个action。</p>
<p><strong>Destroy</strong></p>
<p>注意<em>showing</em>和<em>deleting</em>一条记录都使用project_path：</p>
<pre><code>link_to &quot;Show&quot;, project_path(project)
link_to &quot;Destroy&quot;, project_path(project), :method =&gt; :delete
</code></pre>
<p>唯一不同的是在删除的超链接里增加了一个:method参数，指定HTTP动作（这里是 :delete）。因为浏览器不支持DELETE，当你点击链接的时候Rails生成了一段JavaScript代码：</p>
<pre><code>link_to &quot;Destroy&quot;, project_path(project), :method =&gt; :delete
=&gt;
&lt;a href=&quot;/projects/1&quot;
  onclick=&quot;var f = document.createElement(&rsquo;form&rsquo;);
  f.style.display = &rsquo;none&rsquo;;
  this.parentNode.appendChild(f);
  f.method = &rsquo;POST&rsquo;; f.action = this.href;
  var m = document.createElement(&rsquo;input&rsquo;);
  m.setAttribute(&rsquo;type&rsquo;, &rsquo;hidden&rsquo;);
  m.setAttribute(&rsquo;name&rsquo;, &rsquo;_method&rsquo;);
  m.setAttribute(&rsquo;value&rsquo;, &rsquo;delete&rsquo;);
  f.appendChild(m);f.submit();
  return false;&quot;&gt;Destroy&lt;/a&gt;
</code></pre>
<p>这段代码创建了一个表单以保证服务器端在其隐藏域中获得期望的HTTP动作。这一次Rails根据域的值调用了<em>destroy</em>这个action。</p>
<p><strong>URL-Methods in the Controller</strong></p>
<p>REST中views的超链接和提交后的action都需要使用一个新方法来做跳转。Rails使用URL方法，给每一个path方法生成一个相反的方法。</p>
<pre><code>project_url for project_path
</code></pre>
<p>或者</p>
<pre><code>projects_url for projects_path
</code></pre>
<p>和path方法不同的是，URL方法生成了一个完整的URLs包括protocol，host，port 和 path：</p>
<pre><code>project_url(1)
=&gt;
&quot;http://localhost:3000/projects/1&quot;
projects_url
=&gt;
&quot;http://localhost:3000/projects&quot;
</code></pre>
<p>在REST应用的controllers中，当需要使用<em>redirect_to</em>方法时我们都用URL方法来替代，过去的<em>redirect_to</em>方法类似controller/action/parameter hash</p>
<pre><code>redirect_to :controller =&gt; &quot;projects&quot;, :action =&gt; &quot;show&quot;, :id =&gt; @project.id
</code></pre>
<p>而在REST应用中：</p>
<pre><code>redirect_to project_url(@project)
</code></pre>
<p>你可以在<em>destroy</em>这个action中看到如下这个代码，<em>projects_url</em>没有跟任何参数，它的意思是在对象删除后，跳转到项目的<em>list</em> action。</p>
<p>app/controllers/projects controller.rb</p>
<pre><code>def destroy
  @project = Project.find(params[:id])
  @project.destroy

  respond_to do |format|
    format.html { redirect_to projects_url }
    format.xml { head :ok }
  end
end
</code></pre>
<pre><code></code></pre>
<pre><code><div class="item_content">
<p>到目前为止我们解释了REST概念和使用links，forms和controllers的新方法。但我们未解释这些方法是来自何处。把请求转发到对应的方法是由路由文件<em>config/routes.rb</em>中的一句话来负责：</p><pre><code>map.resources :projects
</code></pre>
<p>脚手架已经帮我们创建这行代码了。路由请求后面需要跟着的是<em>ProjectsController</em>的一个REST actions:</p>
<pre><code>map.resources :projects
=&gt;
Route Generated Helper
&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-
projects projects_url, projects_path
project project_url(id), project_path(id)
new_project new_project_url, new_project_path
edit_project edit_project_url(id), edit_project_path(id)
</code></pre>
<h6>约定</h6>
<p>在编写REST routes的时候需要注意的一点是要按照naming-conventions的方法为CRUD
actions命名。下面的<em>link_to</em>将会生成如下HTML代码：</p>
<pre><code>link_to &quot;Show&quot;, project_path(project)
=&gt;
&lt;a href=&quot;/projects/1&quot;&gt;Show&lt;/a&gt;
</code></pre>
<p><em>link_to</em>方法和所产生的HTML都不包括所要调用的action的名字。如果请求动作是GET，那么Rails知道/projects/:id
应该被映射到<em>ProjectsController</em>的show
action。所以controller必须有一个action的名字是show。类似的index，update，create，destroy，new，edit都必须存在以便REST
controller执行它们所对应的方法。</p>
<h6>定制</h6>
<p>利用下面的选项，REST routes可以适应有特殊用途的要求：</p>
<pre><code>:controller. Specifies the controller to use.
:path_prefix. Names the URL-prefix including needed variables.
:name_prefix. Names the prefix of the created route helpers.
:singular. To name the singular name to be used for the member route.
</code></pre>
<p>下面的路由条目为Sprint创建了一个新路由。</p>
<pre><code>map.resources :sprints,
  :controller =&gt; &quot;ontrack&quot;,
  :path_prefix =&gt; &quot;/ontrack/:project_id&quot;,
  :name_prefix =&gt; &quot;ontrack_&quot;
</code></pre>
<p>path_prefix选项的意思是使用什么格式的URL。每一个URL都是由<em>/ontrack</em>开始，后面紧跟project
id。负责处理它的controller是<em>OntrackController</em>。下面是URL：</p>
<pre><code>http://localhost:3000/ontrack/1/sprints
</code></pre>
<p>根据路由规则获得路由转发到Ontrack-Controller的index action 另一个URL的例子：</p>
<pre><code>http://localhost:3000/ontrack/1/sprints/1
</code></pre>
<p>路由会将上面的URL转发到OntrackController的show。
<em>path_prefix</em>定义URL的格式，<em>name_prefix</em>保证生成的所有辅助方法都以ontrack开始：</p>
<pre><code>ontrack_sprints_path(1)
=&gt;
/ontrack/1/sprints
</code></pre>
<p>或</p>
<pre><code>ontrack_edit_sprint_path(1, 1)
=&gt;
/ontrack/1/sprints/1;edit
</code></pre>
<pre><code></code></pre>
<pre><code><div class="item_content">
<p>RESTful 开发真正有趣的地方是当你使用Nested 
resources（嵌套资源）的时候。这里干净的urls变的更清楚。嵌套资源进一步的阐述了REST使我们更好的理解范例。嵌套资源意思是把资源结合成父子关系。举例来说就是Rails的模型，Projects和Iterations是一对多关系。嵌套的REST控制器仍然负责操作单一的模型类型，但子控制器也读取父模型。听起来很复杂，并且晕头转向吧？虽然我这段话翻译的很差（初中英文水准），但下面的这一章会让你更清晰。</p>
<p>我们先从生成Iteration数据库和一个新的资源开始：</p><pre><code>&gt; ruby script/generate scaffold_resource iteration name:string start:date end:date project_id:integer
&gt; rake db:migrate
</code></pre>
<p>然后在模型类中定义Projects和iterations的一对多关系</p>
<pre><code>Listing 1.4: ontrack/app/models/project.rb
  class Project &lt; ActiveRecord::Base
    has_many :iterations
  end
Listing 1.5: ontrack/app/models/iteration.rb
  class Iteration &lt; ActiveRecord::Base
    belongs_to :project
  end
</code></pre>
<p>除了model, controller 和 views之外，generator也在config/routes.rb中增加了一条新的路由。</p>
<pre><code>map.resources :iterations
</code></pre>
<p>生成器生了类似projects的路由和辅助方法。但如new_iteration_path方法 生成的 /iterations/new
，并无法标明iteration属于哪个project。</p>
<p>REST-Rail试图映射子资源的控制器和URLs。做到这一点，你必须修改config/routes.rb中的</p>
<pre><code>map.resources :iterations</code></pre>
<p>替换为</p>
<pre><code>map.resources :projects do |projects|
  projects.resources :iterations
end
</code></pre>
<p>这条映射规则，使得Iteration进入嵌套资源，并生成了一串新的路由规则。这样你可以操作属于project的iterations。生成的路由有下列格式：</p>
<pre><code>/project/:project_id/iterations
/project/:project_id/iterations/:id
</code></pre>
<p>例如下面这条url</p>
<pre><code>http://localhost:3000/projects/1/iterations</code></pre>
<p>通过传递的 :project id 参数得到该project对象下的IterationsController的index
action。注意URL字符，它提醒我们下面的ActiveRecord关联和映射：</p>
<pre><code>/projects/1/iterations &lt;=&gt; Project.find(1).iterations</code></pre>
<p>嵌套的REST-URLs仍然是干净的REST-URLs，地址中没有包含actions。事实上嵌套资源是把两个REST-URLs汇聚成一个URL。</p>
<pre><code>http://localhost:3000/projects/1/iterations/1</code></pre>
</div>
</code></pre>
</div>
</code></pre>
</div>
</div>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/748316.html</guid>
<subject></subject>
<author>joyjiang</author>
<category></category>
<pubDate>Mon, 21 May 2007 23:23:53 CST </pubDate>
</item>

</channel>
</rss>