<?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/"
	>

<channel>
	<title>影の域 &#187; JavaScript</title>
	<atom:link href="http://www.zfkun.com/blog/index.php/archives/category/code/javascript/feed" rel="self" type="application/rss+xml" />
	<link>http://www.zfkun.com/blog</link>
	<description>时光真的如水...</description>
	<lastBuildDate>Thu, 29 Apr 2010 06:43:15 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>JavaScript的valueOf和toString</title>
		<link>http://www.zfkun.com/blog/index.php/archives/140</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/140#comments</comments>
		<pubDate>Thu, 22 Apr 2010 16:01:25 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[toString]]></category>
		<category><![CDATA[valueOf]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=140</guid>
		<description><![CDATA[JavaScript中valueOf函数方法是返回指定对象的原始值。JavaScript中toString函数方法是返回对象的字符串表示]]></description>
			<content:encoded><![CDATA[<p>JavaScript中valueOf函数方法是返回指定对象的原始值。</p>
<p> 对象 返回值<br />
Array 数组的元素被转换为字符串，这些字符串由逗号分隔，连接在一起。其操作与 Array.toString 和 Array.join 方法相同。<br />
Boolean Boolean 值。<br />
Date 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。<br />
Function 函数本身。<br />
Number 数字值。<br />
Object 对象本身。这是默认情况。<br />
String 字符串值。</p>
<p>JavaScript中toString函数方法是返回对象的字符串表示<br />
对象 操作<br />
Array 将 Array 的元素转换为字符串。结果字符串由逗号分隔，且连接起来。<br />
Boolean 如果 Boolean 值是 true，则返回 “true”。否则，返回 “false”。<br />
Date 返回日期的文字表示法。<br />
Error 返回一个包含相关错误消息的字符串。<br />
Function 返回如下格式的字符串，其中 functionname 是被调用 toString 方法函数的名称： function functionname( ) { [native code] }<br />
Number 返回数字的文字表示。<br />
String 返回 String 对象的值。<br />
默认 返回 “[object objectname]”，其中 objectname 是对象类型的名称。</p>
<p>假设在古代，一两黄金换20两白银，一两白银换25枚铜钱，构建描述货币的类:</p>
<pre name="code" class="js">
function Money(gold, silver, coin)
{
 this.gold = gold;
 this.silver = silver;
 this.coin = coin;
}

Money.parse = function(value)
{
 var coin = parseInt(value % 25);
 var silver = parseInt(value / 25 % 20);
 var gold = parseInt(value / 500);
 return new Money(gold, silver, coin);
}

Money.prototype.valueOf = function()
{
 return ((this.gold * 20) + this.silver) * 25 + this.coin;
}

Money.prototype.toString = function()
{
 return this.gold + "两黄金，" + this.silver + "两白银，" + this.coin + "文钱";
}

var money1 = new Money(5, 6, 1);
var money2 = new Money(2, 4, 6);

var money3 = Money.parse(money1 + money2);
alert(money3);
</pre>
<p>这里隐式调用了valueOf跟toString，二者并存的情况下，在数值运算中，优先调用了valueOf，字符串运算中，优先调用了toString。比如<br />
money1 + money2，调用的就是两者valueOf之后的值相加，而alert的时候，把money3先toString了一下。</p>
<p>这个例子其实是模仿js内置对象Date的，Date基本上也是这样处理问题的。</p>
<pre name="code" class="js">
function Money(n)
{
this.n = n;
}
Money.prototype.valueOf = function()
{
 return this.n * 2
}
Money.prototype.toString = function()
{
 return this.n * 3
}
var m1 = new Money(1);
var m2 = new Money(2);
m = m1 + m2
alert(m1);
alert(m2);
alert(m);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/140/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PureMVC Standard for JavaScript</title>
		<link>http://www.zfkun.com/blog/index.php/archives/127</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/127#comments</comments>
		<pubDate>Mon, 08 Mar 2010 03:55:06 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[puremvc]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=127</guid>
		<description><![CDATA[PureMVC is a lightweight framework for creating applications based upon the classic  Model-View-Controller design meta-pattern. This is the specific implementation for the JavaScript language.]]></description>
			<content:encoded><![CDATA[<p><a title="PureMVC_JS" href="http://trac.puremvc.org/PureMVC_JS/" target="_blank">PureMVC</a> is a lightweight framework for creating applications based upon the classic  <a title="MVC" href="http://en.wikipedia.org/wiki/Model-view-controller" target="_blank">Model-View-Controller</a> design meta-pattern. This is the specific implementation for the JavaScript language.</p>
<h2 id="Note">Home</h2>
<p><a href="http://trac.puremvc.org/PureMVC_JS/">http://trac.puremvc.org/PureMVC_JS/</a></p>
<h2>Note</h2>
<p>Unlike the prior version which used  Objs, the current version uses  MooTools for simulation of class relationships.</p>
<h2 id="Status">Platforms</h2>
<ul>
<li>JavaScript 1.5</li>
<li><a title="Mootools" href="http://mootools.net" target="_blank">Mootools core</a></li>
</ul>
<h2 id="License">License</h2>
<p>Your reuse is governed by the  Creative Commons Attribution 3.0 license.</p>
<p>This implementation, like all other official PureMVC implementations, demos and utilities, is open-source and free to use in personal or commercial applications. If you include the source of PureMVC (modified or not), in another work (open-source or not), you must simply leave in the existing attribution and license information in the included source code.</p>
<h2 id="CurrentVersion">Current Version</h2>
<ul>
<li>2.0</li>
</ul>
<h2 id="SubversionRepository">Subversion Repository</h2>
<ul>
<li><strong>Location:</strong> <a href="http://svn.puremvc.org/PureMVC_JS" target="_blank">http://svn.puremvc.org/PureMVC_JS</a></li>
<li><strong>Latest Release:</strong> tags/2.0</li>
</ul>
<h2 id="Downloads">Downloads</h2>
<ul>
<li> <a href="http://puremvc.org/pages/downloads/JS/PureMVC_JS.zip" target="_blank">PureMVC Framework for JavaScript </a>(.zip archive)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/127/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>contains vs compareDocumentPosition</title>
		<link>http://www.zfkun.com/blog/index.php/archives/125</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/125#comments</comments>
		<pubDate>Sun, 07 Mar 2010 11:04:34 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[compareDocumentPosition]]></category>
		<category><![CDATA[contains]]></category>
		<category><![CDATA[dom]]></category>
		<category><![CDATA[js]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=125</guid>
		<description><![CDATA[一个很棒的 blog 文章，是 PPK 两年前写的，文章中解释了 contains() 和 compareDocumentPosition() 方法运行在他们各自的浏览器上。从那起，我已经对这些方法做了大量的研究，并且已经在很多场合使用他们。在很多任务中，他们被证明是非常有用的（特别关于结构的抽象 DOM 选择器）]]></description>
			<content:encoded><![CDATA[<p>    一个很棒的 blog 文章，是 PPK 两年前写的，文章中解释了 contains() 和 compareDocumentPosition() 方法运行在他们各自的浏览器上。从那起，我已经对这些方法做了大量的研究，并且已经在很多场合使用他们。在很多任务中，他们被证明是非常有用的（特别关于结构的抽象 DOM 选择器）。</p>
<p>    1、DOMElement.contains(DOMNode)</p>
<p>    这个方法起先用在 IE ，用来确定 DOM Node 是否包含在另一个 DOM Element 中。</p>
<p>    当尝试优化 CSS 选择器遍历（像：“#id1 #id2”），这个方法很有用。你可以通过 getElementById 得到元素，然后使用 .contains() 确定 #id1 实际上是否包含 #id2。</p>
<p>    注意点：如果 DOM Node 和 DOM Element 相一致，.contains() 将返回 true ，虽然，一个元素不能包含自己。</p>
<p>    这里有一个简单的执行包装，可以运行在：Internet Explorer, Firefox, Opera, and Safari。</p>
<table border="1" cellspacing="1" cellpadding="1" width="80%" align="center">
<tbody>
<tr>
<td bgcolor="#c0c0c0"> function contains(a, b) {<br />
 return a.contains ? a != b &amp;&amp; a.contains(b) : !!(a.compareDocumentPosition(arg) &amp; 16);<br />
}</td>
</tr>
</tbody>
</table>
<p>    2、NodeA.compareDocumentPosition(NodeB)</p>
<p>    这个方法是 DOM Level 3 specification 的一部分，允许你确定 2 个 DOM Node 之间的相互位置。这个方法比 .contains() 强大。这个方法的一个可能应用是排序 DOM Node 成一个详细精确的顺序。</p>
<p>    使用这个方法你可以确定关于一个元素位置的一连串的信息。所有的这些信息将返回一个比特码（Bit，比特，亦称二进制位）。</p>
<p>    对于那些，人们知之甚少。比特码是将多重数据存储为一个简单的数字（译者注：0 或 1）。你最终打开 / 关闭个别数目（译者注：打开/关闭对应 0 /1），将给你一个最终的结果。</p>
<p>    这里是从 NodeA.compareDocumentPosition(NodeB) 返回的结果，包含你可以得到的信息。</p>
<table border="1" cellspacing="1" cellpadding="1" width="80%" align="center">
<tbody>
<tr>
<td bgcolor="#c0c0c0"> Bits          Number        Meaning<br />
000000         0              元素一致<br />
000001         1              节点在不同的文档（或者一个在文档之外）<br />
000010         2              节点 B 在节点 A 之前<br />
000100         4              节点 A 在节点 B 之前<br />
001000         8              节点 B 包含节点 A<br />
010000         16             节点 A 包含节点 B<br />
100000         32             浏览器的私有使用</td>
</tr>
</tbody>
</table>
<p>    现在，这意味着一个可能的结果类似于：</p>
<table border="1" cellspacing="1" cellpadding="1" width="80%" align="center">
<tbody>
<tr>
<td bgcolor="#c0c0c0"> &lt;div id=&#8221;a&#8221;&gt;<br />
 &lt;div id=&#8221;b&#8221;&gt;&lt;/div&gt;<br />
&lt;/div&gt;<br />
&lt;script&gt;<br />
 alert( document.getElementById(&#8220;a&#8221;).compareDocumentPosition(document.getElementById(&#8220;b&#8221;)) == 20);<br />
&lt;/script&gt;</td>
</tr>
</tbody>
</table>
<p>    一旦一个节点 A 包含另一个节点 B，包含 B（+16） 且在 B 之前（+4），则最后的结果是数字 20 。如果你查看比特发生的变化，将增加你的理解。</p>
<p>000100 (4) + 010000 (16) = 010100 (20)</p>
<p>    这个，毫无疑问，有助于理解单个最混乱的 DOM API 方法。当然，他的价值当之无愧的。</p>
<p>    现在，DOMNode.compareDocumentPosition 在 Firefox 和 Opera 中是可用的。然而，有一些技巧，我们可以用来在 IE 中执行他。</p>
<table border="1" cellspacing="1" cellpadding="1" width="80%" align="center">
<tbody>
<tr>
<td bgcolor="#c0c0c0"> // Compare Position &#8211; MIT Licensed, John Resig<br />
function comparePosition(a, b){<br />
 return a.compareDocumentPosition ?<br />
 a.compareDocumentPosition(b) :<br />
 a.contains ?<br />
  ( a != b &amp;&amp; a.contains(b) &amp;&amp; 16 ) +<br />
  ( a != b &amp;&amp; b.contains(a) &amp;&amp; 8 ) +<br />
  ( a.sourceIndex &gt;= 0 &amp;&amp; b.sourceIndex &gt;= 0 ?<br />
   (a.sourceIndex &lt; b.sourceIndex &amp;&amp; 4 ) +<br />
   (a.sourceIndex &gt; b.sourceIndex &amp;&amp; 2 ) :<br />
   1 ) :<br />
  0;<br />
}</td>
</tr>
</tbody>
</table>
<p>    IE 提供给我们一些可以使用的方法和属性。开始，使用 .contains() 方法（如我们前面所讨论的），以便给我们包含（+16）或者被包含（+8）的结果。IE 还有一个 .sourceIndex 属性在所有的 DOM Element 对应着元素在文档中的位置，例如：document.documentElement.sourceIndex == 0。因为我们有这个信息，我们可以完成两个 compareDocumentPosition 难题：在前面（+2）和在后面（+4）。另外，如果一个元素不在当前的文档，.sourceIndex 将等于 -1，这个给我们另外一个回答（+1）。最后，通过这个过程的推断，我们可以确定如果一个元素等于他本身，返回一个空的比特码（+0）。</p>
<p>    这个函数可以在 Internet Explorer、Firefox 和 Opera 中运行。但在 Safari 中却有残缺功能（因为他只有 contains() 方法，而没有 .sourceIndex 属性。我们只能得到包含（+16），被包含（+8），其他的所有结果都将返回（+1）代表一个断开）。</p>
<p>    PPK 提供了一个关于通过创建一个 getElementsByTagNames 方法使新功能可以被使用的很棒的例子。让我们改编他到我们的新方法中：</p>
<table border="1" cellspacing="1" cellpadding="1" width="80%" align="center">
<tbody>
<tr>
<td bgcolor="#c0c0c0">// Original by PPK quirksmode.org<br />
function getElementsByTagNames(list, elem) {<br />
        elem = elem || document;                 var tagNames = list.split(’,’), results = [];         </p>
<p>        for ( var i = 0; i &lt; tagNames.length; i++ ) {<br />
                var tags = elem.getElementsByTagName( tagNames[i] );<br />
                for ( var j = 0; j &lt; tags.length; j++ )<br />
                        results.push( tags[j] );<br />
        }         </p>
<p>        return results.sort(function(a, b){<br />
                return 3 &#8211; (comparePosition(a, b) &amp; 6);<br />
        });<br />
}</td>
</tr>
</tbody>
</table>
<p>    我们现在可以使用他来按次序构建一个站点的目录：</p>
<p>getElementsByTagNames(&#8220;h1, h2, h3&#8243;);</p>
<p>    虽然 Firefox 和 Opera 都采取了一些主动落实这一方法。我依然期待看到更多的浏览器进入，以帮助向前推动</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/125/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JS闭包之词法作用域</title>
		<link>http://www.zfkun.com/blog/index.php/archives/117</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/117#comments</comments>
		<pubDate>Fri, 05 Mar 2010 05:34:43 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[scope]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=117</guid>
		<description><![CDATA[var classA = function(){
    this.prop1 = 1;
}
classA.prototype.func1 = function(){
    var that = this,
        var1 = 2; 

    function a(){
        return function(){
            [...]]]></description>
			<content:encoded><![CDATA[<pre name="code" class="js">var classA = function(){
    this.prop1 = 1;
}
classA.prototype.func1 = function(){
    var that = this,
        var1 = 2; 

    function a(){
        return function(){
            alert(var1);
            alert(this.prop1);
        }.apply(that);
    };
    a();
}
var objA = new ClassA();
objA.func1();</pre>
<p>大家应该写过上面类似的代码吧，其实这里我想要表达的是有时候一个方法定义的地方和使用的地方会相隔十万八千里，那方法执行时，它能访问哪些变量，不能访问哪些变量，这个怎么判断呢？这个就是我们这次需要分析的问题—词法作用域</p>
<p><strong>词法作用域：</strong>变量的作用域是在定义时决定而不是执行时决定，也就是说词法作用域取决于源码，通过静态分析就能确定，因此词法作用域也叫做静态作用域。 with和eval除外，所以只能说JS的作用域机制非常接近词法作用域（Lexical scope）。</p>
<p>下面通过几个小小的案例，开始深入的了解对理解词法作用域和闭包必不可少的，JS执行时底层的一些概念和理论知识。</p>
<h3>经典案列重现</h3>
<h4>1、经典案例一</h4>
<pre name="code" class="js">/*全局（window）域下的一段代码*/
function a(i) {
    var i;
    alert(i);
};
a(10);</pre>
<p>疑问：上面的代码会输出什么呢？<br />
答案：没错，就是弹出10。具体执行过程应该是这样的</p>
<ol>
<li>a 函数有一个形参 i，调用 a 函数时传入实参 10，形参 i=10</li>
<li>接着定义一个同名的局部变量 i，未赋值</li>
<li>alert 输出 10</li>
<li>思考：局部变量 i 和形参 i 是同一个存储空间吗？</li>
</ol>
<h4>2、经典案例二</h4>
<pre name="code" class="js">/*全局（window）域下的一段代码*/
function a(i) {
    alert(i);
    alert(arguments[0]); //arguments[0]应该就是形参 i
    var i = 2;
    alert(i);
    alert(arguments[0]);
};
a(10);</pre>
<p>疑问：上面的代码又会输出什么呢？（（ 10,10,2,10 || 10,10,2,2 ））<br />
答案：在FireBug中的运行结果是第二个10,10,2,2，猜对了… ，下面简单说一下具体执行过程</p>
<ol>
<li>a 函数有一个形参i，调用 a 函数时传入实参 10，形参 i=10</li>
<li>第一个 alert 把形参 i 的值 10 输出</li>
<li>第二个 alert 把 arguments[0] 输出，应该也是 i</li>
<li>接着定义个局部变量 i 并赋值为2，这时候局部变量 i=2</li>
<li>第三个 alert 就把局部变量 i 的值 2 输出</li>
<li>第四个alert再次把 arguments[0] 输出</li>
<li>思考：这里能说明局部变量 i 和形参 i 的值相同吗？</li>
</ol>
<h4>3、经典案例三</h4>
<pre name="code" class="js">/*全局（window）域下的一段代码*/
function a(i) {
    alert(i);
    alert(arguments[0]); //arguments[0]应该就是形参 i
    var i = 2;
    alert(i);
    alert(arguments[0]);
};
a(10);</pre>
<p>疑问：上面的代码又又会输出什么呢？（（ undefined || 10 ））<br />
答案：在FireBug中的运行结果是 10，下面简单说一下具体执行过程</p>
<ol>
<li>第一句声明一个与形参 i 同名的局部变量 i，根据结果我们知道，后一个 i 是指向了</li>
<li>形参 i，所以这里就等于把形参 i 的值 10 赋了局部变量 i</li>
<li>第二个 alert 当然就输出 10</li>
<li>思考：结合案列二，这里基本能说明局部变量 i 和形参 i 指向了同一个存储地址！</li>
</ol>
<h4>4、经典案例四</h4>
<pre name="code" class="js">/*全局（window）域下的一段代码*/
var i=10;
function a() {
    alert(i);
    var i = 2;
    alert(i);
};
a();</pre>
<p>疑问：上面的代码又会输出什么呢？（小子，看这回整不死你！哇哈哈，就不给你选项）<br />
答案：在FireBug中的运行结果是 undefined, 2，下面简单说一下具体执行过程</p>
<ol>
<li>第一个alert输出undefined</li>
<li>第二个alert输出 2</li>
<li>思考：到底怎么回事儿？</li>
</ol>
<h4>5、经典案例五…………..N</h4>
<p>看到上面的几个例子，你可能会想，怎么可能，我写了几年的 js 了，怎么这么简单例子也会犹豫，结果可能还答错了。其实可能原因是：我们能很快的写出一个方法，但到底方法内部是怎么执行的呢？执行的细节又是怎么样的呢？你可能没有进行过深入的学习和了解。要了解这些细节，那就需要了解 JS 引擎的工作方式，所以下面我们就把 JS 引擎对一个方法的解析过程进行一个稍微深入一些的介绍</p>
<h4>解析过程</h4>
<p><strong>1、执行顺序</strong></p>
<ul>
<li>编译型语言，编译步骤分为：词法分析、语法分析、语义检查、代码优化和字节生成。</li>
<li>解释型语言，通过词法分析和语法分析得到语法分析树后，就可以开始解释执行了。这里是一个简单原始的关于解析过程的原理，仅作为参考，详细的解析过程（各种JS引擎还有不同）还需要更深一步的研究</li>
</ul>
<p>JavaScript执行过程，如果一个文档流中包含多个script代码段（用script标签分隔的js代码或引入的js文件），它们的运行顺序是：</p>
<ol>
<li>步骤1. 读入第一个代码段（js执行引擎并非一行一行地执行程序，而是一段一段地分析执行的）</li>
<li>步骤2. 做词法分析和语法分析，有错则报语法错误（比如括号不匹配等），并跳转到步骤5</li>
<li>步骤3. 对【var】变量和【function】定义做“预解析“（永远不会报错的，因为只解析正确的声明）</li>
<li>步骤4. 执行代码段，有错则报错（比如变量未定义）</li>
<li>步骤5. 如果还有下一个代码段，则读入下一个代码段，重复步骤2</li>
<li>步骤6. 结束</li>
</ol>
<p><strong>2、特殊说明</strong><br />
全局域（window）域下所有JS代码可以被看成是一个“匿名方法“，它会被自动执行，而此“匿名方法“内的其它方法则是在被显示调用的时候才被执行<br />
<strong>3、关键步骤</strong><br />
上面的过程，我们主要是分成两个阶段</p>
<ol>
<li>解析：就是通过语法分析和预解析构造合法的语法分析树。</li>
<li>执行：执行具体的某个function，JS引擎在执行每个函数实例时，都会创建一个执行环境（ExecutionContext）和活动对象（activeObject）（它们属于宿主对象，与函数实例的生命周期保持一致）</li>
</ol>
<p><strong>3、关键概念</strong><br />
到这里，我们再更强调以下一些概念，这些概念都会在下面用一个一个的实体来表示，便于大家理解</p>
<ul>
<li>语法分析树（SyntaxTree）可以直观地表示出这段代码的相关信息,具体的实现就是JS引擎创建了一些表，用来记录每个方法内的变量集（variables），方法集（functions）和作用域（scope）等</li>
<li>执行环境（ExecutionContext）可理解为一个记录当前执行的方法【外部描述信息】的对象,记录所执行方法的类型，名称，参数和活动对象（activeObject）</li>
<li>活动对象（activeObject）可理解为一个记录当前执行的方法【内部执行信息】的对象,记录内部变量集（variables）、内嵌函数集（functions）、实参（arguments）、作用域链（scopeChain）等执行所需信息，其中内部变量集（variables）、内嵌函数集（functions）是直接从第一步建立的语法分析树复制过来的</li>
<li>词法作用域：变量的作用域是在定义时决定而不是执行时决定，也就是说词法作用域取决于源码，通过静态分析就能确定，因此词法作用域也叫做静态作用域。 with和eval除外，所以只能说JS的作用域机制非常接近词法作用域（Lexical scope）</li>
<li>作用域链：词法作用域的实现机制就是作用域链（scopeChain）。作用域链是一套按名称查找（Name Lookup）的机制，首先在当前执行环境的 ActiveObject 中寻找，没找到，则顺着作用域链到父 ActiveObject 中寻找，一直找到全局调用对象（Global Object）</li>
</ul>
<p><strong>4、实体表示</strong><br />
<img src="http://ued.sohu.com/uploads/2009/11/%E6%8D%95%E8%8E%B7.JPG" alt="源码，语法分析树，执行环境和活动对象的引用关系" /></p>
<h3>解析模拟</h3>
<p>估计，看到这儿，大家还是很朦胧吧，什么是语法分析树，语法分析树到底长什么样子，作用域链又怎么实现的，活动对象又有什么内容等等，还是不是太清晰，下面我们就通过一段实际的代码来模拟整个解析过程，我们就把语法分析树，活动对象实实在在的创建出来，理解作用域，作用域链的到底是怎么实现的<br />
<strong>1、模拟代码</strong></p>
<pre name="code" class="js">/*全局（window）域下的一段代码*/
var i = 1,j = 2,k = 3;
function a(o,p,x,q){
    var x = 4;
        alert(i);
    function b(r,s) {
        var i = 11,y = 5;
            alert(i);
        function c(t){
          var z = 6;
                alert(i);
        };
            //函数表达式
        var d = function(){
                alert(y);
            };
            c(60);
            d();
    };
        b(40,50);
}
a(10,20,30);</pre>
<p><strong>2、语法分析树</strong><br />
上面的代码很简单，就是先定义了一些全局变量和全局方法，接着在方法内再定义局部变量和局部方法，现在JS解释器读入这段代码开始解析，前面提到 JS 引擎会先通过语法分析和预解析得到语法分析树，至于语法分析树长什么样儿，都有些什么信息，下面我们以一种简单的结构：一个 JS 对象(为了清晰表示个各种对象间的引用关系，这里的只是伪对象表示，可能无法运行)来描述语法分析树（这是我们比较熟悉的，实际结构我们不去深究，肯定复杂得多，这里是为了帮助理解解析过程而特意简化）</p>
<pre name="code" class="js">/**
* 模拟建立一棵语法分析树，存储function内的变量和方法
*/
var SyntaxTree = {
        // 全局对象在语法分析树中的表示
    window: {
        variables:{
            i:{ value:1},
            j:{ value:2},
            k:{ value:3}
        },
        functions:{
            a: this.a
        }
    }, 

    a:{
        variables:{
            x:'undefined'
        },
        functions:{
            b: this.b
        },
        scope: this.window
    }, 

    b:{
        variables:{
            y:'undefined'
        },
        functions:{
            c: this.c,
            d: this.d
        },
        scope: this.a
    }, 

    c:{
        variables:{
            z:'undefined'
        },
        functions:{},
        scope: this.b
    }, 

    d:{
        variables:{},
        functions:{},
        scope: {
           myname:d,
           scope: this.b
        }
    }
};</pre>
<p>上面就是关于语法分析树的一个简单表示，正如我们前面分析的，语法分析树主要记录了每个 function 中的变量集（variables），方法集（functions）和作用域（scope）<br />
语法分析树关键点</p>
<ul>
<li>1变量集（variables）中，只有变量定义，没有变量值，这时候的变量值全部为“undefined”</li>
<li>2作用域（scope），根据词法作用域的特点，这个时候每个变量的作用域就已经明确了，而不会随执行时的环境而改变。【什么意思呢？就是我们经常将一个方法 return 回去，然后在另外一个方法中去执行，执行时，方法中变量的作用域是按照方法定义时的作用域走。其实这里想表达的意思就是不管你在多么复杂，多么远的地方执行该方法，最终判断方法中变量能否被访问还是得回到方法定义时的地方查证】</li>
<li>3作用域（scope）建立规则</li>
<li>a对于函数声明和匿名函数表达式来说，[scope]就是它创建时的作用域</li>
<li>b对于有名字的函数表达式，[scope]顶端是一个新的JS对象（也就是继承了Object.prototype），这个对象有两个属性，第一个是自身的名称，第二个是定义的作用域，第一个函数名称是为了确保函数内部的代码可以无误地访问自己的函数名进行递归。</li>
</ul>
<p><strong>3、执行环境与活动对象</strong><br />
语法分析完成，开始执行代码。我们调用每一个方法的时候，JS 引擎都会自动为其建立一个执行环境和一个活动对象，它们和方法实例的生命周期保持一致，为方法执行提供必要的执行支持，针对上面的几个方法，我们这里统一为其建立了活动对象（按道理是在执行方法的时候才会生成活动对象，为了便于演示，这里一下子定义了所有方法的活动对象），具体如下：<br />
<strong>执行环境</strong></p>
<pre name="code" class="js">/**
* 执行环境:函数执行时创建的执行环境
*/
var ExecutionContext = {
    window: {
        type: 'global',
        name: 'global',
        body: ActiveObject.window
    }, 

    a:{
        type: 'function',
        name: 'a',
        body: ActiveObject.a,
        scopeChain: this.window.body
    }, 

    b:{
        type: 'function',
        name: 'b',
        body: ActiveObject.b,
        scopeChain: this.a.body
    }, 

    c:{
        type: 'function',
        name: 'c',
        body: ActiveObject.c,
        scopeChain: this.b.body
    }, 

    d:{
        type: 'function',
        name: 'd',
        body: ActiveObject.d,
        scopeChain: this.b.body
    }
}</pre>
<p>上面每一个方法的执行环境都存储了相应方法的类型（function）、方法名称（funcName）、活动对象（ActiveObject）、作用域链（scopeChain）等信息,其关键点如下：</p>
<ul>
<li>body属性，直接指向当前方法的活动对象</li>
<li>scopeChain属性，作用域链，它是一个链表结构，根据语法分析树中当前方法对应的scope属性，它指向scope对应的方法的活动对象（ActivceObject），变量查找就是跟着这条链条查找的</li>
</ul>
<p><strong>活动对象</strong></p>
<pre name="code" class="js">/**
* 活动对象：函数执行时创建的活动对象列表
*/
var ActiveObject = {
        window: {
        variables:{
            i: { value:1},
            j: { value:2},
            k: { value:3}
        },
        functions:{
            a: this.a
        }
    }, 

    a:{
        variables:{
            x: {value:4}
        },
        functions:{
            b: SyntaxTree.b
        },
        parameters:{
            o: {value: 10},
            p: {value: 20},
            x: this.variables.x,
            q: 'undefined'
        },
        arguments:[this.parameters.o,this.parameters.p,this.parameters.x]
    }, 

    b:{
        variables:{
            y:{ value:5}
        },
        functions:{
            c: SyntaxTree.c,
            d: SyntaxTree.d
        },
        parameters:{
            r:{value:40},
            s:{value:50}
        },
        arguments:[this.parameters.r,this.parameters.s]
    }, 

    c:{
        variables:{
            z:{ value:6}
        },
        functions:{},
        parameters:{
            u:{value:70}
        },
        arguments:[this.parameters.u]
    }, 

    d:{
        variables:{},
        functions:{},
        parameters:{},
        arguments:[]
    }
}</pre>
<p>上面每一个活动对象都存储了相应方法的内部变量集（variables）、内嵌函数集（functions）、形参（parameters）、实参（arguments）等执行所需信息，活动对象关键点</p>
<ul>
<li>创建活动对象，从语法分析树复制方法的内部变量集（variables）和内嵌函数集（functions）</li>
<li>方法开始执行，活动对象里的内部变量集全部被重置为 undefined</li>
<li>创建形参（parameters）和实参（arguments）对象，同名的实参，形参和变量之间是【引用】关系</li>
<li>执行方法内的赋值语句，这才会对变量集中的变量进行赋值处理</li>
<li>变量查找规则是首先在当前执行环境的 ActiveObject 中寻找，没找到，则顺着执行环境中属性 ScopeChain 指向的 ActiveObject 中寻找，一直到 Global Object（window）</li>
<li>方法执行完成后，内部变量值不会被重置，至于变量什么时候被销毁，请参考下面一条</li>
<li>方法内变量的生存周期取决于方法实例是否存在活动引用，如没有就销毁活动对象</li>
<li>6和7 是使闭包能访问到外部变量的根本原因</li>
</ul>
<h3>重释经典案例</h3>
<p>案列一二三</p>
<p>根据【在一个方法中，同名的实参，形参和变量之间是引用关系，也就是JS引擎的处理是同名变量和形参都引用同一个内存地址】，所以才会有二中的修改arguments会影响到局部变量的情况出现</p>
<p>案例四</p>
<p>根据【JS引擎变量查找规则，首先在当前执行环境的 ActiveObject 中寻找，没找到，则顺着执行环境中属性 ScopeChain 指向的 ActiveObject 中寻找，一直到 Global Object（window）】，所以在四中，因为在当前的ActiveObject中找到了有变量 i 的定义，只是值为 “undefined”，所以直接输出 “undefined” 了</p>
<h3>总结</h3>
<p>以上是我在学习和使用了JS一段时间后,为了更深入的了解它, 也为了更好的把握对它的应用, 从而在对闭包的学习过程中,自己对于词法作用域的一些理解和总结,中间可能有一些地方和真实的JS解释引擎有差异,因为我只是站在一个刚入门的前端开发人员而不是系统设计者的角度上去分析这个问题，希望能对JS开发者理解此法作用域带来一些帮助！</p>
<p>原文链接： <a href="http://ued.sohu.com/article/204">http://ued.sohu.com/article/204</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/117/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Comet—“服务器推”技术</title>
		<link>http://www.zfkun.com/blog/index.php/archives/115</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/115#comments</comments>
		<pubDate>Wed, 03 Mar 2010 06:02:46 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[comet]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[push]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=115</guid>
		<description><![CDATA[Comet指的是一种Web应用程序架构。可以直接说，它不是一种技术，而是一种思想，只是这种思想采用了已有的技术去实现。在这种思想里，客户端（Client）不需要显式地向服务器端（Server）发出请求，Server会在其数据发生变化的时候主动将数据异步发送给Client，从而使Client能够及时更新数据并呈现给用户。它不同于传统的Web，也不同于当前流行的Ajax，这种思想非常架构思想非常适合event-driven(事件驱动)式的Web应用和对交互性及实时性要求很强的应用，比如股票交易，聊天室，Web IM，网游等。]]></description>
			<content:encoded><![CDATA[<h2>一、名称解释：</h2>
<p>Comet最早是由Alex Russell（Dojo Toolkit 项目主管和Dojo Foundation主席）在自己的博客中提出的术语，他是这样说的：”New services like Jot Live and Meebo are built with a style of data transmission that is neither traditional nor Ajax. Their brand of low-latency data transfer to the browser is unique, and it is becoming ever-more common. Lacking a better term, I’ve taken to calling this style of event-driven, server-push data streaming “Comet”.” “They all use long-lived HTTP connections to reduce the latency with which messages are passed to the server. In essence, they do not poll the server occasionally. Instead the server has an open line of communication with which it can push data to the client.”</p>
<p>Comet指的是一种Web应用程序架构。可以直接说，它不是一种技术，而是一种思想，只是这种思想采用了已有的技术去实现。在这种思想里，客户端（Client）不需要显式地向服务器端（Server）发出请求，Server会在其数据发生变化的时候主动将数据异步发送给Client，从而使Client能够及时更新数据并呈现给用户。它不同于传统的Web，也不同于当前流行的Ajax，这种思想非常架构思想非常适合event-driven(事件驱动)式的Web应用和对交互性及实时性要求很强的应用，比如股票交易，聊天室，Web IM，网游等。</p>
<h2>二、成功案例：</h2>
<ul>
<li><a href="http://mail.google.com/mail/help/chat.html" target="_blank">GMail’s GTalk integration</a></li>
<li><a href="http://jotlive.com/" target="_blank">Jot Live</a></li>
<li><a href="http://renkoo.com/" target="_blank">Renkoo</a></li>
<li><a href="http://meebo.com/" target="_blank">Meebo</a></li>
</ul>
<h2>三、实现方式：</h2>
<p>Comet使用Client和Server之前的HTTP长链接最为数据传输的通道。</p>
<p>实现Comet，最常见的有下面两种方式：</p>
<ol>
<li>Ajax的长轮询（long-polling）：Javascript在处理完服务器返回的信息后再次发出请求，重新建立连接。不同于一般的Ajax，Javascript请求Server，无数据时Server不中断请求，still loading，在一定时间内，获取到数据后，返回请求，又Javascript获取数据后再次发出请求，由此轮询。需要注意的是请求的间隔时间以及每次请求的最长Loading时间。<br />
优点：异步请求；无需浏览器任何插件支持；采用Ajax技术，兼容性强；<br />
缺点：会产生大量的通信量，只能通过增加轮询的时间间隔来减轻Server的压力；</li>
<li>Iframe结合Htmlfile流（streaming）：通过在页面上嵌入一个隐藏的Iframe，设置其src属性为一个长连接的请求，Server采用flush方式将数据作为前端Javascript函数的参数传递；<br />
优点：不会有很大的通信量，而且数据接收非常及时，并且无中断；<br />
缺点：会产生进度条的Loading状态并一直穿着，用户使用体验很不好，在Google Talk中，通过Htmlfile Active解决了IE下的进度条显示问题；保持长期链接也非常耗服务器资源；</li>
</ol>
<h2>四、临时采用方案：</h2>
<p>目前，白社会(bai.sohu.com)采用了第一种方式来实现实时消息的推送，这种方式还存在一个问题就是，在多个tab页下，如何保证没有相同域的长连接请求（IE在同域下只能同时支持两个长链接），于是采用了子域形式来区分多个域，以保证每个tab页下访问的地址都不在一个域名下，此时问题又出来了，出现了Ajax跨域名请求，不过这个问题比较常见，解决方案也很多，我们采用的是Iframe形式；由此，为了实现Comet，我们走了很长一条路，虽然路的确走完了，但是走得很长很艰难，这也是为什么称它为“临时解决方案”，因为它并不是一个完美的方案，但我也相信，以后会更加完美。</p>
<h2>五、现有可参考框架</h2>
<ol>
<li>Cometd：官方地址：<a href="http://www.cometd.org" target="_blank">www.cometd.org<br />
</a>可以参考<a href="http://www.blogjava.net/xmatthew/archive/2008/11/20/208911.html" target="_blank">http://www.blogjava.net/xmatthew/archive/2008/11/20/208911.html</a></li>
<li>Pushlet：官方地址：<a href="http://www.pushlets.com" target="_blank">www.pushlets.com</a><br />
pushlet 提供了基于 AJAX 的 JavaScript 库文件用于实现长轮询方式的”服务器推”；还提供了基于 iframe 的 JavaScript 库文件用于实现流方式的”服务器推”。<br />
可以参考<a href="http://http://www.javaeye.com/topic/89158" target="_blank">http://www.javaeye.com/topic/89158</a></li>
</ol>
<h2>六、文章参考</h2>
<ol>
<li><a href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/" target="_blank">Comet：基于 HTTP 长连接的”服务器推”技术</a></li>
<li><a href="http://www.ibm.com/developerworks/cn/web/wa-lo-w2fpak-comet/index.html" target="_blank">实战 Comet 应用程序开发</a></li>
<li><a href="http://en.wikipedia.org/wiki/Comet_(programming)" target="_blank">Comet on wikipedia</a></li>
</ol>
<p>最后，本人对Comet也未进行深入的研究，只是希望通过此文能给大家起一个抛砖引玉的作用，希望大家能够在客户端实时技术上能够起点帮助作用，能够越来越丰富我们前端技术。</p>
<p>原文地址： <a href="http://ued.sohu.com/article/118">http://ued.sohu.com/article/118</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/115/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Javascript的变量与delete操作符</title>
		<link>http://www.zfkun.com/blog/index.php/archives/112</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/112#comments</comments>
		<pubDate>Mon, 01 Mar 2010 05:19:40 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[delete]]></category>
		<category><![CDATA[js]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=112</guid>
		<description><![CDATA[Javascript中的delete操作虽然是一个小小的delete操作符，其行为却异常复杂]]></description>
			<content:encoded><![CDATA[<p><a href="http://nanto.asablo.jp/blog/2008/01/09/2552470">原文链接</a></p>
<p>虽然是一个小小的delete操作符，其行为却异常复杂。</p>
<p><!-- end Pukiwiki generated code--><!-- begin Pukiwiki generated code--><a id="contents_2"></a></p>
<ul>
<li><a href="#content_2_0">Javascript的变量</a></li>
<li><a href="#content_2_1">delete操作符删除的对象</a></li>
<li><a href="#content_2_2">对变量执行delete的情况</a></li>
<li><a href="#content_2_3">能删除的属性和不能删除的属性</a></li>
<li><a href="#content_2_4">能删除的变量和不能删除的变量</a></li>
<li><a href="#content_2_5">delete的返回值</a></li>
</ul>
<p> </p>
<h2 id="content_2_0">Javascript的变量</h2>
<p>实际上Javascript中，变量 = 对象属性，这是因为 Javascript 在执行脚本之前会创建一个Global对象，所有的全局变量都是这个Global对象的属性，执行函数时也会创建一个Activation对象，所有的局部变量都是这个Activation对象的属性。如下例：</p>
<pre name="code" class="js">var global = 42;
this.global;    // 42, 可以通过this来访问Global对象

this.global2 = 12;
global2;        // 12

function foo() {
  var local = 36;
  // 不过无法直接访问Activation，
  // 因此无法通过 foo.local 的方式来访问local变量
}</pre>
<h2 id="content_2_1">delete操作符删除的对象</h2>
<p>C++中也有delete操作符，它删除的是指针所指向的对象。例如：</p>
<pre name="code" class="c">// C++
class Object {
public:
  Object *x;
}

Object o;
o.x = new Object();
delete o.x;     // 上一行new的Object对象将被释放</pre>
<p>但Javascript的delete与C++不同，<strong>它不会删除o.x指向的对象，而是删除o.x属性本身</strong>。</p>
<pre>// Javascript
var o = {};
o.x = new Object();
delete o.x;     // 上一行new的Object对象依然存在
o.x;            // undefined，o的名为x的属性被删除了</pre>
<p>在实际的Javascript中，delete o.x之后，Object对象会由于失去了引用而被垃圾回收，所以delete o.x也就“相当于”删除了o.x所指向的对象，但这个动作并不是ECMAScript标准，也就是说，即使某个实现完全不删除Object对象，也不算是违反ECMAScript标准。</p>
<p>“删除属性而不是删除对象”这一点，可以通过以下的代码来确认。</p>
<pre name="code" class="js">var o = {};
var a = { x: 10 };
o.a = a;
delete o.a;    // o.a属性被删除
o.a;           // undefined
a.x;           // 10, 因为{ x: 10 } 对象依然被 a 引用，所以不会被回收</pre>
<p>另外，delete o.x 也可以写作 delete o["x"]，两者效果相同。</p>
<h2 id="content_2_2">对变量执行delete的情况</h2>
<p>由于变量也是 Global 或者是 Activation 对象的属性，所以对变量的delete操作也是同样的结果。</p>
<pre name="code" class="js">var global = 42;
delete global;     // 删除Global.global

function foo() {
  var local = 36;
  delete local;    // 删除Activation.local
}</pre>
<h2 id="content_2_3">能删除的属性和不能删除的属性</h2>
<p>并不是所有的属性都能被delete。例如，prototype中声明的属性就无法被delete：</p>
<pre name="code" class="js">function C() { this.x = 42; }
C.prototype.x = 12;

var o = new C();
o.x;     // 42, 构造函数中定义的o.x

delete o.x;
o.x;     // 12,  prototype中定义的o.x，即使再次执行delete o.x也不会被删除</pre>
<p>对象的预定义属性也无法删除。 可以认为这类属性带有DontDelete的特性。</p>
<pre name="code" class="js">var re = /abc/i;
delete re.ignoreCase;
re.ignoreCase; // true, ignoreCase无法删除</pre>
<h2 id="content_2_4">能删除的变量和不能删除的变量</h2>
<p>通过var声明的变量和通过function声明的函数拥有DontDelete特性，无法被删除。</p>
<pre>var x = 36;
delete x;
x;     // 36, x没有被删除

y = 12;
delete y;
y;     // undefined

function foo() { return 42; }
delete foo;
foo();  // 42</pre>
<p>但是有一点例外，就是通过 eval 执行的代码中，通过var声明的变量虽然与正常的var声明变量同属于Global对象，但它们不具有DontDelete特性，能被删除。</p>
<pre name="code" class="js">eval("var x = 36;");
x;     // 42
delete x;
x;     // undefined</pre>
<p>但是这也有一点例外，eval的代码中的函数内通过var定义的变量具有DontDelete，不能被删除。</p>
<pre name="code" class="js">eval("(function() { var x = 42; delete x; return x; })();");
// 返回 42</pre>
<h2 id="content_2_5">delete的返回值</h2>
<p>delete是普通运算符，会返回true或false。规则为：当被delete的对象的属性存在并且拥有DontDelete时返回false，否则返回true。这里的一个特点就是，对象属性不存在时也返回true，所以返回值并非完全等同于删除成功与否。</p>
<pre name="code" class="js">function C() { this.x = 42; }
C.prototype.y = 12;
var o = new C();

delete o.x; // true
o.x;        // undefined
"x" in o;   // false
// o.x存在并且没有DontDelete，返回true

delete o.y; // true
o.y;        // 12
// o自身没有o.y属性，所以返回true
// 从这里也可以看到prototype链的存在，对象自身属性和prototype属性是不同的

delete o;   // false
// Global.o拥有DontDelete特性所以返回false

delete undefinedProperty;  // true
// Global没有名为undefinedProperty的属性因此返回true

delete 42;  // true
// 42不是属性所以返回true。有的实现会抛出异常（违反ECMAScript标准）

var x = 24;
delete x++;  // true
x;           // 25
// 被删除的是x++的返回值(24)，不是属性，所以返回true</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/112/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Understanding and Solving Internet Explorer Leak Patterns</title>
		<link>http://www.zfkun.com/blog/index.php/archives/109</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/109#comments</comments>
		<pubDate>Sun, 28 Feb 2010 06:01:57 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[closure]]></category>
		<category><![CDATA[dom]]></category>
		<category><![CDATA[gc]]></category>
		<category><![CDATA[ie]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[leak]]></category>
		<category><![CDATA[memory]]></category>
		<category><![CDATA[reference]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=109</guid>
		<description><![CDATA[In the past, memory leaks haven't posed huge problems for Web developers. Pages were kept relatively simple and navigation between different locations within a site was a great way to clean up any loose memory. If there was a leak, it was most likely small enough to go unnoticed.

New Web applications live up to higher standards. A page might run for hours without being navigated and retrieve updated information dynamically through Web services. Language features are pushed to the breaking point by combining complex event schemes, object-oriented JScript, and closures to produce entire applications. With these and other changes, certain memory leak patterns are becoming more prominent, especially those previously hidden by navigation.

The good news is that memory leak patterns can be easily spotted if you know what to look for. Most of the troublesome patterns you might face have known workarounds requiring only a small amount of extra work on your behalf. While some pages might still fall prey to small memory leaks, the most noticeable ones can be easily removed.]]></description>
			<content:encoded><![CDATA[<h2><strong>Understanding and Solving Internet Explorer Leak Patterns</strong></h2>
<p><!--Content type: PSDK_3. Transform: webcollection2mtps.xslt.--><a id="ie_leakpattern"><!----></a></p>
<p>Justin Rogers<br />
Microsoft Corporation</p>
<p>June 2005</p>
<h2>The Evolution of the Web Developer</h2>
<p>In the past, memory leaks haven&#8217;t posed huge problems for Web developers. Pages were kept relatively simple and navigation between different locations within a site was a great way to clean up any loose memory. If there was a leak, it was most likely small enough to go unnoticed.</p>
<p>New Web applications live up to higher standards. A page might run for hours without being navigated and retrieve updated information dynamically through Web services. Language features are pushed to the breaking point by combining complex event schemes, object-oriented JScript, and closures to produce entire applications. With these and other changes, certain memory leak patterns are becoming more prominent, especially those previously hidden by navigation.</p>
<p>The good news is that memory leak patterns can be easily spotted if you know what to look for. Most of the troublesome patterns you might face have known workarounds requiring only a small amount of extra work on your behalf. While some pages might still fall prey to small memory leaks, the most noticeable ones can be easily removed.</p>
<h2>Leak Patterns</h2>
<p>The following sections will discuss patterns of memory leaks and point out some common examples of each pattern. One great example of a pattern is the closure feature of JScript, while another example is the use of closures in hooking events. If you&#8217;re familiar with the event hooking example, you might be able to find and fix many of your memory leaks, but other closure-related issues might go unnoticed.</p>
<p>Now, let&#8217;s look at the following patterns:</p>
<ol>
<li>Circular References—When mutual references are counted between Internet Explorer&#8217;s COM infrastructure and any scripting engine, objects can leak memory. This is the broadest pattern.</li>
<li>Closures—Closures are a specific form of circular reference that pose the largest pattern to existing Web application architectures. Closures are easy to spot because they rely on a specific language keyword and can be searched for generically.</li>
<li>Cross-Page Leaks—Cross-page leaks are often very small leaks of internal book-keeping objects as you move from site to site. We&#8217;ll examine the DOM Insertion Order issue, along with a workaround that shows how small changes to your code can prevent the creation of these book-keeping objects.</li>
<li>Pseudo-Leaks—These aren&#8217;t really leaks, but can be extremely annoying if you don&#8217;t understand where your memory is going. We&#8217;ll examine the script element rewriting and how it appears to leak quite a bit of memory, when it is really performing as required.</li>
</ol>
<h2>Circular References</h2>
<p>Circular references are the root of nearly every leak. Normally, script engines handle circular references through their garbage collectors, but certain unknowns can prevent their heuristics from working properly. The unknown in the case of IE would be the status of any DOM elements that a portion of script has access to. The basic principle would be as follows:</p>
<p><!--src=[ie_leak_patterns_fig01.gif]--><img src="http://i.msdn.microsoft.com/Bb250448.ie_leak_patterns_fig01(en-us,VS.85).gif" alt="Figure 1 Basic Circular Reference Pattern" /></p>
<p><strong>Figure 1. Basic Circular Reference Pattern</strong></p>
<p>The cause of the leak in this pattern is based on COM reference counting. The script engine objects will hold a reference to the DOM element and will be waiting for any outstanding references to be removed before cleaning up and releasing the DOM element pointer. In our case we have two references on the script engine object: the script engine scope, and the DOM element expando property. While terminating the script engine will release the first reference, the DOM element reference will never be released because it is waiting on the script engine object to release it! You might think it would be easy to detect this scenario and fix the problem, but in practice the basic case presented is only the tip of the iceberg. You could have circular references at the end of a 30 object chain and those would be much harder to detect.</p>
<p>If you are wondering what this pattern looks like in HTML, you can cause a leak by using a global script engine variable and a DOM element as shown.</p>
<pre><code>&lt;html&gt;
    &lt;head&gt;
        &lt;script language="JScript"&gt;

        var myGlobalObject;

        function SetupLeak()
        {
            // First set up the script scope to element reference
            myGlobalObject =
                document.getElementById("LeakedDiv");

            // Next set up the element to script scope reference
            document.getElementById("LeakedDiv").expandoProperty =
                myGlobalObject;
        }

        function BreakLeak()
        {
            document.getElementById("LeakedDiv").expandoProperty =
                null;
        }
        &lt;/script&gt;
    &lt;/head&gt;

    &lt;body onload="SetupLeak()" onunload="BreakLeak()"&gt;
        &lt;div id="LeakedDiv"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>To break the leak pattern you can make use of explicit null assignments. By assigning null before the document unloads you are telling the script engine there is no longer an association between the element and the object inside the engine. It can now properly clean up references and will release the DOM element. In this case, you as the Web developer know more about the relationships between your objects than the script engine does.</p>
<p>While that is the basic pattern, it can be difficult to spot more complex scenarios. A common usage of object-oriented JScript is to extend DOM elements by encapsulating them inside of a JScript object. During the construction process, you generally pass in the DOM element you want to attach to and then store a reference to the DOM element on the newly constructed object while at the same time storing an instance of the newly constructed object on the DOM element. That way your application model always has access to everything it needs. The problem is this is a very explicit circular reference, but because it uses different language aspects it might go unnoticed. Breaking up this kind of pattern can become more complex, and you can use the same simple methods discussed earlier.</p>
<pre><code>&lt;html&gt;
    &lt;head&gt;
        &lt;script language="JScript"&gt;

        function Encapsulator(element)
        {
            // Set up our element
            this.elementReference = element;

            // Make our circular reference
            element.expandoProperty = this;
        }

        function SetupLeak()
        {
            // The leak happens all at once
            new Encapsulator(document.getElementById("LeakedDiv"));
        }

        function BreakLeak()
        {
            document.getElementById("LeakedDiv").expandoProperty =
                null;
        }
        &lt;/script&gt;
    &lt;/head&gt;

    &lt;body onload="SetupLeak()" onunload="BreakLeak()"&gt;
        &lt;div id="LeakedDiv"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>More complex solutions to this problem involve registration schemes to note which elements/properties need to be unhooked, having the peer element hook events so that it can clean up before the document unloads, but often you can run into additional leak patterns without actually fixing the problem.</p>
<h2>Closures</h2>
<p>Closures are very often responsible for leaks because they create circular references without the programmer being fully aware. It isn&#8217;t immediately obvious that parent function parameters and local variables will be frozen in time, referenced, and held until the closure itself is released. In fact this has become such a common programming tactic, and users have run into issues so often, there are quite a few resources already available. Because they detail some of the history behind closures as well as some of the specific instances of closure leaks we&#8217;ll check those out after applying the closure model to our circular reference diagram and figuring out where these extra references are coming from.</p>
<p><!--src=[ie_leak_patterns_fig02.gif]--><img src="http://i.msdn.microsoft.com/Bb250448.ie_leak_patterns_fig02(en-us,VS.85).gif" alt="Figure 2 Circular References with Closures" /></p>
<p><strong>Figure 2. Circular References with Closures</strong></p>
<p>With normal circular references there were two solid objects holding references to each other, but closures are different. Rather than make the references directly, they are made instead by importing information from their parent function&#8217;s scope. Normally, a function&#8217;s local variables and the parameters used when calling a function only exist for the lifetime of the function itself. With closures, these variables and parameters continue to have an outstanding reference as long as the closure is alive, and since closures can live beyond the lifetime of their parent function so can any of the locals and parameters in that function. In the example, Parameter 1 would normally be released as soon as the function call was over. Because we&#8217;ve added a closure, a second reference is made, and that second reference won&#8217;t be released until the closure is also released. If you happened to attach the closure to an event, then you would have to detach it from that event. If you happened to attach the closure to an expando then you would need to null that expando.</p>
<p>Closures are also created per call, so calling this function twice will create two individual closures, each holding references to the parameters passed in each time. Because of this transparent nature it is really easy to leak closures. The following example provides the most basic of leaks using closures:</p>
<pre><code>&lt;html&gt;
    &lt;head&gt;
        &lt;script language="JScript"&gt;

        function AttachEvents(element)
        {
            // This structure causes element to ref ClickEventHandler
            element.attachEvent("onclick", ClickEventHandler);

            function ClickEventHandler()
            {
                // This closure refs element
            }
        }

        function SetupLeak()
        {
            // The leak happens all at once
            AttachEvents(document.getElementById("LeakedDiv"));
        }

        function BreakLeak()
        {
        }
        &lt;/script&gt;
    &lt;/head\&gt;

    &lt;body onload="SetupLeak()" onunload="BreakLeak()"&gt;
        &lt;div id="LeakedDiv"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>If you are wondering how to break this leak, it won&#8217;t be as easy as a normal circular reference. The &#8220;closure&#8221; can be viewed as a temporary object that exists in the function scope. Once the function exits, you lose reference to the closure itself, so what would you end up calling <strong>detachEvent</strong> with? One of the most interesting approaches to this problem was demonstrated on <a id="ctl00_MTCS_main_ctl03" onclick="javascript:Track('ctl00_MTCS_main_ctl00|ctl00_MTCS_main_ctl03',this);" href="http://spaces.msn.com/members/siteexperts/Blog/cns!1pNcL8JwTfkkjv4gg6LkVCpw!338.entry">MSN spaces thanks to Scott Isaacs</a>. The approach uses a second closure to additionally hook the window&#8217;s <strong>onUnload</strong> event, and because this closure has the same &#8220;scoped&#8221; objects it is able to detach the event, detach itself, and finish the clean up process. To make everything easily fit with our model we can also store the closure on an expando, detach it, and then null the expando, as in the following example.</p>
<pre><code>&lt;html&gt;
    &lt;head&gt;
        &lt;script language="JScript"&gt;

        function AttachEvents(element)
        {
            // In order to remove this we need to put
            // it somewhere. Creates another ref
            element.expandoClick = ClickEventHandler;

            // This structure causes element to ref ClickEventHandler
            element.attachEvent("onclick", element.expandoClick);

            function ClickEventHandler()
            {
                // This closure refs element
            }
        }

        function SetupLeak()
        {
            // The leak happens all at once
            AttachEvents(document.getElementById("LeakedDiv"));
        }

        function BreakLeak()
        {
            document.getElementById("LeakedDiv").detachEvent("onclick",
                document.getElementById("LeakedDiv").expandoClick);
            document.getElementById("LeakedDiv").expandoClick = null;
        }
        &lt;/script&gt;
    &lt;/head&gt;

    &lt;body onload="SetupLeak()" onunload="BreakLeak()"&gt;
        &lt;div id="LeakedDiv"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>In a <a id="ctl00_MTCS_main_ctl04" onclick="javascript:Track('ctl00_MTCS_main_ctl00|ctl00_MTCS_main_ctl04',this);" href="http://support.microsoft.com/default.aspx?scid=KB;EN-US;830555">Knowledge Base article</a>, we actually recommend that you try not to use closures unless they are necessary. In the example, I&#8217;ve given we don&#8217;t need to use a closure as the event handler, instead we can move the closure to a global scope. When the closure becomes a function, it no longer inherits the parameters or local variables from its parent function so we don&#8217;t have to worry about closure-based circular references at all. Most code can be fixed by creating an architecture that doesn&#8217;t rely on closures where they aren&#8217;t necessary.</p>
<p>Finally, Eric Lippert, one of the developers of the scripting engines, has a <a id="ctl00_MTCS_main_ctl05" onclick="javascript:Track('ctl00_MTCS_main_ctl00|ctl00_MTCS_main_ctl05',this);" href="http://blogs.msdn.com/ericlippert/archive/2003/09/17/53028.aspx">great post on closures in general</a>. His final recommendations are also along the lines of only using closures when truly necessary. While his article doesn&#8217;t mention any of the workarounds for the closure pattern, hopefully we&#8217;ve covered enough examples here to get you started.</p>
<h2>Cross-Page Leaks</h2>
<p>Leaks that are based on order of insertion are almost always caused by the creation of intermediate objects that don&#8217;t get cleaned up properly. That is exactly the case when creating dynamic elements and then attaching them to the DOM. The basic pattern is attaching two dynamically created objects together temporarily which creates a scope from the child to the parent element. Later, when you attach this two-element tree to the primary tree, they both inherit the scope of the document and a temporary object is leaked. The following diagram shows two methods for attaching dynamically created elements to the tree. In the first model, attach each child element to its parent, and finally attach the entire subtree to the primary tree. This method can cause leaks through temporary objects if other conditions are met. In the second model, we attach elements into the primary tree working our way from top-level dynamically created element down through all of the children. Because each attachment inherits the scope of the primary document we never generate temporary scopes. This method is much better at avoiding potential memory leaks.</p>
<p><!--src=[ie_leak_patterns_fig03.gif]--><img src="http://i.msdn.microsoft.com/Bb250448.ie_leak_patterns_fig03(en-us,VS.85).gif" alt="Figure 3 DOM Insertion Order Leak Model" /></p>
<p><strong>Figure 3. DOM Insertion Order Leak Model</strong></p>
<p>Next, we are going to cover an example of a leak that is transparent to most leak-detection algorithms. Because we don&#8217;t leak any publicly visible elements and the objects we leak are very small you might never notice this problem. For our example to work, the dynamically created elements will have to contain a script pointer in the form of an inline function. This will allow us to leak an internal script object that is created temporarily as we attach elements together. Because the leak is small, we&#8217;ll have to run thousands of samples. In fact, the objects leaked are only a few bytes. By running the sample and navigating to an empty page, you can see the difference in memory consumption between the two versions. When we use the first DOM model of attaching child to parent, then parent to the primary tree, our memory usage goes up a bit. This is a cross-navigation leak and the memory isn&#8217;t reclaimed until you restart the IE process. If you run the sample a few more times, using the second DOM model of attaching the parent to the primary tree and then the child to the parent, your memory won&#8217;t continue to climb and you&#8217;ll find that you&#8217;ve fixed the cross-page navigation leak.</p>
<pre><code>&lt;html&gt;
    &lt;head&gt;
        &lt;script language="JScript"&gt;

        function LeakMemory()
        {
            var hostElement = document.getElementById("hostElement");

            // Do it a lot, look at Task Manager for memory response

            for(i = 0; i &lt; 5000; i++)
            {
                var parentDiv =
                    document.createElement("&lt;div onClick='foo()'&gt;");
                var childDiv =
                    document.createElement("&lt;div onClick='foo()'&gt;");

                // This will leak a temporary object
                parentDiv.appendChild(childDiv);
                hostElement.appendChild(parentDiv);
                hostElement.removeChild(parentDiv);
                parentDiv.removeChild(childDiv);
                parentDiv = null;
                childDiv = null;
            }
            hostElement = null;
        }

        function CleanMemory()
        {
            var hostElement = document.getElementById("hostElement");

            // Do it a lot, look at Task Manager for memory response

            for(i = 0; i &lt; 5000; i++)
            {
                var parentDiv =
                    document.createElement("&lt;div onClick='foo()'&gt;");
                var childDiv =
                    document.createElement("&lt;div onClick='foo()'&gt;");

                // Changing the order is important, this won't leak
                hostElement.appendChild(parentDiv);
                parentDiv.appendChild(childDiv);
                hostElement.removeChild(parentDiv);
                parentDiv.removeChild(childDiv);
                parentDiv = null;
                childDiv = null;
            }
            hostElement = null;
        }
        &lt;/script&gt;
    &lt;/head&gt;

    &lt;body&gt;
        &lt;button onclick="LeakMemory()"&gt;Memory Leaking Insert&lt;/button&gt;
        &lt;button onclick="CleanMemory()"&gt;Clean Insert&lt;/button&gt;
        &lt;div id="hostElement"&gt;&lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>This leak deserves clarification, because our workaround goes against some best practices in IE. The key points to understand about the leak are that DOM elements are being created with scripts already attached. This is actually crucial to the leak, because if we create DOM elements that don&#8217;t contain any script and attach them together in the same manner we don&#8217;t have a leak problem. This gives rise to a second workaround that might be even better for larger subtrees (in the example we only have two elements, so building the tree off the primary DOM isn&#8217;t a performance hit). The second workaround would be to create your elements with no scripts attached initially so that you can safely build your subtree. After you&#8217;ve attached your subtree to the primary DOM, go back and wire up any script events at that point. Remember to follow the principles for circular references and closures so you don&#8217;t cause a different leak in your code as you hook up your events.</p>
<p>I really wanted to point out this issue because it shows that not all memory leaks are easy to find. It could take thousands of iterations of a smaller pattern to become visible, and it might be something slight, like the order of insertion of DOM elements that causes the problem to arise. If you tend to program using only best practices, then you think you are safe, but this leak shows that even best practices can exhibit leaks. Our solution here was to improve upon the best practice or even introduce a new best practice in order to remove the leaking condition.</p>
<h2>Pseudo-Leaks</h2>
<p>Often times the actual behavior and expected behavior of some APIs can lead you to misdiagnose memory leaks. Pseudo-leaks almost always appear on the same page during dynamic scripting operations and should rarely be visible after navigation away from the page to a blank page. That is how you can eliminate the issue as a cross-page leak and then start to work on whether the memory consumption is expected. We&#8217;ll use script text rewriting as our example of a pseudo-leak.</p>
<p>Like the DOM Insertion Order issue, this issue also relies on the creation of temporary objects in order to &#8220;leak&#8221; memory. By rewriting the script text inside of a script element over and over again, slowly you&#8217;ll begin to leak various script engine objects that were attached to the previous contents. In particular, objects related to debugging script are left behind as are fully formed code elements.</p>
<pre><code>&lt;html&gt;
    &lt;head&gt;
        &lt;script language="JScript"&gt;

        function LeakMemory()
        {
            // Do it a lot, look at Task Manager for memory response

            for(i = 0; i &lt; 5000; i++)
            {
                hostElement.text = "function foo() { }";
            }
        }
        &lt;/script&gt;
    &lt;/head&gt;

    &lt;body&gt;
        &lt;button onclick="LeakMemory()"&gt;Memory Leaking Insert&lt;/button&gt;
        &lt;script id="hostElement"&gt;function foo() { }&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>If you run the above code and use the Task Manager trick again, while navigating between the &#8220;leaking&#8221; page and a blank page, you won&#8217;t notice a script leak. This script leak is entirely within a page and when you navigate away then you get your memory back. The reason this one is bad is due to expected behavior. You expect that after rewriting some script that the original script won&#8217;t stay around. But it really has to, because it might have been used already for event attachments and there might be outstanding reference counts. As you can see, this is a pseudo-leak. On the surface the amount of memory consumption looks really bad, but there is a completely valid reason.</p>
<h2>Conclusion</h2>
<p>Every Web developer builds a personal list of code examples that they know leak and learns to work around those leaks when they see them in code. This is extremely handy and is the reason the Web is relatively leak-free today. Thinking about the leaks in terms of patterns instead of individual code examples, you can start to develop even better strategies for dealing with them. The idea is to take them into account during the design phase and make sure you have plans for any potential leaks. Use defensive coding practices and assume that you&#8217;ll need to clean up all your own memory. While this is an overstatement of the problem, you very rarely need to clean up your own memory; it becomes obvious which variables and expando properties have the potential for leaking.</p>
<p>In the interest of patterns and design I highly recommend <a id="ctl00_MTCS_main_ctl07" onclick="javascript:Track('ctl00_MTCS_main_ctl00|ctl00_MTCS_main_ctl07',this);" href="http://spaces.msn.com/members/siteexperts/Blog/cns!1pNcL8JwTfkkjv4gg6LkVCpw!338.entry">Scott&#8217;s short blog entry</a> because it demonstrates a general purpose example of removing all closure-based leaks. It does require a bit more code, but the practice is sound and the improved pattern is easy to spot in code and to debug. Similar registration schemes can be used for expando-based circular references as long as care is taken that the registration method itself isn&#8217;t riddled with leaks (especially where closures are used)!</p>
<p><!----></p>
<p><!----></p>
<p><strong>About the author</strong></p>
<p><strong>Justin Rogers</strong> recently joined the Internet Explorer team as an Object Model developer working on extensibility and previously worked on such notable projects as the .NET QuickStart Tutorials, .NET Terrarium, and SQL Reporting Services Management Studio in SQL Server 2005.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/109/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转]Google Closure: 糟糕的JavaScript</title>
		<link>http://www.zfkun.com/blog/index.php/archives/80</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/80#comments</comments>
		<pubDate>Tue, 19 Jan 2010 15:38:29 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[closure]]></category>
		<category><![CDATA[google]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=80</guid>
		<description><![CDATA[上周在澳大利亚佩恩的Edge of the Web会议上我碰到了javascript library Raphaël 和 gRaphaël 的创建者Dmitry Baranovskiy。这两个library做的最重要的事情也许就是使在javascript效率相对低下的IE上面绘制一些复杂的矢量图变得了可能。然而，Dmitry 却很不爽，因为他找到的一些实现的很糟糕的代码，在Google刚刚发布的Closure Library中。]]></description>
			<content:encoded><![CDATA[<p><strong>原文：<a href="http://www.sitepoint.com/blogs/2009/11/12/google-closure-how-not-to-write-javascript/">Google Closure: How not to write JavaScript</a></strong></p>
<p><strong>译者注：google在2009年11月6号开源了自己在 gmail、google reader 等几乎所有重要 google 产品中使用的javascrpt : </strong><a href="http://code.google.com/closure/" target="_blank"><strong><span style="color: #1166cc;">google closure</span></strong></a><strong> ，包括一套庞大的类似与 dojo 的 library、一套与之相应的 compiler、一套 template 系统。closure 完成了很多事情，包括一直困扰前端开发们的开发效率和运行效率之间的平衡（closure 使用library来提升开发效率，使用侵入性极强的 compiler 来去除无用代码，保证执行效率缩减 js 的大小，这与 YUI 等 library 采用的 </strong><a href="http://developer.yahoo.com/yui/articles/hosting/" target="_blank"><strong><span style="color: #1166cc;">combo-handling</span></strong></a><strong> 是不一样的思路，但对于单独的页面，js 的代码量将更少是肯定的）总之，google 这次开源 Closure 是一个很棒的事情，网上有这太多关于这件事的讨论，大家可以到文章结尾的相关链接处看到更多的相关讨论的文章。这里翻译的是一篇 sitepoint 上指出的一些 Closure 的 javascript 的细节处理的错误，虽然有这些 stupid 的部分，却并不妨碍 google closure 是一个伟大的工具（据创始人 <a href="http://erik.eae.net/archives/2009/11/05/22.27.29/" target="_blank"><span style="color: #1166cc;">erik 说</span></a></strong><strong>，现在有超过400名google工程师贡献了closure的代码），在这种规模下，代码还是尽量的stupid一些好了。虽然这么说，了解一些聪明的javascript代码也并不会妨碍我们成为一个好的程序员，评价一个东西很糟糕也总是比创建一个新的东西容易得多。哈哈，废话不多说了，正文开始。</strong></p>
<p>上周在澳大利亚佩恩的<a href="http://www.edgeoftheweb.org.au/" target="_blank"><span style="color: #1166cc;">Edge of the Web</span></a>会议上我碰到了javascript library <a href="http://raphaeljs.com/" target="_blank"><span style="color: #1166cc;">Raphaël</span></a> 和 <a href="http://g.raphaeljs.com/" target="_blank"><span style="color: #1166cc;">gRaphaël</span></a> 的创建者<a href="http://dmitry.baranovskiy.com/" target="_blank"><span style="color: #1166cc;">Dmitry Baranovskiy</span></a>。这两个library做的最重要的事情也许就是使在javascript效率相对低下的IE上面绘制一些复杂的矢量图变得了可能。然而，Dmitry 却很不爽，因为他找到的一些实现的很糟糕的代码，在Google刚刚发布的<a href="http://googlecode.blogspot.com/2009/11/introducing-closure-tools.html" target="_blank"><span style="color: #1166cc;">Closure Library</span></a>中。</p>
<p>在会议上做了一个名为<a href="http://www.slideshare.net/Dmitry.Baranovskiy/your-javascript-library" target="_blank"><span style="color: #1166cc;">how to write your own JavaScript library</span></a>的演讲（<a href="http://passingcuriosity.com/2009/notes-on-dmitry-baranovskiys-talk-on-your-javascript-library/" target="_blank"><span style="color: #1166cc;">详细笔记</span></a>）之后，Dmitry在第二天早上早餐之后分享了他关于这个新library的想法：“就是这个世界现在需要的东西——另一个糟糕的JavaScript library”。当我问道是什么使它如此“糟糕”的时候，他解释说：“<strong>它是一个由不懂JavaScript的Java程序员们开发的JavaScript library</strong>。”</p>
<p>在那一天接下来的时间里面，Dmitry向那些愿意倾听的展示了他在Closure代码中发现的一个接一个的可怕的代码的例子。他告诉我，他最大的担忧是人们会因为Closure挂着强大的Google的招牌而从放弃一些真的很棒的例如<a href="http://jquery.com/" target="_blank"><span style="color: #1166cc;">jQuery</span></a>这样的library转而使用它。</p>
<p>“我和你做个交易吧”，我告诉他，“给我一些可怕的代码的例子，我把他们发布在SitePoint上。”</p>
<h2><span style="color: #003366;"><strong><span style="font-size: xx-large;">缓慢的循环</span></strong></span></h2>
<p>文件 <a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/array/array.js?r=2" target="_blank"><span style="color: #1166cc;">array.js</span></a>，63行:</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">for</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">i</span><span style="color: gray;"> = </span><span style="color: blue;">fromIndex</span><span style="color: gray;">; </span><span style="color: blue;">i</span><span style="color: gray;"> &lt; </span><span style="color: blue;">arr</span><span style="color: gray;">.</span><span style="color: blue;">length</span><span style="color: gray;">; </span><span style="color: blue;">i</span><span style="color: gray;">++</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
</ol>
</div>
<p>这个 for 循环每一次循环都查找了数组 (arr) 的.length 属性，简单的在开始循环的时候设置一个变量来存储这个数字，可以让循环跑得更快：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">for</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">i</span><span style="color: gray;"> = </span><span style="color: blue;">fromIndex</span><span style="color: gray;">, </span><span style="color: blue;">ii</span><span style="color: gray;"> = </span><span style="color: blue;">arr</span><span style="color: gray;">.</span><span style="color: blue;">length</span><span style="color: gray;">; </span><span style="color: blue;">i</span><span style="color: gray;"> &lt; </span><span style="color: blue;">ii</span><span style="color: gray;">; </span><span style="color: blue;">i</span><span style="color: gray;">++</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
</ol>
</div>
<p>Google的程序员们在同一个文件里面稍后的地方似乎发现了这个技巧，文件 <a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/array/array.js?r=2" target="_blank"><span style="color: #1166cc;">array.js</span></a>，153行:</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">l</span><span style="color: gray;"> = </span><span style="color: blue;">arr</span><span style="color: gray;">.</span><span style="color: blue;">length</span><span style="color: gray;">;  </span><span style="color: #ffa500;">// must be fixed during loop&#8230; see docs</span></li>
<li><span style="color: gray;">⋮</span></li>
<li><span style="color: green;">for</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">i</span><span style="color: gray;"> = </span><span style="color: blue;">l</span><span style="color: gray;"> &#8211; </span><span style="color: maroon;">1</span><span style="color: gray;">; </span><span style="color: blue;">i</span><span style="color: gray;"> &gt;= </span><span style="color: maroon;">0</span><span style="color: gray;">; &#8211;</span><span style="color: blue;">i</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
</ol>
</div>
<p>这个循环避免了在每次循环中的属性查找，但是这个for循环是如此的简单以至于它可以进一步的被简化成一个while循环，而且可以运行得更快：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">i</span><span style="color: gray;"> = </span><span style="color: blue;">arr</span><span style="color: gray;">.</span><span style="color: blue;">length</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">⋮</span></li>
<li><span style="color: green;">while</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: blue;">i</span><span style="color: gray;">&#8211;</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
</ol>
</div>
<p>但不是所有的Closure Library的效率都是由于没有优化好的循环造成的，文件 <a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/dom/dom.js?r=2" target="_blank"><span style="color: #1166cc;">dom.js</span></a>，797行：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">switch</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: blue;">node</span><span style="color: gray;">.</span><span style="color: blue;">tagName</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">APPLET</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">AREA</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">BR</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">COL</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">FRAME</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">HR</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">IMG</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">INPUT</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">IFRAME</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">ISINDEX</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">LINK</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">NOFRAMES</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">NOSCRIPT</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">META</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">OBJECT</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">PARAM</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">SCRIPT</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">case</span><span style="color: gray;"> </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">STYLE</span><span style="color: gray;">:</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">return</span><span style="color: gray;"> </span><span style="color: green;">false</span><span style="color: gray;">;</span></li>
<li><span style="color: olive;">}</span></li>
<li><span style="color: green;">return</span><span style="color: gray;"> </span><span style="color: green;">true</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>这类型的代码在Java中是相当普遍的，而且运行起来还不错。然而在JavaScript中，switch语句在每次一个程序员想要检查某个特定的HTML元素是否允许有子元素的时候都会低效的执行。</p>
<p>有经验的JavaScript程序员知道创建一个包含这个逻辑的object来做这个判断是快得多的：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">takesChildren</span><span style="color: gray;"> = </span><span style="color: olive;">{}</span></li>
<li><span style="color: blue;">takesChildren</span><span style="color: olive;">[</span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">APPLET</span><span style="color: olive;">]</span><span style="color: gray;"> = </span><span style="color: maroon;">1</span><span style="color: gray;">;</span></li>
<li><span style="color: blue;">takesChildren</span><span style="color: olive;">[</span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">dom</span><span style="color: gray;">.</span><span style="color: blue;">TagName</span><span style="color: gray;">.</span><span style="color: blue;">AREA</span><span style="color: olive;">]</span><span style="color: gray;"> = </span><span style="color: maroon;">1</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">⋮</span></li>
</ol>
</div>
<p>建立这样一个object后，检查是否某个标签接收子元素的函数将运行的快得多：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">return</span><span style="color: gray;"> !</span><span style="color: blue;">takesChildren</span><span style="color: olive;">[</span><span style="color: blue;">node</span><span style="color: gray;">.</span><span style="color: blue;">tagName</span><span style="color: olive;">]</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>这段代码可以进一步通过使用hasOwnProperty(下文有对此的详细解释)对外界干扰免疫：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">return</span><span style="color: gray;"> !</span><span style="color: blue;">takesChildren</span><span style="color: gray;">.</span><span style="color: blue;">hasOwnProperty</span><span style="color: olive;">(</span><span style="color: blue;">node</span><span style="color: gray;">.</span><span style="color: blue;">tagName</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>如果我们对Google有所期待的话，那就是执行效率了。好玩的是，Google发布了它自己的浏览器，Google Chrome，主要是为了提升JavaScript的执行效率到高一个层次！</p>
<p>看着这样的代码，我们不得不怀疑是不是Google通过培训他们自己的开发者写好一些的JavaScript代码也可以达到同样的目的。</p>
<h2><span style="color: #003366;"><strong><span style="font-size: xx-large;">漏水船中的六个月</span></strong></span></h2>
<p>说Google在构建Closure的时候忽略了开发效率是不公平的。实际上，这个library提供了一个通用的方法来缓存那些执行缓慢的函数的结果，这个方法被再次以同样的参数被调用的时候，结果会被立即返回。文件<a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/memoize/memoize.js?r=2" target="_blank"><span style="color: #1166cc;">memoize.js</span></a>，39行：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">memoize</span><span style="color: gray;"> = </span><span style="color: green;">function</span><span style="color: olive;">(</span><span style="color: blue;">f</span><span style="color: gray;">, </span><span style="color: blue;">opt_serializer</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">functionHash</span><span style="color: gray;"> = </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">getHashCode</span><span style="color: olive;">(</span><span style="color: blue;">f</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">serializer</span><span style="color: gray;"> = </span><span style="color: blue;">opt_serializer</span><span style="color: gray;"> || </span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">memoize</span><span style="color: gray;">.</span><span style="color: blue;">simpleSerializer</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">return</span><span style="color: gray;"> </span><span style="color: green;">function</span><span style="color: olive;">()</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">    </span><span style="color: #ffa500;">// Maps the serialized list of args to the corresponding return value.</span></li>
<li><span style="color: gray;">    </span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">cache</span><span style="color: gray;"> = </span><span style="color: green;">this</span><span style="color: olive;">[</span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">memoize</span><span style="color: gray;">.</span><span style="color: blue;">CACHE_PROPERTY_</span><span style="color: olive;">]</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">    </span><span style="color: green;">if</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: gray;">!</span><span style="color: blue;">cache</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">      </span><span style="color: blue;">cache</span><span style="color: gray;"> = </span><span style="color: green;">this</span><span style="color: olive;">[</span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">memoize</span><span style="color: gray;">.</span><span style="color: blue;">CACHE_PROPERTY_</span><span style="color: olive;">]</span><span style="color: gray;"> = </span><span style="color: olive;">{}</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">    </span><span style="color: olive;">}</span></li>
<li><span style="color: gray;">    </span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">key</span><span style="color: gray;"> = </span><span style="color: blue;">serializer</span><span style="color: olive;">(</span><span style="color: blue;">functionHash</span><span style="color: gray;">, </span><span style="color: blue;">arguments</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">    </span><span style="color: green;">if</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: gray;">!</span><span style="color: olive;">(</span><span style="color: blue;">key</span><span style="color: gray;"> </span><span style="color: green;">in</span><span style="color: gray;"> </span><span style="color: blue;">cache</span><span style="color: olive;">))</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">      </span><span style="color: blue;">cache</span><span style="color: olive;">[</span><span style="color: blue;">key</span><span style="color: olive;">]</span><span style="color: gray;"> = </span><span style="color: blue;">f</span><span style="color: gray;">.</span><span style="color: blue;">apply</span><span style="color: olive;">(</span><span style="color: green;">this</span><span style="color: gray;">, </span><span style="color: blue;">arguments</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">    </span><span style="color: olive;">}</span></li>
<li><span style="color: gray;">    </span><span style="color: green;">return</span><span style="color: gray;"> </span><span style="color: blue;">cache</span><span style="color: olive;">[</span><span style="color: blue;">key</span><span style="color: olive;">]</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">  </span><span style="color: olive;">}</span><span style="color: gray;">;</span></li>
<li><span style="color: olive;">}</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>这是一个被很多大型JavaScript library采用的提升执行效率的聪明技巧；问题是，Google没有提供任何的方法来限制缓存的大小！当被缓存的方法只被很少的参数组合调用的时候这是没问题的，但这个方法如果通用的话就是危险的。</p>
<p>假如缓存一个方法的参数是鼠标的坐标位置的话，这段代码的内存占用将会失去控制的飞快增长，并且拖慢浏览器的速度。</p>
<p>用Dmitry的话来说就是：“我不太清楚在Java里面这个代码风格叫什么，但在JavaScript里面，这叫‘内存泄漏’”。</p>
<h2><span style="color: #003366;"><strong><span style="font-size: xx-large;">真空中的代码</span></strong></span></h2>
<p>是在他的关于开发一个JavaScript library的讲演中，Dmitry把JavaScript的全局作用域比做一个公共厕所。“你不能避免去那里”，他说，“但是如果可以的话尽量避免表面的接触。”</p>
<p>一个通用的JavaScript library如果要是可信赖的，它不仅仅要避免影响其他任何可能在同一空间运行的JavaScript代码，它同样要保护自身不被其它不那么礼貌的代码所影响。</p>
<p>在文件<a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/object/object.js?r=2" target="_blank"><span style="color: #1166cc;">object.js</span></a>，31行：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">object</span><span style="color: gray;">.</span><span style="color: blue;">forEach</span><span style="color: gray;"> = </span><span style="color: green;">function</span><span style="color: olive;">(</span><span style="color: blue;">obj</span><span style="color: gray;">, </span><span style="color: blue;">f</span><span style="color: gray;">, </span><span style="color: blue;">opt_obj</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">for</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">key</span><span style="color: gray;"> </span><span style="color: green;">in</span><span style="color: gray;"> </span><span style="color: blue;">obj</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">    </span><span style="color: blue;">f</span><span style="color: gray;">.</span><span style="color: blue;">call</span><span style="color: olive;">(</span><span style="color: blue;">opt_obj</span><span style="color: gray;">, </span><span style="color: blue;">obj</span><span style="color: olive;">[</span><span style="color: blue;">key</span><span style="color: olive;">]</span><span style="color: gray;">, </span><span style="color: blue;">key</span><span style="color: gray;">, </span><span style="color: blue;">obj</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">  </span><span style="color: olive;">}</span></li>
<li><span style="color: olive;">}</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>像这样的for-in循环在JavaScript library中是绝对危险的，因为你不会知道有其他的什么JavaScript代码可能在页面中运行，也不知道它可能会添加一些什么东西到JavaScript标准的Object.prototype中。（stauren注：这里是Dmitry不了解Closure的整个设计理念了，看过Closure Compiler的ADVANCE模式的高侵入式压缩方法就知道，它需求整个页面上有且仅有这一段js代码，否则编译会失败）</p>
<p>Object.prototype是一个包含着所有的JavaScript object共享属性的JavaScript object。给Object.prototype添加一个方法，当前页面上每一个JavaScript object都会包含这个方法——就算这个对象之前已经被创建！早期的像<a href="http://www.prototypejs.org/" target="_blank"><span style="color: #1166cc;">Prototype</span></a>这样的JavaScript library 为Object.prototyp添加了大量各种的方便特性。</p>
<p>不幸的是，和Object.prototype中原生就有属性不一样，添加到Object.prototype的自定义属性会在任何页面上的for-in循环中被列举出来。</p>
<p>简单来说，Closure library不能与任何往Object.prototype添加特性的JavaScript代码共存。（stauren注：没错，google就是这么设计的。）</p>
<p>Google可以使用for-in循环中使用hasOwnProperty检查属性是否真的属于该object来让代码更健壮：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">object</span><span style="color: gray;">.</span><span style="color: blue;">forEach</span><span style="color: gray;"> = </span><span style="color: green;">function</span><span style="color: olive;">(</span><span style="color: blue;">obj</span><span style="color: gray;">, </span><span style="color: blue;">f</span><span style="color: gray;">, </span><span style="color: blue;">opt_obj</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">for</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">key</span><span style="color: gray;"> </span><span style="color: green;">in</span><span style="color: gray;"> </span><span style="color: blue;">obj</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">    </span><span style="color: green;">if</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: blue;">obj</span><span style="color: gray;">.</span><span style="color: blue;">hasOwnProperty</span><span style="color: olive;">(</span><span style="color: blue;">key</span><span style="color: olive;">))</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">      </span><span style="color: blue;">f</span><span style="color: gray;">.</span><span style="color: blue;">call</span><span style="color: olive;">(</span><span style="color: blue;">opt_obj</span><span style="color: gray;">, </span><span style="color: blue;">obj</span><span style="color: olive;">[</span><span style="color: blue;">key</span><span style="color: olive;">]</span><span style="color: gray;">, </span><span style="color: blue;">key</span><span style="color: gray;">, </span><span style="color: blue;">obj</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">    </span><span style="color: olive;">}</span></li>
<li><span style="color: gray;">  </span><span style="color: olive;">}</span></li>
<li><span style="color: olive;">}</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>这是另一个Closure Library中特别脆弱的部分，来自 <a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/base.js?r=2" target="_blank"><span style="color: #1166cc;">base.js</span></a>, 667行：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">isDef</span><span style="color: gray;"> = </span><span style="color: green;">function</span><span style="color: olive;">(</span><span style="color: blue;">val</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">return</span><span style="color: gray;"> </span><span style="color: blue;">val</span><span style="color: gray;"> !== </span><span style="color: blue;">undefined</span><span style="color: gray;">;</span></li>
<li><span style="color: olive;">}</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>这个函数检查一个特定的变量的值是否被定义。但如果有第三方的脚本将全局变量 undefined 设定为另一个值，它将会失效（stauren注：这是因为undefined在JavaScript中不是保留字）。只需要页面上任何一个位置有下面一行js就会把Closure Library搞崩溃：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">undefined</span><span style="color: gray;"> = </span><span style="color: maroon;">5</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>依赖全局变量 undefined 是JavaScript library作者犯的另一个菜鸟错误。</p>
<p>你也许会想，那些乱给 undefined 变量赋值的人活该他们倒霉，但修正这个错误的代价是小的：简单的在函数内声明一个本地的 undefined 变量就好了！</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">isDef</span><span style="color: gray;"> = </span><span style="color: green;">function</span><span style="color: olive;">(</span><span style="color: blue;">val</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">undefined</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">return</span><span style="color: gray;"> </span><span style="color: blue;">val</span><span style="color: gray;"> !== </span><span style="color: blue;">undefined</span><span style="color: gray;">;</span></li>
<li><span style="color: olive;">}</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p><span style="color: gray;"> </span></p>
<h2><span style="color: #003366;"><strong><span style="font-size: xx-large;">混乱的类型</span></strong></span></h2>
<p>在其他语言的开发者看来，JavaScript中最让人迷惑的部分莫过于数据类型系统了。Closure Library包含这方面大量的错误，进一步显示了作者对于JavaScript这部分细节的经验缺乏。</p>
<p>文件 <a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/string/string.js?r=2" target="_blank"><span style="color: #1166cc;">string.js</span></a>, 97行：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: #ffa500;">// We cast to String in case an argument is a Function. …</span></li>
<li><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">replacement</span><span style="color: gray;"> = </span><span style="color: teal;">String</span><span style="color: olive;">(</span><span style="color: blue;">arguments</span><span style="color: olive;">[</span><span style="color: blue;">i</span><span style="color: olive;">])</span><span style="color: gray;">.</span><span style="color: blue;">replace</span><span style="color: olive;">(</span><span style="color: gray;">…</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>这行代码使用了 String 转换函数把 arguments[i] 转换为一个字符串对象。这恐怕是做这样的一个转换的最慢的方式了，虽然对于其他语言的开发者来说这也许是最明显的办法。</p>
<p>一个快的多的方法是在你需要转换的值上面加一个空白字符串(“”):</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">replacement</span><span style="color: gray;"> = </span><span style="color: olive;">(</span><span style="color: blue;">arguments</span><span style="color: olive;">[</span><span style="color: blue;">i</span><span style="color: olive;">]</span><span style="color: gray;"> + </span><span style="color: #8b0000;">&#8220;&#8221;</span><span style="color: olive;">)</span><span style="color: gray;">.</span><span style="color: blue;">replace</span><span style="color: olive;">(</span><span style="color: gray;">…</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>下面是一个更和字符串相关的类型混乱。来自文件 <a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/base.js?r=2" target="_blank"><span style="color: #1166cc;">base.js</span></a>，742行：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">isString</span><span style="color: gray;"> = </span><span style="color: green;">function</span><span style="color: olive;">(</span><span style="color: blue;">val</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">return</span><span style="color: gray;"> </span><span style="color: green;">typeof</span><span style="color: gray;"> </span><span style="color: blue;">val</span><span style="color: gray;"> == </span><span style="color: #8b0000;">&#8216;</span><span style="color: red;">string</span><span style="color: #8b0000;">&#8216;</span><span style="color: gray;">;</span></li>
<li><span style="color: olive;">}</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>JavaScript实际上用两种方式来表现文本字符串——原生字符串类型和字符串对象：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">a</span><span style="color: gray;"> = </span><span style="color: #8b0000;">&#8220;</span><span style="color: red;">I am a string!</span><span style="color: #8b0000;">&#8220;</span><span style="color: gray;">;</span></li>
<li><span style="color: blue;">alert</span><span style="color: olive;">(</span><span style="color: green;">typeof</span><span style="color: gray;"> </span><span style="color: blue;">a</span><span style="color: olive;">)</span><span style="color: gray;">; </span><span style="color: #ffa500;">// Will output &#8220;string&#8221;</span></li>
<li><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">b</span><span style="color: gray;"> = </span><span style="color: green;">new</span><span style="color: gray;"> </span><span style="color: teal;">String</span><span style="color: olive;">(</span><span style="color: #8b0000;">&#8220;</span><span style="color: red;">I am also a string!</span><span style="color: #8b0000;">&#8220;</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
<li><span style="color: blue;">alert</span><span style="color: olive;">(</span><span style="color: green;">typeof</span><span style="color: gray;"> </span><span style="color: blue;">b</span><span style="color: olive;">)</span><span style="color: gray;">; </span><span style="color: #ffa500;">// Will output &#8220;object&#8221;</span></li>
</ol>
</div>
<p>绝大多数时候用原生字符串类型来表示字符串是更有效的（上面的变量a），但要调用任何字符串上的原生的方法（例如toLowerCase），这个变量必须先被转换成一个字符串对象（上面的变量b）。JavaScript会在需要的时候自动的在2种类型之间转换。这个特性叫做“自动装箱(autoboxing)”，在很多其他的语言中也有。</p>
<p>不幸的是，在Google的只懂Java的程序员们眼中看来，Java只将字符串表示为对象。这是我对于为什么Closure Library会忽略JavaScript中第二种类型的字符串的最靠谱的猜想。</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">b</span><span style="color: gray;"> = </span><span style="color: green;">new</span><span style="color: gray;"> </span><span style="color: teal;">String</span><span style="color: olive;">(</span><span style="color: #8b0000;">&#8220;</span><span style="color: red;">I am also a string!</span><span style="color: #8b0000;">&#8220;</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
<li><span style="color: blue;">alert</span><span style="color: olive;">(</span><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">isString</span><span style="color: olive;">(</span><span style="color: blue;">b</span><span style="color: olive;">))</span><span style="color: gray;">; </span><span style="color: #ffa500;">// Will output FALSE</span></li>
</ol>
</div>
<p>下面是另一个Java带来的类型混乱的例子。来自文件 <a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/color/color.js?r=2" target="_blank"><span style="color: #1166cc;">color.js</span></a>,  633行：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">return</span><span style="color: gray;"> </span><span style="color: olive;">[</span></li>
<li><span style="color: gray;">  </span><span style="color: teal;">Math</span><span style="color: gray;">.</span><span style="color: blue;">round</span><span style="color: olive;">(</span><span style="color: blue;">factor</span><span style="color: gray;"> * </span><span style="color: blue;">rgb1</span><span style="color: olive;">[</span><span style="color: maroon;">0</span><span style="color: olive;">]</span><span style="color: gray;"> + </span><span style="color: olive;">(</span><span style="color: maroon;">1.0</span><span style="color: gray;"> &#8211; </span><span style="color: blue;">factor</span><span style="color: olive;">)</span><span style="color: gray;"> * </span><span style="color: blue;">rgb2</span><span style="color: olive;">[</span><span style="color: maroon;">0</span><span style="color: olive;">])</span><span style="color: gray;">,</span></li>
<li><span style="color: gray;">  </span><span style="color: teal;">Math</span><span style="color: gray;">.</span><span style="color: blue;">round</span><span style="color: olive;">(</span><span style="color: blue;">factor</span><span style="color: gray;"> * </span><span style="color: blue;">rgb1</span><span style="color: olive;">[</span><span style="color: maroon;">1</span><span style="color: olive;">]</span><span style="color: gray;"> + </span><span style="color: olive;">(</span><span style="color: maroon;">1.0</span><span style="color: gray;"> &#8211; </span><span style="color: blue;">factor</span><span style="color: olive;">)</span><span style="color: gray;"> * </span><span style="color: blue;">rgb2</span><span style="color: olive;">[</span><span style="color: maroon;">1</span><span style="color: olive;">])</span><span style="color: gray;">,</span></li>
<li><span style="color: gray;">  </span><span style="color: teal;">Math</span><span style="color: gray;">.</span><span style="color: blue;">round</span><span style="color: olive;">(</span><span style="color: blue;">factor</span><span style="color: gray;"> * </span><span style="color: blue;">rgb1</span><span style="color: olive;">[</span><span style="color: maroon;">2</span><span style="color: olive;">]</span><span style="color: gray;"> + </span><span style="color: olive;">(</span><span style="color: maroon;">1.0</span><span style="color: gray;"> &#8211; </span><span style="color: blue;">factor</span><span style="color: olive;">)</span><span style="color: gray;"> * </span><span style="color: blue;">rgb2</span><span style="color: olive;">[</span><span style="color: maroon;">2</span><span style="color: olive;">])</span></li>
<li><span style="color: olive;">]</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>以上的那些 1.0 说明了问题。像Java这样的语言用 代表整形数据使用的(1)与代表浮点数据的(1.0)是不一样的。但在JavaScript中，数字类型就是数字类型。(1 – factor)一样会运行得很好。</p>
<p>另一个有着Java味道的JavaScript代码的例子可以在 <a href="http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/fx/fx.js?r=2" target="_blank"><span style="color: #1166cc;">fx.js</span></a> 中找到，465行：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: blue;">goog</span><span style="color: gray;">.</span><span style="color: blue;">fx</span><span style="color: gray;">.</span><span style="color: blue;">Animation</span><span style="color: gray;">.</span><span style="color: blue;">prototype</span><span style="color: gray;">.</span><span style="color: blue;">updateCoords_</span><span style="color: gray;"> = </span><span style="color: green;">function</span><span style="color: olive;">(</span><span style="color: blue;">t</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">coords</span><span style="color: gray;"> = </span><span style="color: green;">new</span><span style="color: gray;"> </span><span style="color: teal;">Array</span><span style="color: olive;">(</span><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">startPoint</span><span style="color: gray;">.</span><span style="color: blue;">length</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">  </span><span style="color: green;">for</span><span style="color: gray;"> </span><span style="color: olive;">(</span><span style="color: green;">var</span><span style="color: gray;"> </span><span style="color: blue;">i</span><span style="color: gray;"> = </span><span style="color: maroon;">0</span><span style="color: gray;">; </span><span style="color: blue;">i</span><span style="color: gray;"> &lt; </span><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">startPoint</span><span style="color: gray;">.</span><span style="color: blue;">length</span><span style="color: gray;">; </span><span style="color: blue;">i</span><span style="color: gray;">++</span><span style="color: olive;">)</span><span style="color: gray;"> </span><span style="color: olive;">{</span></li>
<li><span style="color: gray;">    </span><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">coords</span><span style="color: olive;">[</span><span style="color: blue;">i</span><span style="color: olive;">]</span><span style="color: gray;"> = </span><span style="color: olive;">(</span><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">endPoint</span><span style="color: olive;">[</span><span style="color: blue;">i</span><span style="color: olive;">]</span><span style="color: gray;"> &#8211; </span><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">startPoint</span><span style="color: olive;">[</span><span style="color: blue;">i</span><span style="color: olive;">])</span><span style="color: gray;"> * </span><span style="color: blue;">t</span><span style="color: gray;"> +</span></li>
<li><span style="color: gray;">    </span><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">startPoint</span><span style="color: olive;">[</span><span style="color: blue;">i</span><span style="color: olive;">]</span><span style="color: gray;">;</span></li>
<li><span style="color: gray;">  </span><span style="color: olive;">}</span></li>
<li><span style="color: olive;">}</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>看到第二行里面他们是怎么构造一个数组的吗？</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">coords</span><span style="color: gray;"> = </span><span style="color: green;">new</span><span style="color: gray;"> </span><span style="color: teal;">Array</span><span style="color: olive;">(</span><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">startPoint</span><span style="color: gray;">.</span><span style="color: blue;">length</span><span style="color: olive;">)</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>虽然在Java中这是必须的，但在JavaScript中在运行前指定数组的长度是完全没有意义的。这就和使用 var i = new Number(0); 而不是 var i=0; 来新建一个存储数字用的变量一样没有意义。</p>
<p>实际上，你可以只是简历一个空白的数组，让它自己在被填入值的时候自己变大。这样做代码不但更短，运行得也更快：</p>
<div class="hl-surround">
<ol class="hl-main ln-show" title="Double click to hide line number." ondblclick="linenumber(this)">
<li class="hl-firstline"><span style="color: green;">this</span><span style="color: gray;">.</span><span style="color: blue;">coords</span><span style="color: gray;"> = </span><span style="color: olive;">[]</span><span style="color: gray;">;</span></li>
</ol>
</div>
<p>啊，你们有没有注意到这个函数里面还有另外一个效率低下的for循环呢？</p>
<h2><span style="color: #003366;"><strong><span style="font-size: xx-large;">API 设计</span></strong></span></h2>
<p>如果所有这些底层的代码质量缺陷还不能让你信服，我觉得你应该试试Google在Closure Library中包含的一些API。</p>
<p>例如Closure里面的图形类(<a href="http://closure-library.googlecode.com/svn/trunk/closure/goog/docs/closure_goog_graphics_graphics.js.html" target="_blank"><span style="color: #1166cc;">graphics classes</span></a>)，是以HTML5 canvas API为基础构建的，你应该很奇怪为什么一个JavaScript API会以一个HTML标准来设计。简单来说，这是冗余、低效的，完全比不上同类代码。</p>
<p>作为<a href="http://raphaeljs.com/" target="_blank"><span style="color: #1166cc;">Raphaël</span></a> 和 <a href="http://g.raphaeljs.com/" target="_blank"><span style="color: #1166cc;">gRaphaël</span></a> 的作者，Dmitry在设计可用的JavaScript API方面相当有经验。如果你想感受一下canvas API的全部恐怖（当然，Closure的图形API也有所贡献），看看<a href="http://www.webdirections.org/resources/dmitry-baranovskiy-canvas/" target="_blank"><span style="color: #1166cc;">Dmitry在Web Directions South 2009讲演</span></a>上面关于这个话题的音频和ppt吧。</p>
<h2><span style="color: #003366;"><strong><span style="font-size: xx-large;">Goolgle对于代码质量的责任</span></strong></span></h2>
<p>到这个时候我想你应该确信了在网上的最好的JavaScript代码中，Closure Library不是一个闪闪发光的明星了。如果你想找的是这样的代码，我可以向你你推荐一下更声名远扬的就像<a href="http://jquery.com/" target="_blank"><span style="color: #1166cc;">jQuery</span></a>这样的library吗？</p>
<p>但你也许会想“这又怎么样？Google想发布什么垃圾代码就发布什么垃圾代码——又没人强迫你用它。”如果这是一个某google员工以自己名义发布的个人项目，我同意这个观点，但Google通过给Closure Library打上Google 商标的行为认可了它。</p>
<p>事实上，程序员们会因为 Closure Library 有着Google的名字而使用它，这就真的是一个杯具了。你喜欢也罢不喜欢也罢，Google在开发社区中是一个被信任的名字，所以Google应该抱着对开发社区负责的态度，在决定像Closure这样的library是否值得向公众曝光之前好好的自己检查一下。</p>
<p>—</p>
<p>译者注：说it sucks总是很容易，Closure自然有种种的不足，不过完全没有抹杀它为JavaScript界带来的一些新想法，包括强大的Google Compiler。要完全的了解一个东西，最好各方的想法都看一看，如下：</p>
<ul style="padding-left: 20px; margin: 5px 0px 15px;">
<li style="margin: 0px;"><a href="http://erik.eae.net/archives/2009/11/05/22.27.29/"><span style="color: #1166cc;">Erik Arvidsson, founder of Closure, awesome JS hackers, on the history</span></a></li>
<li style="margin: 0px;"><a href="http://alex.dojotoolkit.org/2009/11/a-bit-of-closure/"><span style="color: #1166cc;">Alex Russell, founder of Dojo and Chrome Frame</span></a></li>
<li style="margin: 0px;"><a href="http://www.slideshare.net/Dmitry.Baranovskiy/your-javascript-library"><span style="color: #1166cc;">Dimitry presentation on JavaScript libraries</span></a></li>
<li style="margin: 0px;"><a href="http://caprazzi.net/posts/learning-closure-ajax-with-goog-net/"><span style="color: #1166cc;">Learning Closure with goog.net</span></a></li>
<li style="margin: 0px;"><a href="http://bolinfest.com/javascript/inheritance.php"><span style="color: #1166cc;">Inheritance Patterns in JavaScript</span></a></li>
<li style="margin: 0px;"><a href="http://almaer.com/blog/getting-closure-dont-just-use-it-dont-just-abuse-it" target="_blank"><span style="color: #1166cc;">Getting Closure: Don’t just use it, don’t just abuse it</span></a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/80/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mootools Sortables插件BUG修正</title>
		<link>http://www.zfkun.com/blog/index.php/archives/67</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/67#comments</comments>
		<pubDate>Sun, 17 Jan 2010 03:04:04 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[drag]]></category>
		<category><![CDATA[mootools]]></category>
		<category><![CDATA[preventDefault]]></category>
		<category><![CDATA[sortables]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=67</guid>
		<description><![CDATA[     前阵子新版专辑管理项目开发中碰到比较郁闷的问题，经测试最终决定给Sortables打个小补丁。

    由于对选择状态的文本内容、连接和图片等进行拖放操作会触发系统的默认动作，例如ie中拖动图片鼠标会变成禁止操作状态，这样会导致这个拖放程序执行失败，所以Mootools 的Drag基类配置里其实有preventDefault配置项来给使用者解决此类问题，默认值为false，需要处理时设置为true即可。
    然而郁闷的是，开发时才发现，Sortables类里内部创建的Drag.Move实例却只能使用preventDefault的默认值，因为Sortables没有给出配置项用来设置内部(this.drag)这个DragMove实例的preventDefault参数项。无奈之下，只能打补丁了。
    另打补丁期间又发现另一个问题，甚是郁闷。Sortables的start方法仅判断是否空闲进而决定是否真正执行。而实际项目需求有时需要屏蔽鼠标右键(带来的问题很多)，而看了办法发现，找到的唯一办法仍旧是打补丁。
    最终，打上2处补丁后，世界清静了。。。]]></description>
			<content:encoded><![CDATA[<p>      前阵子新版专辑管理项目开发中碰到比较郁闷的问题，经测试最终决定给Sortables打个小补丁。</p>
<p>      由于对选择状态的文本内容、连接和图片等进行拖放操作会触发系统的默认动作，例如ie中拖动图片鼠标会变成禁止操作状态，这样会导致这个拖放程序执行失败，所以Mootools 的Drag基类配置里其实有preventDefault配置项来给使用者解决此类问题，默认值为false，需要处理时设置为true即可。</p>
<p>      然而郁闷的是，开发时才发现，Sortables类里内部创建的Drag.Move实例却只能使用preventDefault的默认值，因为Sortables没有给出配置项用来设置内部(this.drag)这个DragMove实例的preventDefault参数项。无奈之下，只能打补丁了。</p>
<p>      另打补丁期间又发现另一个问题，甚是郁闷。Sortables的start方法仅判断是否空闲进而决定是否真正执行。而实际项目需求有时需要屏蔽鼠标右键(带来的问题很多)，而看了办法发现，找到的唯一办法仍旧是打补丁。</p>
<p>      最终，打上2处补丁后，世界清静了。。。</p>
<p>源代码：</p>
<pre name="code" class="js">
var Sortables = new Class({

 Implements: [Events, Options],

 options: {/*
      onSort: $empty(element, clone),
     onStart: $empty(element, clone),
     onComplete: $empty(element),*/
     snap: 4,
     opacity: 1,
     clone: false,
     revert: false,
     handle: false,
     constrain: false
 },

……

start: function(event, element){
    if (!this.idle) return;
    this.idle = false;
    this.element = element;
    this.opacity = element.get('opacity');
    this.list = element.getParent();
    this.clone = this.getClone(event, element);

    this.drag = new Drag.Move(this.clone, {
       snap: this.options.snap,
       container: this.options.constrain &amp;&amp; this.element.getParent(),
       droppables: this.getDroppables(),
       onSnap: function(){
           event.stop();
           this.clone.setStyle('visibility', 'visible');
           this.element.set('opacity', this.options.opacity || 0);
           this.fireEvent('start', [this.element, this.clone]);
       }.bind(this),
       onEnter: this.insert.bind(this),
       onCancel: this.reset.bind(this),
       onComplete: this.end.bind(this)
  });

  this.clone.inject(this.element, 'before');
  this.drag.start(event);
 },

……
</pre>
<p>补丁后:</p>
<pre name="code" class="js">
var Sortables = new Class({

 Implements: [Events, Options],

 options: {/*
      onSort: $empty(element, clone),
     onStart: $empty(element, clone),
     onComplete: $empty(element),*/
     snap: 4,
     opacity: 1,
     clone: false,
     revert: false,
     handle: false,
     constrain: false,
     rightClickAble: false,  // add by myself
     preventDefault:false   // add by myself
 },

……

start: function(event, element){
    if (!this.idle || (!this.options.rightClickAble &amp;&amp; event.rightClick)) return;  // modified by myself
    this.idle = false;
    this.element = element;
    this.opacity = element.get('opacity');
    this.list = element.getParent();
    this.clone = this.getClone(event, element);

    this.drag = new Drag.Move(this.clone, {
       snap: this.options.snap,
       container: this.options.constrain &amp;&amp; this.element.getParent(),
       droppables: this.getDroppables(),
       preventDefault:!!this.options.preventDefault,  // add by myself
       onSnap: function(){
           event.stop();
           this.clone.setStyle('visibility', 'visible');
           this.element.set('opacity', this.options.opacity || 0);
           this.fireEvent('start', [this.element, this.clone]);
       }.bind(this),
       onEnter: this.insert.bind(this),
       onCancel: this.reset.bind(this),
       onComplete: this.end.bind(this)
  });

  this.clone.inject(this.element, 'before');
  this.drag.start(event);
 },

……
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/67/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>绝对震撼!10款动感图片展示js代码</title>
		<link>http://www.zfkun.com/blog/index.php/archives/55</link>
		<comments>http://www.zfkun.com/blog/index.php/archives/55#comments</comments>
		<pubDate>Sat, 16 Jan 2010 15:33:12 +0000</pubDate>
		<dc:creator>影之迷惑</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[动感]]></category>
		<category><![CDATA[图片]]></category>

		<guid isPermaLink="false">http://www.zfkun.com/blog/?p=55</guid>
		<description><![CDATA[绝对震撼!10款动感图片展示js代码
http://pcedu.pconline.com.cn/empolder/wz/javascript/0809/1421645.html
]]></description>
			<content:encoded><![CDATA[<p>绝对震撼!10款动感图片展示js代码</p>
<p><a href="http://pcedu.pconline.com.cn/empolder/wz/javascript/0809/1421645.html">http://pcedu.pconline.com.cn/empolder/wz/javascript/0809/1421645.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.zfkun.com/blog/index.php/archives/55/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
