<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel>
    <title>前端表现 - 老赵点滴 - 追求编程之美</title>
    <link>http://blog.zhaojie.me/front-end/</link>
    <description>先做人，再做技术人员，最后做程序员。打造国内最好的.NET技术博客。</description>
    <language>zh-cn</language>
    <managingEditor>jeffz@live.com (老赵)</managingEditor>
    <webMaster>jeffz@live.com (老赵)</webMaster>
    <pubDate>Wed, 27 Apr 2011 15:20:17 GMT</pubDate>
    <lastBuildDate>Wed, 27 Apr 2011 15:20:17 GMT</lastBuildDate>
    <ttl>60</ttl>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>受禁锢的异步编程思维</title>
      <link>http://blog.zhaojie.me/2011/12/the-stuck-mind-of-asynchronous-programming.html</link>
      <guid>http://blog.zhaojie.me/2011/12/the-stuck-mind-of-asynchronous-programming.html</guid>
      <description>&lt;p&gt;最近一直在努力推广&lt;a href="http://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;，补充了很多中文文档和示例，因此博客上都已经有两篇文章有了“上”而没有“下”，即使最复杂的图示也已经绘制完毕。在推广Jscex的过程中，我发现有个比较明显的问题是，许多使用JavaScript的程序员已经习惯旧有的编程方式，甚至推崇一些据他们说很“漂亮”的模式。但在我看来，这其实跟许多GoF模式是在修补OO语言的不足有些类似，很多异步模式都只是因为JavaScript语言特性不足而设计出来的“权宜之计”。我们在传统JavaScript编程环境下并没有其他选择，单纯地认为这是“美”，说实话只不过是一种安慰罢了。&lt;/p&gt;

&lt;p&gt;Jscex的重头戏便是处理异步操作，但异步操作并不只是如Node.js中通过回调函数传回结果的那些方法，或者是网页上的AJAX请求等等。异步操作的定义其实可以概括成“&lt;span style="color:#ff0000"&gt;会在未来某个时刻完成的操作&lt;/span&gt;”，就只是这么简单。什么事情会在未来发生，那么它对你来说就是个异步操作。因此其实在日常开发过程中可谓到处是异步操作，例如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;播放动画（播放会在未来结束） &lt;/li&gt;

  &lt;li&gt;模态对话框关闭（模态对话框会在未来关闭） &lt;/li&gt;

  &lt;li&gt;用户操作（用户会在未来点击按钮） &lt;/li&gt;

  &lt;li&gt;各类事件（数据流会在未来关闭，WebWorker会在未来获得消息，图片会在未来加载成功） &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些示例实在数不胜数。但是，在许多JavaScript程序员眼中，似乎只有AJAX或是Node.js中的那些异步方法才算是异步操作。其他的东西，比如用户点击一个按钮，这难道不是个天然的“事件”吗？其实这就要视这个异步任务的性质如何了。如果它是一系列操作的“发起者”，那么的确，使用事件触发的方式来对待这次点击操作可能是最合理的。但如果，这个操作只是一系列过程中的一个步骤，那么如果依然把它视为一个事件型的操作，就只会破坏我们的逻辑了。&lt;/p&gt;

&lt;p&gt;举个例子，和Jscex的&lt;a href="https://github.com/JeffreyZhao/jscex/blob/master/README-cn.md"&gt;快速入门&lt;/a&gt;比较类似，即&lt;a href="http://en.wikipedia.org/wiki/Fibonacci_number"&gt;菲薄纳契（Fibonacci）数列&lt;/a&gt;：&lt;/p&gt;
&lt;img class="embed" title="F_n = F_{n-1} + F_{n - 2}" src="http://latex.codecogs.com/gif.latex?F_n = F_{n-1} + F_{n - 2}" /&gt; 

&lt;p&gt;其边界情况为：&lt;/p&gt;
&lt;img class="embed" title="F_0 = 0, F_1 = 1" src="http://latex.codecogs.com/gif.latex?F_0 = 0, F_1 = 1" /&gt; 

&lt;p&gt;以上是其标准定义，直接写成算法即是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;fib = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {

    console.log(0);
    console.log(1);

    &lt;span style="color: blue"&gt;var &lt;/span&gt;a = 0, current = 1;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;b = a;
        a = current;
        current = a + b;

        console.log(current);
    }
};&lt;/pre&gt;

&lt;p&gt;上述代码将会无限地循环下去，不断输出数列的每一项。快速入门里的要求，是将其修改为“每隔一秒输出一个数字”，于是有同学就说：这不天生是计时器的场景吗？但事实并非如此。“计时器”或是setTimeout函数，都只是环境提供给我们的唯一可用的功能，我们要意识到这不是我们主动的“选择”。如果一看到“每隔一秒”这样的需求，JavaScript程序员就认为“计时器”是“最好”的办法，这就说明思维被禁锢了。我相信这样的功能交给其他任何平台的程序员，他们的第一感觉几乎都会是“使用Sleep函数暂停一秒”。这其实才是最简单的做法，直接，清晰，完整保留现有代码逻辑。&lt;/p&gt;

&lt;p&gt;这也是基于Jscex之后的实现方式。这里我再将要求修改一下，改为用户“每点击一次按钮”输出一个数字，又该怎么做？基于Jscex的做法如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;Async = Jscex.Async;

&lt;span style="color: blue"&gt;var &lt;/span&gt;fibAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;button = document.getElementById(&lt;span style="color: maroon"&gt;&amp;quot;button&amp;quot;&lt;/span&gt;);

    $await(Async.onEvent(button, &lt;span style="color: maroon"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;)); &lt;span style="color: #006400"&gt;// 等待用户点击
    &lt;/span&gt;console.log(0);

    $await(Async.onEvent(button, &lt;span style="color: maroon"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;)); &lt;span style="color: #006400"&gt;// 等待用户点击
    &lt;/span&gt;console.log(1);

    &lt;span style="color: blue"&gt;var &lt;/span&gt;a = 0, current = 1;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;b = a;
        a = current;
        current = a + b;

        $await(Async.onEvent(button, &lt;span style="color: maroon"&gt;&amp;quot;click&amp;quot;&lt;/span&gt;)); &lt;span style="color: #006400"&gt;// 等待用户点击
        &lt;/span&gt;console.log(current);
    }
}));

fibAsync().start();&lt;/pre&gt;

&lt;p&gt;有朋友可能会问：用户点击按钮不是需要响应事件的嘛，这个事件到哪里去了？其实正像我所说的那样，把这里的“用户点击按钮”当作事件对待并非最合理的方式，因为它只是“整个过程”中的一个环节而已。在这里，我们其实只是要在输出数字之前“等待用户点击”即可，这个“输出”以及相关的“计算”操作，并非是由“按钮点击”所触发的逻辑，而是一个连续的统一过程中的一部分而已。&lt;/p&gt;

&lt;p&gt;您可以试试纯粹使用事件机制来实现这个功能，保证您需要重新实现这段斐波那契数列的逻辑。当然，菲薄纳契数列的逻辑很简单，重写下估计也不会花太大的功夫，但如果您需要改造&lt;a href="http://files.zhaojie.me/jscex/samples/async/hanoi.html"&gt;汉诺塔的动画效果&lt;/a&gt;呢？&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;hanoiAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(n, from, to, mid) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(n &amp;gt; 0) {
        $await(hanoiAsync(n - 1, from, mid, to));
    }

    &lt;span style="color: #006400"&gt;// 等待按钮点击
    // var btnNext = document.getElementById(&amp;quot;btnNext&amp;quot;);
    // $await(Jscex.Async.onEvent(btnNext, &amp;quot;click&amp;quot;));

    &lt;/span&gt;$await(moveDishAsync(n, from, to));

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(n &amp;gt; 0) {
        $await(hanoiAsync(n - 1, mid, to, from));
    }
}));&lt;/pre&gt;

&lt;p&gt;以上代码是以动画形式表现汉诺塔的解题过程，但如果用户提出想要“每点一次按钮”才移动一个盘子，那其实我们只要将上面两行代码取消注释即可。如果忽然有一天，老板要求通过一个选项来决定是否“自动移动”，在Jscex里只要加一个if判断即可。您可以简单设想一下直接裸写这些代码会遇到什么样的景象，改造时会遇到哪些困难。&lt;/p&gt;

&lt;p&gt;我还为Jscex准备了一个示例，是关于“&lt;a href="https://github.com/JeffreyZhao/jscex/blob/master/doc/async/samples/modal-dialog-cn.md"&gt;模态对话框&lt;/a&gt;”配合相关异步操作的。由于是“模态对话框”，我们是要在对话框关闭之后才继续做某些事情。可惜在JavaScript中，如果您直接把一个界面元素展现为一个模态对话框，它是无法阻止后面的代码继续执行的，要阻止则只能使用confirm或alert方法。于是，我们只能把后续操作放到一个回调函数中去，并在模态对话框关闭之后才执行。但是您要知道，模态对话框只不过是整个过程中的一个步骤，理想状况下我们的完整功能不该被拆成多个部分，再使用所谓“美妙”的回调串联起来。&lt;/p&gt;

&lt;p&gt;这点在Jscex中还是那么简单，直接按最简单的逻辑来进行即可：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// 显示模态对话框&lt;/span&gt;
$await($(&lt;span style="color: maroon"&gt;&amp;quot;#dialog-confirm&amp;quot;&lt;/span&gt;).dialogAsync({ modal: &lt;span style="color: blue"&gt;true &lt;/span&gt;}));

&lt;span style="color: #006400"&gt;// 发起AJAX请求&lt;/span&gt;
&lt;span style="color:blue"&gt;var&lt;/span&gt; response = $await($.ajaxAsync({ url: &lt;span style="color: maroon"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;, dataType: &lt;span style="color: maroon"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; }));

&lt;span style="color: #006400"&gt;// 继续做事&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;而无需：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// 继续做事&lt;/span&gt;
$(&lt;span style="color: maroon"&gt;&amp;quot;#dialog-confirm&amp;quot;&lt;/span&gt;).dialog({
    modal: &lt;span style="color: blue"&gt;true&lt;/span&gt;,
    close: &lt;span style="color: blue"&gt;function&lt;/span&gt; () {
        &lt;span style="color: #006400"&gt;// 发起AJAX请求&lt;/span&gt;
        $.ajax({
            url: &lt;span style="color: maroon"&gt;&amp;quot;...&amp;quot;&lt;/span&gt;,
            dataType: &lt;span style="color: maroon"&gt;&amp;quot;text&amp;quot;&lt;/span&gt;,
            success: &lt;span style="color: blue"&gt;function&lt;/span&gt; () {
                &lt;span style="color: #006400"&gt;// 继续做事&lt;/span&gt;
            }
        });
});&lt;/pre&gt;

&lt;p&gt;经常会听到有些朋友谈起，说在实际开发过程中很少遇到异步场景。但在我看来，实在可谓遍地是异步，这种观念的差别只是在于是否经过了“抽象”。不加抽象地使用技术平台为我们提供的异步操作，会让我们的思维被它所禁锢。在JavaScript编程中浸淫太久了，可能就会忘记我们从最初是如何编程的。Jscex的目标，便是将这些东西回归自然，将逻辑以最自然的方式表达出来。循环？那就用for或是while吧，在函数之间跳来跳去是做什么的？&lt;/p&gt;

&lt;p&gt;我从来不担心的Jscex的实用价值。Jscex来自C#，F#以及Scala等现成的理念，各种开发模式都是被翻来覆去讨论过，总结过，验证过的。这些语言其实都能实现与JavaScript类似的编程模式，但它们不需要，因为语言特性让程序员可以使用更简单直接的做法来解决问题。Jscex只是将这些现成的内容，从其他模式带到JavaScript编程领域上而已。&lt;/p&gt;

&lt;p&gt;如今我唯一担心的，只是那些被禁锢的编程思维。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/12/the-stuck-mind-of-asynchronous-programming.html#comments</comments>
      <pubDate>Mon, 19 Dec 2011 13:49:37 GMT</pubDate>
      <lastBuildDate>Tue, 27 Dec 2011 15:17:21 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>基于Node.js、Express和Jscex开发的ToDo网站示例</title>
      <link>http://blog.zhaojie.me/2011/07/nodejs-express-jscex-demo-website-todo.html</link>
      <guid>http://blog.zhaojie.me/2011/07/nodejs-express-jscex-demo-website-todo.html</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/JeffreyZhao/jscex" target="_blank"&gt;Jscex&lt;/a&gt;的主要使用场景是“JavaScript异步编程”，不过并没有限制是跑在浏览器还是服务器端。最近&lt;a href="http://nodejs.org/" target="_blank"&gt;Node.js&lt;/a&gt;很火热，也刚发布了原生的Windows版，不少同学会用它来做一些网站这样的小程序。目前用Node.js开发网站最著名的框架是&lt;a href="http://expressjs.com/" target="_blank"&gt;Express&lt;/a&gt;，使用起来也是比较容易的。前段时间看到&lt;a href="http://cnodejs.org/blog/?p=1310" target="_blank"&gt;CNodeJS社区的一篇文章&lt;/a&gt;，有同学将一个&lt;a href="http://simple-is-better.com/news/309" target="_blank"&gt;Python写的ToDo列表网站&lt;/a&gt;移植到了Node.js上，我为了推广Jscex，就fork了这个项目，将其修改为&lt;a href="https://github.com/JeffreyZhao/todo" target="_blank"&gt;基于Jscex的版本&lt;/a&gt;，大伙儿可以来比较一下。当然这个网站过于简单，我也正在寻找更合适的项目。&lt;/p&gt;

&lt;p&gt;JavaScript是一个没有阻塞特性的语言，因此各类API都会设计为异步，这对于服务器的伸缩性和客户端网页的响应能力都有好处，不过在程序编写上就会遇到各种问题了。例如在ToDo示例中的一个简单的处理函数，因为需要查询数据库，就要写成带回调的样子：&lt;/p&gt;

&lt;pre class="code"&gt;exports.index = &lt;span style="color: blue"&gt;function &lt;/span&gt;(req, res, next) {
    db.query(&lt;span style="color: maroon"&gt;'select * from todo order by finished asc, id asc limit 50'&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(err, rows) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) &lt;span style="color: blue"&gt;return &lt;/span&gt;next(err);
        res.render(&lt;span style="color: maroon"&gt;'index'&lt;/span&gt;, { todos: rows });
    });
};&lt;/pre&gt;

&lt;p&gt;db变量用来操作MySQL数据库，它的query方法传入sql（可能还会有参数）并提供一个回调函数，用来提示错误或是返回查询结果。在回调中我们必须判断err是否存在，如果存在便调用next报告框架“出错了”。每个异步操作都必须如此，试想如果在这个查询后还有另一个查询，则还需要进行一次嵌套和err判断。每个处理函数都是如此，这也是异步编程的烦恼之一：难以进行统一的异常处理，处理代码总是需要分散在各处，一不小心就变成“野异常”，还很难排查出来。&lt;/p&gt;

&lt;p&gt;我将ToDo网站简单地Jscex化了一下。首先让MySQL的查询能够接入Jscex（lib\jscex.mysql.js）：&lt;/p&gt;

&lt;pre class="code"&gt;exports.jscexify = &lt;span style="color: blue"&gt;function &lt;/span&gt;(db) {
    db.queryAsync = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;_this = &lt;span style="color: blue"&gt;this&lt;/span&gt;;

        &lt;span style="color: blue"&gt;var &lt;/span&gt;args = [];
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; arguments.length; i++) {
            args.push(arguments[i]);
        }

        &lt;span style="color: blue"&gt;var &lt;/span&gt;delegate = {
            onStart: &lt;span style="color: blue"&gt;function &lt;/span&gt;(callback) {

                args.push(&lt;span style="color: blue"&gt;function &lt;/span&gt;(err, result) {
                    &lt;span style="color: blue"&gt;if &lt;/span&gt;(err) {
                        callback(&lt;span style="color: maroon"&gt;&amp;quot;failure&amp;quot;&lt;/span&gt;, err);
                    } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
                        callback(&lt;span style="color: maroon"&gt;&amp;quot;success&amp;quot;&lt;/span&gt;, result);
                    }
                });

                _this.query.apply(_this, args);
            }
        };

        &lt;span style="color: blue"&gt;return new &lt;/span&gt;Jscex.Async.Task(delegate);
    }
}&lt;/pre&gt;

&lt;p&gt;一般来说，将一个异步接口给Jscex化并不需要那么多代码（最关键的其实只是onStart函数）。这里近30行代码，其中大部分是为了支持“变长”参数，因此queryAsync函数会保留调用时的所有参数，补上一个callback，再去调用query函数本身。此时，便可以去改写之前的index等处理函数了（controllers\todo.js），例如：&lt;/p&gt;

&lt;pre class="code"&gt;exports.index = toHandler(eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(req, res) {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;todos = $await(db.queryAsync(&lt;span style="color: maroon"&gt;'select * from todo order by finished asc, id asc limit 50'&lt;/span&gt;));
    res.render(&lt;span style="color: maroon"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;, { todos: todos });

})));&lt;/pre&gt;

&lt;p&gt;toHandler函数的作用，是将一个“接受req和res，返回Task”的函数，封装成标准的“接受req、res和next三个参数”的处理函数，并提供统一的错误处理：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;toHandler = &lt;span style="color: blue"&gt;function &lt;/span&gt;(asyncFunc) {
    &lt;span style="color: blue"&gt;return function &lt;/span&gt;(req, res, next) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;task = asyncFunc(req, res);
        task.addListener(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(task.status == &lt;span style="color: maroon"&gt;&amp;quot;failed&amp;quot;&lt;/span&gt;) {
                next(task.error);
            }
        });
        task.start();
    }
}&lt;/pre&gt;

&lt;p&gt;我在todo.js里保留了原有各个处理函数的实现，感兴趣的朋友可以对比一下它们之前的差别。可惜的是，由于ToDo实在过于简单，Jscex的优势并没有表现出来太多。例如，每个处理程序中只有一个MySQL查询，没有判断和循环，更别说为了充分利用IO并发能力，从而组合多个异步函数了。因此，我最近也一直在寻找更复杂一些的示例，不过似乎用Express的开源网站并不多见，我几乎都想自己写一个了。目前感觉&lt;a href="https://github.com/alexyoung/nodepad" target="_blank"&gt;Nodepad&lt;/a&gt;似乎还算不错，接下来可能会对它下手。&lt;/p&gt;

&lt;p&gt;ToDo网站依赖Express，ejs和MySQL驱动，同时我把Jscex作为添加为它的子模块。如果您要&lt;a href="https://github.com/JeffreyZhao/todo" target="_blank"&gt;克隆一份ToDo的代码&lt;/a&gt;把玩一番，可以：&lt;/p&gt;

&lt;pre class="code"&gt;&amp;gt; git clone git://github.com/JeffreyZhao/todo.git
&amp;gt; cd todo
&amp;gt; git submodule init
&amp;gt; git submodule update
&amp;gt; npm install express ejs mysql
&amp;gt; node server.js&lt;/pre&gt;

&lt;p&gt;从现在开始，我会在InfoQ中文站上发表一系列关于Jscex的文章，既有关于浏览器端的JavaScript开发，也有在服务器端利用Node.js开发的内容。可能您目前还可能会有所疑惑，例如为什么要使用危险的eval函数，eval和Jscex.compile函数不能封装起来吗？其实在看了我的文章并对Jscex有了基本了解之后，就会发现这些都是以“传统眼光”来看待Jscex时所形成的误解。Jscex的做法的确“另辟蹊径”，否则在JavaScript异步类库已经多如牛毛的情况下，我不知如何让它脱颖而出。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/07/nodejs-express-jscex-demo-website-todo.html#comments</comments>
      <pubDate>Fri, 15 Jul 2011 06:33:10 GMT</pubDate>
      <lastBuildDate>Fri, 15 Jul 2011 08:57:40 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>JavaScript：假如default不是switch的最后一项</title>
      <link>http://blog.zhaojie.me/2011/05/javascript-when-break-is-not-the-last-choice-of-switch.html</link>
      <guid>http://blog.zhaojie.me/2011/05/javascript-when-break-is-not-the-last-choice-of-switch.html</guid>
      <description>&lt;p&gt;话说大家对于switch语句应该再熟悉不过了，各种类C语言都不例外，JavaScript自然也是如此。switch的逻辑很简单，根据switch内容的值执行对应的case项，否则执行default项即可。但是不同的语言在具体一些细节上面的处理却是不同的。例如在JavaScript里，每个case项都可以没有break，于是语句便会顺延到下个case或是default里面去——但某些语言设计者认为这种特性容易造成代码理解上的偏差，因此比如在C#里便要求每个非空的case都要有个break。那么再来一个细节问题：如果default之后还有case，那么会出现什么样的情况？如果default里没有break呢？&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;switch &lt;/span&gt;(a) {
    &lt;span style="color: blue"&gt;case &lt;/span&gt;0:
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;);
    &lt;span style="color: blue"&gt;default&lt;/span&gt;:
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;);
    &lt;span style="color: blue"&gt;case &lt;/span&gt;1:
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;);
}&lt;/pre&gt;

&lt;p&gt;就好比这段代码，当a等于0、1或2的时候，将会输出什么样的内容呢？先猜猜，别急着往下看。&lt;/p&gt;

&lt;p&gt;当a等于0时，则会输出：&lt;/p&gt;

&lt;pre class="code"&gt;0
default
1&lt;/pre&gt;

&lt;p&gt;当a等于1时，则会输出：&lt;/p&gt;

&lt;pre class="code"&gt;1&lt;/pre&gt;

&lt;p&gt;当a等于2时，则会输出：&lt;/p&gt;

&lt;pre class="code"&gt;default
1&lt;/pre&gt;

&lt;p&gt;好吧，尽管这样的代码比较罕见，但执行结果也并没有什么“特殊”的。switch的规则依旧可以用一句话说清：如果匹配到某个case，则从该case处开始执行，否则就从default处开始执行，一直向下，直到出现break语句为止。至于default的位置是否在最后，对于执行的策略可谓完全没有影响。&lt;/p&gt;

&lt;p&gt;当然，我实在没想到为什么有人会写这样的代码，所以假如有人对这点感觉恍惚我也觉得没太大关系。不过既然我要写&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;，则还是必须对此类代码的行为有所了解。尽管语言的使用者可以选择合适的子集，但语言的开发者（编译器、解释器等等）却必须遵循完整的规范，这是Jscex这类项目需要应对的麻烦。&lt;/p&gt;

&lt;p&gt;既然Jscex号称支持“全部JavaScript语言特性”，自然对switch的支持也在包括在内。switch的麻烦之处在于它的每个分支不像if语句那样完全相互独立，而是会不断“穿透”下去直至遇上break。因此Jscex在处理switch的时候也使用了一些技巧。例如下面这段代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;switch &lt;/span&gt;(a) {
    &lt;span style="color: blue"&gt;case &lt;/span&gt;0:
        $await(helloWorld());
    &lt;span style="color: blue"&gt;default&lt;/span&gt;:
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;);
    &lt;span style="color: blue"&gt;case &lt;/span&gt;1:
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;);
}&lt;/pre&gt;

&lt;p&gt;Jscex会将每个case及default中的语句“补齐”，以“确保”每项里都有完整的语句以及最后的break：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;switch &lt;/span&gt;(a) {
    &lt;span style="color: blue"&gt;case &lt;/span&gt;0:
        $await(helloWorld());
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;);
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;);
        &lt;span style="color: blue"&gt;break&lt;/span&gt;;
    &lt;span style="color: blue"&gt;default&lt;/span&gt;:
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;);
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;);
        &lt;span style="color: blue"&gt;break&lt;/span&gt;;
    &lt;span style="color: blue"&gt;case &lt;/span&gt;1:
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;);
        &lt;span style="color: blue"&gt;break&lt;/span&gt;;
}&lt;/pre&gt;

&lt;p&gt;然后再将其编译为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;switch &lt;/span&gt;(a) {
    &lt;span style="color: blue"&gt;case &lt;/span&gt;0:
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Bind(helloWorld(), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            console.log(&lt;span style="color: maroon"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;);
            console.log(&lt;span style="color: maroon"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;);
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Normal();
        });
    &lt;span style="color: blue"&gt;default&lt;/span&gt;:
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;);
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Normal();
    &lt;span style="color: blue"&gt;case &lt;/span&gt;1:
        console.log(&lt;span style="color: maroon"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Normal();
    }
})&lt;/pre&gt;

&lt;p&gt;自然，如果switch里没有包含bind操作（例如$await语句），则整个switch语句都会得以保留，这也是&lt;a href="http://blog.zhaojie.me/2011/05/jscex-compiled-code-optimization.html"&gt;Jscex编译结果的优化策略&lt;/a&gt;之一。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/05/javascript-when-break-is-not-the-last-choice-of-switch.html#comments</comments>
      <pubDate>Tue, 24 May 2011 03:33:03 GMT</pubDate>
      <lastBuildDate>Wed, 25 May 2011 16:59:52 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>上周末Jscex项目介绍的幻灯片</title>
      <link>http://blog.zhaojie.me/2011/05/jscex-write-sexy-javascript-slide.html</link>
      <guid>http://blog.zhaojie.me/2011/05/jscex-write-sexy-javascript-slide.html</guid>
      <description>&lt;p&gt;上周末，在风景秀丽的浙江大学校园内，举行了&lt;a href="http://cnodejs.org/blog/?p=956"&gt;NodeParty杭州站的活动&lt;/a&gt;。我在活动上结合&lt;a href="http://nodejs.org/"&gt;Node.js&lt;/a&gt;项目对&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;进行了简单介绍，包括其设计目的，设计原则，使用方式，高级模式，组成部分等等。在场的许多朋友也提出了不少问题，我也一一作了解答或是演示。总体感觉还算不错，毕竟是亲手编写的项目，对其各方面还是了然于胸的。在此发布演讲用的幻灯片，希望能给不在现场的同学带来一些帮助。&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_7976001"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="Jscex: Write Sexy JavaScript (中文)" href="http://www.slideshare.net/jeffz/jscex-write-sexy-javascript-cn"&gt;Jscex: Write Sexy JavaScript (中文)&lt;/a&gt;&lt;/strong&gt; &lt;iframe height="355" marginheight="0" src="http://www.slideshare.net/slideshow/embed_code/7976001" frameborder="0" width="425" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt; &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;为了便于在国外社区推广，我也制作了英文版的幻灯片（事实上这是“原版”，中文版反而是会前临时决定翻译过来的），在此一并献上：&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_7975832"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="Jscex: Write Sexy JavaScript" href="http://www.slideshare.net/jeffz/jscex-write-sexy-javascript"&gt;Jscex: Write Sexy JavaScript&lt;/a&gt;&lt;/strong&gt; &lt;iframe height="355" marginheight="0" src="http://www.slideshare.net/slideshow/embed_code/7975832" frameborder="0" width="425" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt; &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;欢迎您使用Jscex，遇到什么问题也请及时与我联系。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub（英文主站）：&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;https://github.com/JeffreyZhao/jscex&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;SNDACode（中文站）：&lt;a href="http://www.sndacode.com/projects/jscex"&gt;http://www.sndacode.com/projects/jscex&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2011/05/jscex-write-sexy-javascript-slide.html#comments</comments>
      <pubDate>Mon, 16 May 2011 05:43:40 GMT</pubDate>
      <lastBuildDate>Mon, 16 May 2011 05:45:21 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>浅谈Jscex的$await语义及异步任务模型</title>
      <link>http://blog.zhaojie.me/2011/05/jscex-async-task-model-and-await-semantic.html</link>
      <guid>http://blog.zhaojie.me/2011/05/jscex-async-task-model-and-await-semantic.html</guid>
      <description>&lt;p&gt;从某些程度上说，&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;的是提供了“新语言”，只不过这种新语言和JavaScript长的一模一样，最多添加了一个$await操作这个语义而已。其他方面，JavaScript的各种语法都可以让Jscex编译，所以它基本可以说是个完备的方案。之前有朋友提出疑问，说$await只能执行单个任务，那么岂不是多个任务之间就出现了先后依赖关系？假如有三个任务：A和B可以并行，但C依赖前两者，ABC如果串行的话，系统的总耗时便不够理想了。其实Jscex并没有这种限制，因为它的任务模型和$await语义简单且具有深厚的理论基础，灵活、丰富而统一。&lt;/p&gt;

&lt;p&gt;首先谈下$await的语义，有些朋友阅读示例代码，可能会觉得它表示“执行一个异步任务”。其实不然。$await的语义实际上只是“等待该任务结束”，同时：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;如果该任务没有运行，则启动该任务。 &lt;/li&gt;

  &lt;li&gt;如果该任务已经完成，则立即返回结果（或抛出异常）。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在Jscex的异步类库中，“异步任务”是独立的模型，它有自己的start或addListener等成员。一个Jscex异步函数的执行结果也是个异步任务对象，我们最终也是调用其start方法来启动这个任务。在一个Jscex函数内部，我们也可以手动地启动任务。例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(taskA, taskB, taskC) {
    taskA.start();
    taskB.start();

    $await(taskA);
    $await(taskB);

    $await(taskC);
}&lt;/pre&gt;

&lt;p&gt;任务A和B的start方法会在调用后立即返回，并在两者都完成后，才会启动并等待C任务。以上便回答了之前那个朋友提出的问题。由于A和B没有依赖，我们便让其并行执行；C依赖前两者，于是便等A和B结束再启动，仅此而已。事实上，我们也可以这么做：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(taskA, taskB, taskC) {
    $await(Jscex.Async.parallel(taskA, taskB));
    $await(taskC);
}&lt;/pre&gt;

&lt;p&gt;Jscex.Async.parallel是一个辅助函数，接受一堆异步任务，并返回一个新的异步任务，执行它表示并行地执行这些子任务，它也会在子任务都完成后才结束。这个异步任务模型十分简单，人人都能轻松地使用及扩展，也十分灵活。假如我们将刚才的需求换一下：C依赖于B，但A与前两者都独立，则可以编写这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(taskA, taskB, taskC) {
    taskA.start();

    $await(taskB.continueWith(taskC));
    $await(taskA);
}&lt;/pre&gt;

&lt;p&gt;在JavaScript中，我们只要为Jscex.Async.Task对象扩展一个continueWith方法，表示taskC将于taskB之后执行，并作为一个新任务返回即可。此类&lt;a href="http://en.wikipedia.org/wiki/Futures_and_promises"&gt;Future/Promise模型&lt;/a&gt;，以及Jscex的Monadic编译形式在异步编程领域中都有着经典的理论基础，因此Jscex在异步编程方面的支持可谓简单而统一。&lt;/p&gt;

&lt;p&gt;Jscex一直在不断前进，如今Jscex又支持了一种$await语句形式，“赋值”：&lt;/p&gt;

&lt;pre class="code"&gt;hello.world = $await(...);&lt;/pre&gt;

&lt;p&gt;其实按理说，Jscex早该支持这种形式了，但可能是由于最早的思路有些过于借鉴F#的模式（无副作用，因此没有此类赋值），一不留意便疏忽至今了。JavaScript其实终究是一门更为“命令式”的语言，虽然Jscex由F#那里得到启发，但如今其具体实现和最终形式，却也是专为JavaScript而设计的，以确保其必要的功能（例如break，continue等中断逻辑流的语言特性）与性能。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/05/jscex-async-task-model-and-await-semantic.html#comments</comments>
      <pubDate>Tue, 10 May 2011 14:29:43 GMT</pubDate>
      <lastBuildDate>Tue, 10 May 2011 14:32:07 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>浅谈Jscex编译结果的优化</title>
      <link>http://blog.zhaojie.me/2011/05/jscex-compiled-code-optimization.html</link>
      <guid>http://blog.zhaojie.me/2011/05/jscex-compiled-code-optimization.html</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/JeffreyZhao/Jscex"&gt;Jscex&lt;/a&gt;的核心是一个JavaScript语言到Monadic形式的编译器。从理论上说，这种编译规则十分简单，要写一个能够“正常运行”的编译器很容易。但是“正常运行”不代表足够优化。优化不当，会导致生成的结果中产生太多函数及闭包，对性能产生负面影响。在Jscex的早期原型中，从AST生成最终代码的逻辑比较简单，只做了一些基础优化。后来&lt;a href="http://blog.zhaojie.me/2011/04/jscex-released-under-bsd-license.html"&gt;重构了编译器，减少了不必要的代码&lt;/a&gt;。而上周我提交了更新，实现了更复杂而有效的优化策略。如今的Jscex编译器部分应该已经足够稳定，剩下的便是类库方便的发展了。&lt;/p&gt;

&lt;h1&gt;基础优化&lt;/h1&gt;

&lt;p&gt;Jscex的基础编译策略十分精简，这里仅列出其中最简单一条来说明问题。比如有如下代码：&lt;/p&gt;

&lt;pre class="code"&gt;statement1;
statement2;&lt;/pre&gt;

&lt;p&gt;按照Jscex的转化标准，应该由Combine及Delay组合而成：&lt;/p&gt;

&lt;pre class="code"&gt;builder.Combine(
    builder.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        statement1;
    },
    builder.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        statement2;
    }));&lt;/pre&gt;

&lt;p&gt;在Jscex中，无论是Combine和Delay返回的都是协议相同的中间对象。出现Delay，是因为JavaScript一门Strict Evaluation的语言，需要有个机制确保“延迟执行”。Combine的作用是连接两个中间对象，并返回一个新的中间对象，该对象的“执行语义”表示“按序”执行两者。如果有三条语句则会继续使用Combine嵌套起来。&lt;/p&gt;

&lt;p&gt;那么如果有10条语句连续执行呢？这便会产生大量的Combine，Delay调用，以及一大堆函数、闭包等等。运行结果自然没有问题（就像Delay套Delay那样），但很显然会产生不必要的性能开销。因此，从最起初的Jscex原型开始，便对这样的连续语句做出了优化。例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// before&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;a = 0;
    b = a + 1;
    a++;
    &lt;span style="color: blue"&gt;return &lt;/span&gt;a + b;
}

&lt;span style="color: green"&gt;// after&lt;/span&gt;
(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$$_builder_$$_0 = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;,
        $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;a = 0;
            b = a + 1;
            a++;
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Return(a + b);
        })
    );
})&lt;/pre&gt;

&lt;p&gt;无谓的Combine和Delay消失了，程序主体依旧是顺序执行的普通代码。&lt;/p&gt;

&lt;h1&gt;高级优化&lt;/h1&gt;

&lt;p&gt;连续的语句容易优化，所以在最早的Jscex原型中就已经有所体现。但是，如try/catch或是while循环这样的语言特性，又会变成怎么样的代码呢？例如，我们得到一个URL数组，然后使用个普通的for循环来遍历并请求每个URL里的内容：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// before
&lt;/span&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
        $await(requestAsync(urls[i]));
    }
}

&lt;span style="color: #006400"&gt;// after
&lt;/span&gt;(&lt;span style="color: blue"&gt;function &lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$$_builder_$$_0 = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;,
        $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0;
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Loop(
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;i &amp;lt; urls.length;
                },
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    i++;
                },
                $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Bind(requestAsync(urls[i]), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Normal();
                    });
                }),
                &lt;span style="color: blue"&gt;false
            &lt;/span&gt;);
        })
    );
})&lt;/pre&gt;

&lt;p&gt;无论是for，while还是do循环，编译之后都会成为了Loop调用。以上的代码已经足够优化，但如果我们换种写法，将“顺序”请求变为“并发”请求：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// before
&lt;/span&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;requests = [];

    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
        requests.push(requestAsync(urls[i]));
    }

    $await(Jscex.Async.parallel(requests));
}&lt;/pre&gt;

&lt;p&gt;在相当一段时间内，Jscex编译器输出的代码是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// after&lt;/span&gt;
(&lt;span style="color: blue"&gt;function &lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$$_builder_$$_0 = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;,
        $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;requests = [];
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Combine(
                $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0;
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Loop(
                        &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                            &lt;span style="color: blue"&gt;return &lt;/span&gt;i &amp;lt; urls.length;
                        },
                        &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                            i++;
                        },
                        $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                            requests.push(requestAsync(urls[i]));
                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Normal();
                        })
                    );
                }),
                $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(Jscex.Async.parallel(requests), &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Normal();
                    });
                })
            );
        });
    )
})&lt;/pre&gt;

&lt;p&gt;其实也很容易理解：一个标准的for循环嘛。但实际上，编译器完全没有必要生成那么复杂的代码。因为这个for循环内部没有涉及到绑定操作（例如异步类库下的$await），它完全只是段普通的代码，应该全部保留，就像这样：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// after&lt;/span&gt;
(&lt;span style="color: blue"&gt;function &lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$$_builder_$$_0 = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;,
        $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;requests = [];
            &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
                requests.push(requestAsync(urls[i]));
            }
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Bind(Jscex.Async.parallel(requests), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Normal();
            });
        })
    );
})&lt;/pre&gt;

&lt;p&gt;以上便是目前编译器会生成的代码。从表面上看来，要实现这点并不困难，只要检查AST中的for节点内部有没有绑定操作，如果没有，则直接输出。我一开始也是这么认为的，但是写了一大堆代码以后发现事情并没有想象中那么简单，于是回滚代码，重新开始。试看这样一段简单的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// before&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;try &lt;/span&gt;{
        &lt;span style="color: blue"&gt;throw &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;Error&amp;quot;&lt;/span&gt;;
    } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(ex) {
        &lt;span style="color: blue"&gt;throw &lt;/span&gt;ex;
    }
}&lt;/pre&gt;

&lt;p&gt;您可以猜测一下，这段代码编译后的结果是怎么样的呢？应该是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// after&lt;/span&gt;
(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$$_builder_$$_0 = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;,
        $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;try &lt;/span&gt;{
                &lt;span style="color: blue"&gt;throw &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;Error&amp;quot;&lt;/span&gt;;
            } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(ex) {
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Throw(ex);
            }
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Normal();
        })
    );
})&lt;/pre&gt;

&lt;p&gt;请注意，try内部的throw在目标代码里还是throw，而catch里的throw则是把异常抛向外部，因此则编译为Throw方法。如果再嵌套一个try/catch则会看的更加明显：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// before&lt;/span&gt;
&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;try &lt;/span&gt;{
        &lt;span style="color: blue"&gt;try &lt;/span&gt;{
            &lt;span style="color: blue"&gt;throw &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;Error&amp;quot;&lt;/span&gt;;
        } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(ex1) {
            &lt;span style="color: blue"&gt;throw &lt;/span&gt;ex1;
        }
    } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(ex2) {
        &lt;span style="color: blue"&gt;throw &lt;/span&gt;ex2;
    }
}

&lt;span style="color: green"&gt;// after&lt;/span&gt;
(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$$_builder_$$_0 = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;,
        $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;try &lt;/span&gt;{
                &lt;span style="color: blue"&gt;try &lt;/span&gt;{
                    &lt;span style="color: blue"&gt;throw &lt;/span&gt;&lt;span style="color: maroon"&gt;&amp;quot;Error&amp;quot;&lt;/span&gt;;
                } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(ex1) {
                    &lt;span style="color: blue"&gt;throw &lt;/span&gt;ex1;
                }
            } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(ex2) {
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Throw(ex2);
            }
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Normal();
        })
    );
})&lt;/pre&gt;

&lt;p&gt;在这段代码里，内层catch里的throw会被外层的catch捕获到，因此它还是普通的throw。与F#不同的是，JavaScript的某些语言特性（如throw，break，continue）会中断代码正常的执行流。因此，即便是一个普通的if分支里的的break，我们也必须判断这是个普通的break语句还是一个Break输出。不过实现这样的编译器也并不困难，例如我只是在生成代码时记录当前位置而已。例如一个普通try内部的throw便是普通的throw，如果这个try变成了Try结构，则必然是Throw输出了。&lt;/p&gt;

&lt;p&gt;优化到这步之后，应该说编译器的输出结果已经定型，剩下的就是类库方面的事情了。顺便一提，&lt;a href="http://www.sndacode.com/projects/jscex/wiki"&gt;Jscex中文站上的说明&lt;/a&gt;也已经同步更新，并补充了更多内容。此外，下周六我将会在&lt;a href="http://cnodejs.org/blog/?p=897"&gt;杭州CNodeJS聚会&lt;/a&gt;上和大家讨论Jscex类库方面的话题，感兴趣的朋友不妨同去同去。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/05/jscex-compiled-code-optimization.html#comments</comments>
      <pubDate>Thu, 05 May 2011 10:45:31 GMT</pubDate>
      <lastBuildDate>Thu, 05 May 2011 10:55:01 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>Jscex编译器更新：已支持嵌套Jscex函数</title>
      <link>http://blog.zhaojie.me/2011/04/jscex-compiler-update-support-nested-jscex-function.html</link>
      <guid>http://blog.zhaojie.me/2011/04/jscex-compiler-update-support-nested-jscex-function.html</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;的编译器更新了。之前的编译器并不会将一个Jscex函数内部的其他Jscex函数代码一并展开，这导致内嵌的Jscex函数会在外部函数调用时反复编译，性能开销较大；不过更重要问题，可能是AOT编译后的代码无法彻底解除与编译器的依赖。嵌套Jscex函数是否合理是一回事儿，使用者可以不去这么做，但是编译器本身还是该支持的。这也是Jscex编译器改进计划中的重要一步。&lt;/p&gt;

&lt;p&gt;之前，如果您编写这样的函数：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;outerAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;innerAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: #006400"&gt;// inner implementations
    &lt;/span&gt;}));

}));&lt;/pre&gt;

&lt;p&gt;实际上编译器会生成这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;outerAsync = (&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$$_builder_$$ = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;,
        $$_builder_$$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {

            &lt;span style="color: blue"&gt;var &lt;/span&gt;innerAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: #006400"&gt;// inner implementations&lt;/span&gt;
            }));

            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$.Normal();
        })
    );
})&lt;/pre&gt;

&lt;p&gt;这样在每次调用outerAsync的时候，都会重新启用Jscex.compile及eval的过程，这个性能开销还是比较可观的。不过目前的编译器已经会将内部的Jscex函数彻底展开：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;outerAsync = (&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$$_builder_$$_0 = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;,
        $$_builder_$$_0.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {

            &lt;span style="color: blue"&gt;var &lt;/span&gt;innerAsync = (&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: blue"&gt;var &lt;/span&gt;$$_builder_$$_1 = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_1.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;,
                    &lt;span style="color: #006400"&gt;// compiled inner implementations&lt;/span&gt;
                    $$_builder_$$_1.Normal()
                );
            });

            &lt;span style="color: blue"&gt;return &lt;/span&gt;$$_builder_$$_0.Normal();
        })
    );
})&lt;/pre&gt;

&lt;p&gt;除了不会多次编译内部的Jscex函数外，还彻底解除了与Jscex编译器的依赖，AOT编译器表示情绪愉快。您可能发现了，原本的builder变量名是固定的，不过为了支持内嵌函数，我在代码里添加了一个种子，这样每次编译时的builder变量名就不同了。例如在上面的代码中，编译后的外层函数使用$$_builder_$$_0，而内层函数使用的则是$$_builder_$$_1。&lt;/p&gt;

&lt;p&gt;于是现在我们便可以使用内嵌Jscex函数了，例如&lt;a href="http://blog.zhaojie.me/2010/11/the-coming-talks-and-jscex.html"&gt;之前提到过的“动画”示例&lt;/a&gt;，现在便可以写为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveSquareAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function&lt;/span&gt;(e) {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;moveAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function&lt;/span&gt;(startPos, endPos, duration) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
            e.style.left = (startPos.x + (endPos.x - startPos.x) * t / duration) + &lt;span style="color: maroon"&gt;&amp;quot;px&amp;quot;&lt;/span&gt;;
            e.style.top = (startPos.y + (endPos.y - startPos.y) * t / duration) + &lt;span style="color: maroon"&gt;&amp;quot;px&amp;quot;&lt;/span&gt;;
            $await(Jscex.Async.sleep(50));
        }

        e.style.left = endPos.x;
        e.style.top = endPos.y;
    }));

    $await(moveAsync({x:100, y:100}, {x:400, y:100}, 1000));
    $await(moveAsync({x:400, y:100}, {x:400, y:400}, 1000));
    $await(moveAsync({x:400, y:400}, {x:100, y:400}, 1000));
    $await(moveAsync({x:100, y:400}, {x:100, y:100}, 1000));
}));&lt;/pre&gt;

&lt;p&gt;内层的moveAsync函数直接使用外部函数的参数e，这代码绝对美观大方——至于是否真写这样的代码，就靠开发人员自身的考量了。&lt;/p&gt;

&lt;p&gt;话说回来，其实让现有的编辑器支持内嵌的Jscex函数并不困难，事实上原本“不支持”的原因也只是“没想太多”，就这么机械地写下来了。例如最核心的Jscex.compile，其“逻辑”大约是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;Jscex.compile = &lt;span style="color: blue"&gt;function &lt;/span&gt;(builderName, func) {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;code = &lt;span style="color: maroon"&gt;&amp;quot;var f = &amp;quot; &lt;/span&gt;+ func.ToString() + &lt;span style="color: maroon"&gt;&amp;quot;;&amp;quot;&lt;/span&gt;;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;ast = UglifyJS.parse(code);

    &lt;span style="color: #006400"&gt;// [ &amp;quot;toplevel&amp;quot;, [ [ &amp;quot;var&amp;quot;, [ [ &amp;quot;f&amp;quot;, [...] ] ] ] ] ]
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;funcAst = ast[1][0][1][0][1];

    &lt;span style="color: #006400"&gt;// compile funcAst with builderName;
&lt;/span&gt;}&lt;/pre&gt;

&lt;p&gt;简单地说，我是根据传入函数的AST生成代码，当然还有些中间过程，但总体而言的“输入”只是函数本身（外加个builderName）。而为了能够在“生成代码”的过程中继续编译内嵌函数，其实我们也只要支持“标准模式（即eval(Jscex.compile(...))这种形式）”的AST即可：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;compileStandardPattern(evalAst) {
    &lt;span style="color: #006400"&gt;// ...
&lt;/span&gt;}

Jscex.compile = &lt;span style="color: blue"&gt;function &lt;/span&gt;(builderName, func) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;funcCode = func.toString();
    &lt;span style="color: blue"&gt;var &lt;/span&gt;evalCode = &lt;span style="color: maroon"&gt;&amp;quot;eval(Jscex.compile(&amp;quot; &lt;/span&gt;+ JSON.stringify(builderName) + &lt;span style="color: maroon"&gt;&amp;quot;, &amp;quot; &lt;/span&gt;+ funcCode + &lt;span style="color: maroon"&gt;&amp;quot;))&amp;quot;
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;evalCodeAst = UglifyJS.parse(evalCode);

    &lt;span style="color: #006400"&gt;// [ &amp;quot;toplevel&amp;quot;, [ [ &amp;quot;stat&amp;quot;, [ &amp;quot;call&amp;quot;, ... ] ] ] ]
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;evalAst = evalCodeAst[1][0][1];
    &lt;span style="color: blue"&gt;var &lt;/span&gt;newCode = compileStandardPattern(evalAst);

    &lt;span style="color: #006400"&gt;// ...
&lt;/span&gt;}&lt;/pre&gt;

&lt;p&gt;compileStandardPattern方法会根据“标准模式”的AST输出展开后的代码。而对于从Jscex.compile调用中输入的函数对象，我们也将其构造为标准形式并生成AST。然后，compileStandardPattern方法内部在生成代码的时候，如果发现某一部分AST又恰好符合“标准模式”，则递归调用自身来生成内嵌Jscex函数的代码，最终便得到了充分展开的结果。&lt;/p&gt;

&lt;p&gt;当然，这么做和AOT编译器都有一个限制：由于其展开方式依靠静态代码分析，因此只能识别出“标准模式”的代码。例如，以下代码可以在JIT编译器下正常工作：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;compile = Jscex.compile;
&lt;span style="color: blue"&gt;var &lt;/span&gt;builderName = &lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;;
&lt;span style="color: blue"&gt;var &lt;/span&gt;func = &lt;span style="color: blue"&gt;function &lt;/span&gt;() { ... };
&lt;span style="color: blue"&gt;var &lt;/span&gt;newCode = compile(builderName, func);
&lt;span style="color: blue"&gt;var &lt;/span&gt;funcAsync = eval(newCode);&lt;/pre&gt;

&lt;p&gt;但显然，如果这段代码出现在某个Jscex函数内部，或是使用AOT编译器，便无法将其展开了。不过这个“限制”只是理论上的，我还没有想到不用“标准模式”的理由。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/04/jscex-compiler-update-support-nested-jscex-function.html#comments</comments>
      <pubDate>Fri, 29 Apr 2011 16:11:16 GMT</pubDate>
      <lastBuildDate>Fri, 29 Apr 2011 16:13:12 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <title>SNDACode及Jscex项目中文站</title>
      <link>http://blog.zhaojie.me/2011/04/jscex-chinese-repository-sndacode.html</link>
      <guid>http://blog.zhaojie.me/2011/04/jscex-chinese-repository-sndacode.html</guid>
      <description>&lt;p&gt;之前有朋友问我，为什么&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex只有英文站&lt;/a&gt;却没有中文的。其实原因很简单：国际化的项目势必更容易推广，而且国内看的懂英文的同学肯定比懂中文的外国人多嘛。而且我也不希望在一个源码库里维护两种语言的说明，这样对于浏览者也会造成误解——但更不希望维护两个源码库。因此我理想中的情况便是：某个站点可以完整地同步我的源码库，并提供一个供我放置文档的地方。最近我的同事搞了一个&lt;a href="http://www.sndacode.com"&gt;SNDACode&lt;/a&gt;站点，公开了部分盛大员工的开源项目。虽然远不如&lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt;等类似站点来的完备，但恰好能满足我的需求，我自然很乐意将它作为&lt;a href="http://www.sndacode.com/projects/jscex"&gt;Jscex的中文站点&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;以下为SNDACode的&lt;a href="http://www.sndacode.com/doc/about.html"&gt;自述&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;我们创建这样一个平台的目的是：希望通过它，通过我们负责任的、开放的心态，在一定程度上加强同行之间的交流、促进中国开源事业的良性有序发展。&lt;/p&gt;

  &lt;p&gt;目前SNDACode平台上所有的开源项目均由盛大内部员工贡献，或者由盛大内部项目组贡献，（如果没有指明开源协议）我们的所有项目都遵循《GNU通用公共许可协议》发布，这意味着，您享有本站开源项目的：运行、复制软件的自由，发行传播软件的自由，获得软件源码的自由，改进软件并将自己作出的改进版本向社会发行传播的自由。&lt;/p&gt;

  &lt;p&gt;在SNDACode，开发者们可以针对这个平台上开源的项目，讨论交流，提交Issue，进而参与到这些项目中来，从而使得这些项目，能够为更多的开发者带来价值。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;目前Jscex的详细说明文字也已经翻译为中文，发布在&lt;a href="http://www.sndacode.com/projects/jscex/wiki"&gt;项目的Wiki&lt;/a&gt;上。一般来说，中文文档会与英文版保持同步，视心情等原因也可能会有一至两天的延迟。希望有了中文资源以后，能够吸引更多的国内同学来使用Jscex。欢迎您在SNDACode上提出各种问题（当然最好还是在GitHub上提出英文的Issue），与我一齐参与到Jscex的建设中来。发起一个开源项目十分简单，几乎只是几分钟的事情，但最关键的还是看后续的努力。&lt;/p&gt;

&lt;p&gt;希望Jscex能够发展壮大，我认为这还是有一定希望的。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/04/jscex-chinese-repository-sndacode.html#comments</comments>
      <pubDate>Wed, 27 Apr 2011 15:20:17 GMT</pubDate>
      <lastBuildDate>Wed, 27 Apr 2011 15:20:17 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>Jscex使用BSD授权协议正式发布</title>
      <link>http://blog.zhaojie.me/2011/04/jscex-released-under-bsd-license.html</link>
      <guid>http://blog.zhaojie.me/2011/04/jscex-released-under-bsd-license.html</guid>
      <description>&lt;p&gt;这次打算把Jscex好好搞一下了，其实很少会有技术方面的障碍能“轮到”我们去突破，但我觉得Jscex的确有机会，HTML 5、Node.js各个都是红火的玩意儿。前几天我花了两个晚上用半生不熟的中式英语写了一篇自认为比较完整的说明文字放到了Github上的项目首页上，没想到几个小时后便收到了&lt;a href="http://onilabs.com/stratifiedjs"&gt;StratifiedJS&lt;/a&gt;（一个与Jscex目标有些类似的项目）作者的邮件，提到了一些关于StratifiedJS的事情。我向他咨询了StratifiedJS的某些细节问题，也向他简单介绍了Jscex的实现原理。如今&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex已经使用BSD授权协议正式发布&lt;/a&gt;（中文站也会在近期推出），再进行一些细节上的优化便要开始作推广了。&lt;/p&gt;

&lt;p&gt;最近的一次优化便是去除不必要的Delay方法调用。Delay的目的是延迟某段代码的执行时间，确保它只会在合适的时间执行，这对于非“延迟”及有副作用的语言来说十分重要。在F#中Delay方法的签名如下（Jscex与之类似）：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// async.Delay
&lt;/span&gt;((&lt;span style="color: blue"&gt;unit &lt;/span&gt;-&amp;gt; Async&amp;lt;'a&amp;gt;) -&amp;gt; Async&amp;lt;'a&amp;gt;)&lt;/pre&gt;

&lt;p&gt;Delay函数可以不断嵌套，从效果说来讲不会有所区别，例如：&lt;/p&gt;

&lt;pre class="code"&gt;builder.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;builder.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;builder.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;return &lt;/span&gt;builder.Loop(...);
        });
    });
});&lt;/pre&gt;

&lt;p&gt;上面这段代码和直接一个builder.Loop相比不会对结果产生任何影响，但是显然，从生成代码的整洁、美观程度（这涉及到可调试性）及性能都会有所损耗。之前的Jscex编译器会生成不必要的Delay（当然也不会有上面那么夸张），那是因为以前的代码生成器只是直接遍历从UglifyJS解析器那里获得的AST，以此来生成代码。新的编译器重新调整了结构和策略，会先将UglifyJS的AST转化为一个中间形式——我把它叫做Jscex AST，然后再让代码生成器从Jscex AST生成代码。新的生成器在工作时，会略过某些delay节点，直接从它的自节点中生成代码，以此达到去除不必要的Delay方法调用的目的。&lt;/p&gt;

&lt;p&gt;举例来说，一个快速排序的Jscex函数：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[y], array[y + 1]));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                $await(swapAsync(array, y, y + 1));
            }
        }
    }
}));&lt;/pre&gt;

&lt;p&gt;它会生成如下的代码（与&lt;a href="http://blog.zhaojie.me/2011/04/jscex-status-uglifyjs-parser-and-aot-compiler.html"&gt;之前的结果&lt;/a&gt;相比省去了一些Delay方法调用）：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = (&lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$_builder_$ = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;, $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;strike&gt;&lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {&lt;/strike&gt;
            &lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0;
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Loop(
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;x &amp;lt; array.length;
                },
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    x++;
                },
                $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;strike&gt;&lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {&lt;/strike&gt;
                        &lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0;
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Loop(
                            &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                &lt;span style="color: blue"&gt;return &lt;/span&gt;y &amp;lt; (array.length - x);
                            },
                            &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                y++;
                            },
                            $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Bind(compareAsync(...), &lt;span style="color: blue"&gt;function &lt;/span&gt;(r) {
                                    &lt;strike&gt;&lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {&lt;/strike&gt;
                                        &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Bind(swapAsync(...), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                                &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Normal();
                                            });
                                        } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
                                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Normal();
                                        }
                                    });
                                });
                            }),
                            &lt;span style="color: blue"&gt;false
                        &lt;/span&gt;);
                    });
                }),
                &lt;span style="color: blue"&gt;false
            &lt;/span&gt;);
        });
    }));
})&lt;/pre&gt;

&lt;p&gt;当然，Jscex还是有进一步优化的空间。例如包括保留“没有bind操作”的代码块，以及“嵌套Jscex函数”等等。最近一段时间应该会不断有一些更新。如果您在用JavaScript开发异步程序，也不妨一起来使用Jscex吧，它毫无疑问能显著改善您的编程生活。&lt;/p&gt;

&lt;p&gt;&lt;font color="#ff0000"&gt;&lt;strong&gt;广告时间：&lt;/strong&gt;&lt;/font&gt;第四届nBazaar技术交流会将于4月23日（本周六）于畅星大厦（上海市浦东新区碧波路888号，地铁二号线张江高科站下，步行10分钟可达）副楼3楼会议厅举行，在此欢迎您的到来，请于下午1点前准时入场。没有报名的朋友也可以现场报名。由于领导支持，我们将在现场送出&lt;a href="http://bambook.sdo.com/"&gt;市场价999元的盛大Bambook&lt;/a&gt;一台作为幸运观众的礼品，此外图灵出版社也赞助了十几本图书将会送给积极发言的观众。更多详细信息请参考&lt;a href="http://nbazaar.org/"&gt;活动首页&lt;/a&gt;。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/04/jscex-released-under-bsd-license.html#comments</comments>
      <pubDate>Thu, 21 Apr 2011 16:15:04 GMT</pubDate>
      <lastBuildDate>Thu, 21 Apr 2011 16:15:04 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>Jscex项目现状：UglifyJS解析器及AOT编译器</title>
      <link>http://blog.zhaojie.me/2011/04/jscex-status-uglifyjs-parser-and-aot-compiler.html</link>
      <guid>http://blog.zhaojie.me/2011/04/jscex-status-uglifyjs-parser-and-aot-compiler.html</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;项目是我为了简化JavaScript异步的一个类库，支持任意JavaScript（ECMASCript 3）引擎。Jscex小巧而强大，可以极大地改善前端的AJAX及动画等场景的编程体验，同样也可以用在&lt;a href="http://nodejs.org/"&gt;node.js&lt;/a&gt;进行服务器开发。从产生Jscex的想法到现在也有几个月的时间了，也一直想设法进行推广。在思考过程也发现了它在实际生产中可能会遇到的问题，于是前两个星期的主要工作，便是针对这些问题进行优化。首先我将Jscex的JavaScript分析器从&lt;a href="https://github.com/mozilla/narcissus"&gt;Narcissus&lt;/a&gt;换成了&lt;a href="https://github.com/mishoo/UglifyJS"&gt;UglifyJS&lt;/a&gt;，并基于node.js开发了一个简单的AOT编译器。接下来我也打算写个稍微详细一点的介绍，然后在国外社区看看反响如何。&lt;/p&gt;

&lt;p&gt;Jscex的本质是一个用JavaScript编写的JavaScript编译器，因此我需要一个JavaScript实现的JavaScript解析器。我起初&lt;a href="http://blog.zhaojie.me/2010/11/narcissus-javascript-parser.html"&gt;选择了著名的Narcissus项目&lt;/a&gt;，但由于它用到了&lt;a href="http://en.wikipedia.org/wiki/SpiderMonkey_(JavaScript_engine)"&gt;SpiderMonkey&lt;/a&gt;的一些扩展，最终我使用的其实是&lt;a href="http://www.neilmix.com/narrativejs/doc/"&gt;NarrativeJS&lt;/a&gt;中旧版的Narcissus代码。我一直在设法减小Jscex核心的体积及执行速度（毕竟一个重要的场景是浏览器端），再加上不是很喜欢旧版Narcissus代码的解析结果，于是我也在不断寻找它的替代品。前段时间我发现了UglifyJS这个JavaScript压缩器，它的解析器移植于&lt;a href="http://marijn.haverbeke.nl/parse-js/"&gt;parse-js&lt;/a&gt;项目，后者是一个用Common Lisp实现的类库，因此输出结构也十分简单，一个“表”而已，&lt;a href="http://blog.zhaojie.me/2011/04/uglifyjs-has-a-good-javascript-parser.html"&gt;执行速度也大大领先于Narcissus&lt;/a&gt;，体积也更小。于是我花了一个周末的时间将Jscex编译器改写为基于UglifyJS的实现。&lt;/p&gt;

&lt;p&gt;在改写过程中，我也同样考虑了目标代码在压缩后的体积。我使用&lt;a href="http://code.google.com/closure/compiler/"&gt;Closure Compiler&lt;/a&gt;的“高级”模式压缩代码，一般来说Closure Compiler的高级模式很破坏代码，我&lt;a href="http://blog.zhaojie.me/2011/04/compress-javascript-with-google-closure-compiler-in-advance-mode.html"&gt;使用了各种方式&lt;/a&gt;来保证压缩后的代码能够正确执行。目前，如果您要在项目中使用Jscex编写异步程序，需要依次加载以下三个文件（它们都在项目源码的bin目录中）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;uglifyjs-parser.min.js：UglifyJS解析器，大小20K，gzip后8K。 &lt;/li&gt;

  &lt;li&gt;jscex.min.js：Jscex核心编译器，大小5.5K，gzip后1.8K。 &lt;/li&gt;

  &lt;li&gt;jscex.async.min.js：Jscex异步核心类库，大小2K，gzip后0.9K。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果您觉得gzip后10K左右的体积还是有些大，那么也可以使用目前已经提供的AOT编译器——虽然AOT编译器的原始目的并不是为了减小体积。&lt;/p&gt;

&lt;p&gt;Jscex改善异步编程的原理，在于让程序员直接编写代码，使用普通的编程思路来实现算法，包括是用try...catch来捕获异常等等，而不会因为异步所需要的回调将代码拆得支离破碎。例如我们要实现冒泡排序算法的动画演示，也只需要使用传统编码方式实现算法即可：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// 标准算法&lt;/span&gt;
&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSort = &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(array[y] &amp;gt; array[y + 1]) {
                swap(array, y, y + 1);
            }
        }
    }
}

&lt;span style="color: green"&gt;// 演示动画&lt;/span&gt;
&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[y], array[y + 1]));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                $await(swapAsync(array, y, y + 1));
            }
        }
    }
}));&lt;/pre&gt;

&lt;p&gt;Jscex.compile会解析代码，并生成异步代码，并交给eval来解释执行。bubbleSortAsync和其中调用的compareAsync（比较两个元素大小，并暂停10毫秒）和swapAsync（交换两个元素，绘图，并暂停20毫秒）都是异步方法。但是无论在编写和使用上，异步方法和同步算法几乎没有区别——唯一的区别便是$await语句必须单起一行。这个限制一是为了保证开发人员可以明确分清普通的JavaScript代码及异步方法调用，二便是为了简化编译器的实现。例如，“理想情况”下类似以下的代码也需要支持：&lt;/p&gt;

&lt;pre class="code"&gt;f(g(1), $await(...))

&lt;span style="color: blue"&gt;if &lt;/span&gt;(x &amp;gt; y &amp;amp;&amp;amp; $await(...)) { ... }&lt;/pre&gt;

&lt;p&gt;尤其是第二行代码，$await可能由于短路而根本不会执行。为此，Jscex要求开发人员明确编写这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;a1 = g(1);
&lt;span style="color: blue"&gt;var &lt;/span&gt;a2 = $await(...);
f(a1, a2);

&lt;span style="color: blue"&gt;if &lt;/span&gt;(x &amp;gt; y) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;flag = $await(...);
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(flag) { ... }
}&lt;/pre&gt;

&lt;p&gt;我并不担心这会让开发人员编写代码时有所不便，事实上F#的&lt;a href="http://msdn.microsoft.com/en-us/library/dd233250.aspx"&gt;Async Workflow&lt;/a&gt;是有这般要求，我甚至敢保证&lt;a href="http://blog.zhaojie.me/2010/10/pdc2010-the-future-of-csharp-and-vb-by-anders-hejlsberg-1.html"&gt;未来C#的异步特性&lt;/a&gt;也是类似的设计。但是，JavaScript有个重要的特点：它在实际使用时往往会被压缩。如果仅仅是去除空白字符，那么Jscex自然还可以正常工作。但事实上现代的JavaScript压缩工具都会分析代码的语义，并重新生成体积更小的代码。例如之前的bubbleSortAsync经过压缩便会成为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync=eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;,&lt;span style="color: blue"&gt;function&lt;/span&gt;(a){&lt;span style="color: blue"&gt;for&lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;b=0;b&amp;lt;a.length;b++)&lt;span style="color: blue"&gt;for&lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;c=0;c&amp;lt;a.length-b;c++){&lt;span style="color: blue"&gt;var &lt;/span&gt;d=$await(compareAsync(a[c],a[c+1]));d&amp;gt;0&amp;amp;&amp;amp;$await(swapAsync(a,c,c+1))}}))&lt;/pre&gt;

&lt;p&gt;试看d&amp;gt;0&amp;amp;&amp;amp;$wait(...)这段代码，完全就让Jscex无法工作了。为此，我为Jscex开发了AOT编译器（scripts目录下的jscexc.js及JscexExtractor.js文件），即在部署前便对代码进行编译并生成目标代码（之前是在运行时生成代码，即JIT编译）。AOT编译器同样使用JavaScript编写，使用node.js运行，这样便可以直接使用Jscex的编译器实现。与编译器核心不同，AOT编译器使用了最新版的Narcissus来解析代码，这是因为Narcissus能够提供更丰富的解析结果，我可以直接获得整个目标方法的起始和结束地址（不过有bug，我使用时绕开了），自然还包括原始代码，用起来十分方便。至于之前提到的依赖于SpiderMonkey扩展，体积较大，执行速度慢等缺点，对于AOT编译器来说便完全不是问题了。&lt;/p&gt;

&lt;p&gt;Jscex的AOT编译器使用起来十分简单：&lt;/p&gt;

&lt;pre class="code"&gt;node jscexc.js --input input_file --output output_file&lt;/pre&gt;

&lt;p&gt;例如，如果一个文件包含之前的bubbleSortAsync方法，那么经过AOT编译器之后，它的代码便会被替换成为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = (&lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;$_builder_$ = Jscex.builders[&lt;span style="color: maroon"&gt;&amp;quot;async&amp;quot;&lt;/span&gt;];
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0;
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Loop(
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;x &amp;lt; array.length;
                },
                &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    x++;
                },
                $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                        &lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0;
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Loop(
                            &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                &lt;span style="color: blue"&gt;return &lt;/span&gt;y &amp;lt; (array.length - x);
                            },
                            &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                y++;
                            },
                            $_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Bind(compareAsync(...), &lt;span style="color: blue"&gt;function &lt;/span&gt;(r) {
                                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                        &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Bind(swapAsync(...), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                                                &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Normal();
                                            });
                                        } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
                                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$_builder_$.Normal();
                                        }
                                    });
                                });
                            }),
                            &lt;span style="color: blue"&gt;false
                        &lt;/span&gt;);
                    });
                }),
                &lt;span style="color: blue"&gt;false
            &lt;/span&gt;);
        });
    });
})&lt;/pre&gt;

&lt;p&gt;再进行压缩，便不会产生任何问题了。从表面看起来，编译后的Jscex代码体积大了不少，但是其中大部分为重复架子代码，压缩比例一般也会比较大。使用AOT编译后的代码有以下几个好处：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;经过JavaScript压缩器处理后也能正确执行。 &lt;/li&gt;

  &lt;li&gt;运行时只需要加载一个极小的jscex.async.min.js文件（异步核心类库），gzip后大小不到1K。 &lt;/li&gt;

  &lt;li&gt;由于代码在发布前生成，节省了JIT编译的开销。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在我个人看来，目前的Jscex已经可以在一些比较正式的场合中使用了。Jscex功能强大，实现小巧，能够与其它类库同时使用（它只会在全局对象上产生一个Jscex对象），接下来我也会为jQuery或MooTools等著名JavaScript框架/类库提供Jscex的绑定。在此也希望您可以实际使用一下Jscex项目，如果遇到问题请及时与我联系，我会给予您必要的支持。&lt;/p&gt;

&lt;p&gt;&lt;span style="color: red"&gt;广告时间：&lt;/span&gt;&lt;a href="http://nbazaar.org/"&gt;第四届nBazaar技术交流会&lt;/a&gt;将于2011年4月23日举行。第四届交流会的形式将略作改变：除了三场演讲（Windows Phone 7、IDE插件开发、单点登陆解决方案设计与实现）之外，本次活动设有嘉宾互动环节，您将有机会和嘉宾就某些话题进行探讨。我们正在收集话题，也希望大家踊跃提问，具体信息详见&lt;a href="http://nbazaar.org/"&gt;http://nbazaar.org/&lt;/a&gt;。此外，nBazaar技术沙龙的邮件列表已经正式启用，所有用户也已添加完成（之前报名或参加过技术会议）。如果有任何疑问，请邮件至&lt;img class="embed" src="http://services.nexodyne.com/email/customicon/9xaGAYDnFdYrRqAdl1tTvrs%3D/Y4fIT8s%3D/000000/ffffff/000000/0/image.png" /&gt;。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/04/jscex-status-uglifyjs-parser-and-aot-compiler.html#comments</comments>
      <pubDate>Thu, 14 Apr 2011 18:04:38 GMT</pubDate>
      <lastBuildDate>Thu, 14 Apr 2011 18:09:53 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>通过定义常量控制Closure Compiler的行为</title>
      <link>http://blog.zhaojie.me/2011/04/use-define-annotation-in-closure-compiler.html</link>
      <guid>http://blog.zhaojie.me/2011/04/use-define-annotation-in-closure-compiler.html</guid>
      <description>&lt;p&gt;&lt;a href="http://blog.zhaojie.me/2011/04/compress-javascript-with-google-closure-compiler-in-advance-mode.html"&gt;上一篇文章&lt;/a&gt;里我提到，在进行&lt;a href="http://code.google.com/closure/compiler/"&gt;Closure Compiler&lt;/a&gt;压缩之前可以对代码进行一些预处理，这样可以得到更好的效果。在回复中有朋友提到可以&lt;a href="http://code.google.com/closure/compiler/docs/js-for-compiler.html"&gt;使用一些Annotation（标记）&lt;/a&gt;，例如加上@export，然后使用--generate_exports，便可以保留需要的那些变量名。不过经过实验还是没有得到预期的效果，所以使用标记来“指导”高级压缩行为依旧是一个不太可行的做法。不过有个标记与我的设想一直，那就是使用@define来“定义一个常量”，然后在编译（压缩）时对其进行覆盖。这为一些压缩需求提供一种更直接的控制方式。&lt;/p&gt;

&lt;p&gt;还是先谈一下@export，当时看了文档的说明，我一度也觉得这能满足我的要求（即保留变量名不被压缩）：&lt;/p&gt;

&lt;pre class="code"&gt;$ java -jar compiler.jar --help
...
--generate_exports         : Generates export code for those marked
                              with @export
...&lt;/pre&gt;

&lt;p&gt;但事实上，实验的结果令人大失所望。例如下面的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;/**
 * @export
 */
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;Jscex = (&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: #006400"&gt;/**
     * @constructor
     */
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;CodeGenerator = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: #006400"&gt;/**
         * @export
         */
        &lt;/span&gt;&lt;span style="color: blue"&gt;this&lt;/span&gt;.normalMode = &lt;span style="color: blue"&gt;false&lt;/span&gt;;
    }
    &lt;span style="color: #006400"&gt;/**
     * @export
     */
    &lt;/span&gt;CodeGenerator.prototype.generate = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        alert(&lt;span style="color: blue"&gt;this&lt;/span&gt;.normalMode);
    }

    &lt;span style="color: blue"&gt;function &lt;/span&gt;compile() { &lt;span style="color: blue"&gt;return new &lt;/span&gt;CodeGenerator(); };

    &lt;span style="color: blue"&gt;return &lt;/span&gt;{ compile: compile };

})();&lt;/pre&gt;

&lt;p&gt;首先，Closure Compiler只允许在Global范围内使用@export标记，因此以上几个@export只有Jscex上的那个才能保留下来。其次，当去除其他@export标记之后，Closure Compiler生成的结果却是：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;b=&lt;span style="color: blue"&gt;function&lt;/span&gt;(){...};goog.a(&lt;span style="color: maroon"&gt;&amp;quot;Jscex&amp;quot;&lt;/span&gt;,b);&lt;/pre&gt;

&lt;p&gt;Closure Compiler很神奇地为脚本添加了&lt;a href="http://code.google.com/closure/library/"&gt;Closure Libraries&lt;/a&gt;这个依赖，这显然无法令人接受。&lt;/p&gt;

&lt;p&gt;失望之余，我观察并尝试了其他一些标记，发现@define还是不错的。就拿前文来说，我希望&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;能够在Debug模式下避免生成不必要的括号，原来的做法是编写这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: maroon"&gt;&amp;quot;dot&amp;quot;&lt;/span&gt;: &lt;span style="color: blue"&gt;function &lt;/span&gt;(ast) {
    &lt;span style="color: blue"&gt;function &lt;/span&gt;needBracket() { &lt;span style="color: #006400"&gt;/* ... */ &lt;/span&gt;}

    &lt;span style="color: blue"&gt;var &lt;/span&gt;nb = needBracket();
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(nb) {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._write(&lt;span style="color: maroon"&gt;&amp;quot;(&amp;quot;&lt;/span&gt;)
            ._visit(ast[1])
            ._write(&lt;span style="color: maroon"&gt;&amp;quot;).&amp;quot;&lt;/span&gt;)
            ._write(ast[2]);
    } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._visit(ast[1])
            ._write(&lt;span style="color: maroon"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;)
            ._write(ast[2]);
    }
},&lt;/pre&gt;

&lt;p&gt;然后在使用Closure Compiler压缩之前将needBracket方法的调用替换成true，这样Closure Compiler就会发现if的第二个分支永远不会执行，因此直接从最后的结果中去除了；更近一步，由于needBracket方法没有其他地方调用过，因此它也会被完整地删除，这就得到了体积更小的代码。不过这个方法多少有点trick的意味，它需要代码编写者与压缩脚本之间进行约定，其实我们可以使用一种更直接的方式，那就是使用@define标记来定义一个常量：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;/** @define {boolean} */
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;JSCEX_DEBUG = &lt;span style="color: blue"&gt;true&lt;/span&gt;;
...

&lt;span style="color: maroon"&gt;&amp;quot;dot&amp;quot;&lt;/span&gt;: &lt;span style="color: blue"&gt;function &lt;/span&gt;(ast) {
    &lt;span style="color: blue"&gt;function &lt;/span&gt;needBracket() { &lt;span style="color: #006400"&gt;/* ... */ &lt;/span&gt;}

    &lt;span style="color: blue"&gt;var &lt;/span&gt;nb = (!JSCEX_DEBUG) || needBracket();
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(nb) {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._write(&lt;span style="color: maroon"&gt;&amp;quot;(&amp;quot;&lt;/span&gt;)
            ._visit(ast[1])
            ._write(&lt;span style="color: maroon"&gt;&amp;quot;).&amp;quot;&lt;/span&gt;)
            ._write(ast[2]);
    } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._visit(ast[1])
            ._write(&lt;span style="color: maroon"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;)
            ._write(ast[2]);
    }
},&lt;/pre&gt;

&lt;p&gt;在代码中JSCEX_DEBU为true，因此nb的值会是needBracket方法调用后的结果。但如果我们在使用Closure Compiler压缩代码添加--define参数，便可以将JSCEX_DEBUG的值修改为false：&lt;/p&gt;

&lt;pre class="code"&gt;java \
    -jar compiler.jar \
    --js jscex.js \
    --js_output_file jscex.min.js \
    --define 'JSCEX_DEBUG=false' \
    --compilation_level ADVANCED_OPTIMIZATIONS&lt;/pre&gt;

&lt;p&gt;于是乎——就不多做解释了。Closure Compiler提供的标记有很多，但实验下来行为大都和想象中不符，这倒也是件很奇怪的事情。可能还是要去邮件列表之类的地方问问吧。&lt;/p&gt;

&lt;p&gt;&lt;span style="color: red"&gt;广告时间：&lt;/span&gt;&lt;a href="http://nbazaar.org/"&gt;第四届nBazaar技术交流会&lt;/a&gt;将于2011年4月23日举行。根据部分用户反馈，第四届交流会的形式将略作改变：除了三场演讲之外，本次活动设有嘉宾互动环节，您将有机会和嘉宾就某些话题进行探讨。我们将在会前收集部分话题，也希望大家踊跃提问，具体方式详见 &lt;a href="http://nbazaar.org/"&gt;http://nbazaar.org/&lt;/a&gt;。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/04/use-define-annotation-in-closure-compiler.html#comments</comments>
      <pubDate>Sun, 10 Apr 2011 17:12:09 GMT</pubDate>
      <lastBuildDate>Sun, 10 Apr 2011 17:12:41 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>使用Google Closure Compiler全力压缩代码</title>
      <link>http://blog.zhaojie.me/2011/04/compress-javascript-with-google-closure-compiler-in-advance-mode.html</link>
      <guid>http://blog.zhaojie.me/2011/04/compress-javascript-with-google-closure-compiler-in-advance-mode.html</guid>
      <description>&lt;p&gt;JavaScript压缩代码的重要性不言而喻，如今的压缩工具也有不少，例如&lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;YUI Compressor&lt;/a&gt;，Google &lt;a href="http://code.google.com/closure/compiler/"&gt;Closure Compiler&lt;/a&gt;，以及现在比较红火的&lt;a href="https://github.com/mishoo/UglifyJS"&gt;UglifyJS&lt;/a&gt;。UglifyJS的出名是由于它代替Closure Compiler成为jQuery项目的压缩工具。根据我的实测，&lt;a href="http://code.jquery.com/jquery-1.5.2.js"&gt;jQuery Core的代码&lt;/a&gt;使用&lt;a href="http://marijnhaverbeke.nl/uglifyjs"&gt;UglifyJS压缩&lt;/a&gt;后（节省62.5%）的确要比&lt;a href="http://closure-compiler.appspot.com/home"&gt;Closure Compiler压缩&lt;/a&gt;后（节省57.53%）更小一些。很显然，这是因为UglifyJS的压缩策略比Closure Compiler更“聪明”一些。我这里用了“聪明”而不是“激进”，是因为“激进”带上了一丝负面的意味——就好比Closure Compiler的“高级”优化方式。之前与UglifyJS相比的是Closure Compiler的“简单”优化方式，它们都是“安全”的，而Closure Compiler的“高级”优化几乎100%会破坏您的代码，因此它提出了各种“激进”的手段去“破坏”您的代码，以此达到压缩的目的。这种手段是把双刃剑，如果您能掌控它的压缩规则，则代码便可以压缩至极小。&lt;/p&gt;

&lt;p&gt;我们先来看看的Closure Compiler的威力。例如我有这样一段代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;Jscex = (&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: #006400"&gt;/**
    * @constructor
    */
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;CodeGenerator = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.normalMode = &lt;span style="color: blue"&gt;false&lt;/span&gt;;
    }
    CodeGenerator.prototype.generate = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        alert(&lt;span style="color: maroon"&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;);
    }

    &lt;span style="color: blue"&gt;function &lt;/span&gt;compile() {
        &lt;span style="color: blue"&gt;return&lt;/span&gt; &lt;span style="color: blue"&gt;new &lt;/span&gt;CodeGenerator();
    };

    &lt;span style="color: blue"&gt;return &lt;/span&gt;{ compile: compile };
})();&lt;/pre&gt;

&lt;p&gt;猜猜看，如果使用Closure Compiler的高级优化方式来压缩代码，会是什么情况呢？结果如下：&lt;/p&gt;

&lt;pre class="code"&gt;(&lt;span style="color: blue"&gt;function&lt;/span&gt;(){&lt;span style="color: blue"&gt;function &lt;/span&gt;a(){&lt;span style="color: blue"&gt;this&lt;/span&gt;.a=!1}&lt;span style="color: blue"&gt;return&lt;/span&gt;{compile:&lt;span style="color: blue"&gt;function&lt;/span&gt;(){&lt;span style="color: blue"&gt;return new &lt;/span&gt;a}}})();&lt;/pre&gt;

&lt;p&gt;目标代码很短，硬着头皮看看也无妨。首先，Jscex对象消失了，因为Closure Compiler认为其他地方并没有使用这个对象。其次，CodeGenerator的normalMode字段也被改名为a，因为这个名字更省空间。最后，generate方法也不见了，理由同第一项。您瞅瞅，这样的代码还能执行吗？这就是Closure Compiler激进的地方，它把输入文件作为一个完整的单元，并不会考虑对外的“接口”是否会变化。我读了Closure Compiler的文档，发现了它支持&lt;a href="http://code.google.com/closure/compiler/docs/js-for-compiler.html"&gt;对源代码做标记&lt;/a&gt;。但是经过实验，这些标记似乎并不能影响编译后的结果，只是让编译器工作时多做一些“静态检查”。&lt;/p&gt;

&lt;p&gt;当然，理论上说Closure Compiler提供了保持成员名称的机制，例如&lt;a href="http://code.google.com/closure/compiler/docs/api-tutorial3.html"&gt;exports和extern&lt;/a&gt;。假设我要保持之前的Jscex对象，那么就必须这么做：&lt;/p&gt;

&lt;pre class="code"&gt;window[&lt;span style="color: maroon"&gt;&amp;quot;Jscex&amp;quot;&lt;/span&gt;] = (&lt;span style="color: blue"&gt;function &lt;/span&gt;() { ... &lt;/span&gt;})();&lt;/pre&gt;

&lt;p&gt;这样Closure Compiler生成的代码就会变成：&lt;/p&gt;

&lt;pre class="code"&gt;window.Jscex=(&lt;span style="color: blue"&gt;function&lt;/span&gt;(){ ... &lt;/span&gt;})();&lt;/pre&gt;

&lt;p&gt;为了“节省”空间，它把“索引”的访问方式又切换回“字段”的访问方式，何等蛋疼！此外，原本我以为Closure Compiler强迫我依赖浏览器环境，后来发现其实window也可以替换为其他名称，例如：&lt;/p&gt;

&lt;pre class="code"&gt;my_root[&lt;span style="color: maroon"&gt;&amp;quot;Jscex&amp;quot;&lt;/span&gt;] = (&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: #006400"&gt;/**
    * @constructor
    */
    &lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;CodeGenerator = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;[&lt;span style="color: maroon"&gt;&amp;quot;normalMode&amp;quot;&lt;/span&gt;] = &lt;span style="color: blue"&gt;false&lt;/span&gt;;
    }
    CodeGenerator.prototype[&lt;span style="color: maroon"&gt;&amp;quot;generate&amp;quot;&lt;/span&gt;] = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        alert(&lt;span style="color: maroon"&gt;&amp;quot;Hello World&amp;quot;&lt;/span&gt;);
    }

    &lt;span style="color: blue"&gt;function &lt;/span&gt;compile() {
        &lt;span style="color: blue"&gt;return new &lt;/span&gt;CodeGenerator();
    };

    &lt;span style="color: blue"&gt;return &lt;/span&gt;{ compile: compile };
})();&lt;/pre&gt;

&lt;p&gt;虽然从理论上说，使用这种方式可以告诉Closure Compiler哪些成员名称是可以压缩的而哪些不行，但我真心难以接受这种四处使用“索引”的写法。不过，其实这对我造成的影响其实不大，因为我很少使用那种“面向对象”的方式来对外公开接口，我一般也就是用“对象”加上“方法”的形式，例如上面的Jscex.compile方法，至于内部类型，如CodeGenerator，就随Closure Compiler压缩去吧。&lt;/p&gt;

&lt;p&gt;话又说回来，其实如果您是从头开始编写JavaScript代码，并且遵守一定规则，那么Closure Compiler的确可以把您的代码压缩地很小。甚至您可以多写一点调试用的代码，但在最终压缩后的代码中去掉它们。这里最基本的原则可以归纳为：将压缩后不需要的代码抽取为独立的方法，然后在预处理阶段去除这些方法的调用代码，于是Closure Compiler便会将这些方法的定义一并删除，节省了相当多的空间。&lt;/p&gt;

&lt;p&gt;以&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;项目为例加以说明：Jscex的核心之一是根据AST生成JavaScript代码，在“调试”版本的实现中，我希望生成的代码能够美观、易读；而在“发布”版本中，我希望需要代码的体积越少越好。于是，对于某个表达式“是否需要添加括号”这样的场景，便需要详细斟酌了。我的策略是，在“调试”代码中，将判断是否需要增加括号的逻辑放置到needBracket方法中，然后编写这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: maroon"&gt;&amp;quot;dot&amp;quot;&lt;/span&gt;: &lt;span style="color: blue"&gt;function &lt;/span&gt;(ast) {
    &lt;span style="color: blue"&gt;function &lt;/span&gt;needBracket() { &lt;span style="color: #006400"&gt;/* ... */ &lt;/span&gt;}

    &lt;span style="color: blue"&gt;var &lt;/span&gt;nb = needBracket();
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(nb) {
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._write(&lt;span style="color: maroon"&gt;&amp;quot;(&amp;quot;&lt;/span&gt;)
            ._visit(ast[1])
            ._write(&lt;span style="color: maroon"&gt;&amp;quot;).&amp;quot;&lt;/span&gt;)
            ._write(ast[2]);
    } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._visit(ast[1])
            ._write(&lt;span style="color: maroon"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;)
            ._write(ast[2]);
    }
},&lt;/pre&gt;

&lt;p&gt;上面这个方法的作用是生成一个dot表达式的代码，其中定义了needBracket方法，我们可以在其中放置复杂而低效的逻辑，用来判断dot的左侧表达式是否需要添加括号。如果needBracket返回true，则生成括号，例如(&amp;quot;abc&amp;quot; + &amp;quot;def&amp;quot;).length；否则，便会生成更为简洁易读的代码，例如Jscex.Async.start，而不会是((Jscex).Async).start。但是在最终“发布”版本的代码中，nb变量被直接设置为true，于是Closure Compiler则会发现if的一个分支永远不会执行，则将其完全去除。在压缩后的代码中，以上方法只会是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;dot:&lt;span style="color: blue"&gt;function&lt;/span&gt;(a){&lt;span style="color: blue"&gt;this&lt;/span&gt;.a(&lt;span style="color: maroon"&gt;&amp;quot;(&amp;quot;&lt;/span&gt;).b(a[1]).a(&lt;span style="color: maroon"&gt;&amp;quot;).&amp;quot;&lt;/span&gt;).a(a[2])},&lt;/pre&gt;

&lt;p&gt;可以看出，这段实现无论如何都会生成带括号的JavaScript代码，丑则丑矣，但对JavaScript引擎来说没有丝毫区别。目前jscex.js的压缩脚本其实是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;# pre-processing for Closure Compiler
sed \
    -e 's/var Jscex =/my_temp_root[&amp;quot;Jscex&amp;quot;] =/' \
    -e 's/\._writeLine(/._write(/g' \
    -e 's/this\._write();//g' \
    -e 's/\._write()//g' \
    -e 's/this\._writeIndents();//g' \
    -e 's/\._writeIndents()//g' \
    -e 's/this\._indentLevel = 0;//g' \
    -e 's/this\._indentLevel++;//g' \
    -e 's/this\._indentLevel--;//g' \
    -e 's/checkBindArgs([^;]*;//g' \
    -e 's/needBracket([^;]*;/true;/g' \
    -e 's/throwUnsupportedError();//g' \
    -e 's/_log([^;]*;//g' \
    ../src/jscex.js &amp;gt; ../bin/jscex.tmp.js


# use Closure Compiler to compress
java \
    -jar ../tools/compiler.jar \
    --js ../bin/jscex.tmp.js \
    --js_output_file ../bin/jscex.tmp.min.js \
    --compilation_level ADVANCED_OPTIMIZATIONS

# post-processing
sed 's/my_temp_root\.Jscex=/var Jscex=/' ../bin/jscex.tmp.min.js &amp;gt; ../bin/jscex.min.js

# remove temp files
rm ../bin/jscex.tmp*.js&lt;/pre&gt;

&lt;p&gt;我在使用Closure Compiler压缩代码之前，会先对脚本进行一下“预处理”，暂时为如下几项：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;为了避免Jscex对象丢失，先将var Jscex替换成my_temp_root[&amp;quot;Jscex&amp;quot;]，压缩之后再将其替换回来。 &lt;/li&gt;

  &lt;li&gt;将所有的writeLine方法调用替换成write，这样代码里便不会用到writeLine方法，Closure Compiler会去除该方法定义。 &lt;/li&gt;

  &lt;li&gt;去除空的write方法调用，这一般是由writeLine替换为write而引起的。 &lt;/li&gt;

  &lt;li&gt;去除与“缩进（indent）”相关的所有属性和方法，这样相关定义在压缩后也会一并消失。 &lt;/li&gt;

  &lt;li&gt;去除各种错误检查，如checkBindArgs，throwUnsupportedError方法的调用。 &lt;/li&gt;

  &lt;li&gt;去除日志输出，即_log方法调用，则_log方法本身也会消失不见。 &lt;/li&gt;

  &lt;li&gt;将needBracket方法调用直接替换为true，强制输出带括号的代码。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用这样的做法，我们可以充分利用Closure Compiler在“高级”优化级别下的激进压缩方式，同时得到正确、高效、体积还很小的代码（补充：后来发现其实某些情况下可以&lt;a href="http://blog.zhaojie.me/2011/04/use-define-annotation-in-closure-compiler.html"&gt;使用定义常量的方式来简化预处理的步骤&lt;/a&gt;）。就拿jscex.js和jQuery Core进行比较（事先都去除注释及空白字符）：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“简单”压缩的jQuery Core（安全压缩）：减小30.83%体积（120.18KB =&amp;gt; 83.13KB）。 &lt;/li&gt;

  &lt;li&gt;“高级”压缩的jQuery Core（非安全压缩，不可用）：减小37.91%体积（120.18KB =&amp;gt; 74.62KB）。 &lt;/li&gt;

  &lt;li&gt;“高级”压缩的Jscex.js（非安全压缩，可用）：节省55.02%体积（12.14KB =&amp;gt; 5.46KB）。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以上数据是从&lt;a href="http://closure-compiler.appspot.com/home"&gt;在线的Closure Compiler&lt;/a&gt;得出的结果，我也不知道为何效果不如本地明显。从本地压缩来看，jscex.js是25812字节，而jscex.min.js只有5585字节，有将近5倍的差距。&lt;/p&gt;

&lt;p&gt;可惜的是，如果不是从代码编写及压缩一开始就考虑到Closure Compiler的诸多行为，我们只能使用“简单”的压缩方式来确保代码的正确性。如果要让一个现有的大段代码（例如jQuery）安全通过Closure Compiler的“高级”考验，这几乎是一件不可能的事情。&lt;/p&gt;

&lt;p&gt;&lt;span style="color: red"&gt;广告时间：&lt;/span&gt;&lt;a href="http://nbazaar.org/"&gt;第四届nBazaar技术交流会&lt;/a&gt;将于2011年4月23日举行。根据部分用户反馈，第四届交流会的形式将略作改变：除了三场演讲之外，本次活动设有嘉宾互动环节，您将有机会和嘉宾就某些话题进行探讨。我们将在会前收集部分话题，也希望大家踊跃提问，具体方式详见 &lt;a href="http://nbazaar.org/"&gt;http://nbazaar.org/&lt;/a&gt;。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/04/compress-javascript-with-google-closure-compiler-in-advance-mode.html#comments</comments>
      <pubDate>Thu, 07 Apr 2011 01:30:56 GMT</pubDate>
      <lastBuildDate>Thu, 14 Apr 2011 16:49:26 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>UglifyJS有个不错的JavaScript解析器</title>
      <link>http://blog.zhaojie.me/2011/04/uglifyjs-has-a-good-javascript-parser.html</link>
      <guid>http://blog.zhaojie.me/2011/04/uglifyjs-has-a-good-javascript-parser.html</guid>
      <description>&lt;p&gt;我一直在为&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;寻找好用的JavaScript解析器，之前我用的是&lt;a href="https://github.com/mozilla/narcissus"&gt;Narcissus&lt;/a&gt;，也&lt;a href="http://blog.zhaojie.me/2010/11/narcissus-javascript-parser.html"&gt;写过相关文章&lt;/a&gt;。不过可惜的是，Narcissus使用了SpiderMonkey的扩展，因此它并不是用ECMAScript 3实现的，无法在IE 8等浏览器中使用。目前Jscex使用的是&lt;a href="http://www.neilmix.com/narrativejs/doc/"&gt;NarrativeJS&lt;/a&gt;中旧版的Narcissus，但是我并不喜欢它输出的AST结构，使用中也发现高级功能里的一些bug，有些食之无味弃之可惜的感觉，而改写新版Narcissus又必须大动干戈。最近我接触到了&lt;a href="https://github.com/mishoo/UglifyJS"&gt;UglifyJS&lt;/a&gt;，发现它的解析器相当不错，性能也比Narcissus高出许多，在此介绍给大家。&lt;/p&gt;

&lt;h1&gt;介绍&lt;/h1&gt;

&lt;p&gt;UglifyJS是个JavaScript压缩器，效果和&lt;a href="http://code.google.com/closure/compiler/"&gt;Google Closure Compiler&lt;/a&gt;相比有过之而无不及。对于现代化的JavaScript压缩器来说，简单的去除空白和压缩局部变量是远远不够的，同时需要理解代码的语义，将其替换成提及更小的形式（Uglify的说明页上有许多描述）。这显然需要一个JavaScript解析器。UglifyJS基于NodeJS开发，不过可以在各种支持CommonJS模块系统的JavaScript引擎/平台上运行。如果没有CommonJS，也只需将exports相关的代码去掉即可。&lt;/p&gt;

&lt;p&gt;JavaScript解析器的作用自然是将JavaScript代码分解成AST，然后根据AST便可以做到许多有趣的事情。相同的AST可以在内存中有不同的表现形式，例如之前提到我不太喜欢Jscex目前使用的旧版Narcissus，一个重要的原因便是它的AST结构不够友好（最新的Narcissus倒不错）。此外，虽然它提供了一些高级功能，例如标注了每个元素在源代码中的位置，这样使用者就可以直接根据getSource方法获得它对应的源代码——只可惜经试验这个功能有bug，这迫使我还得遍历完整的AST。&lt;/p&gt;

&lt;p&gt;UglifyJS的JavaScript分词器和解析器存放在源代码的parse-js.js文件中，移植于&lt;a href="http://marijn.haverbeke.nl/parse-js/"&gt;parse-js&lt;/a&gt;项目，后者是一个用Common Lisp实现的类库。现在您应该可以猜到它输出的AST是什么表现形式了吧。没错，就是个“表”，用JavaScript来表示，就是个数组套数组。我写了点简单的代码对其进行格式化输出，您可以&lt;a href="http://files.zhaojie.me/demos/js-parsers/uglifyjs-parser.html"&gt;在这里简单尝试一下UglifyJS的解析器&lt;/a&gt;。这个输出虽然简单，但对于Jscex来说也已经完全够用了。&lt;/p&gt;

&lt;h1&gt;使用&lt;/h1&gt;

&lt;p&gt;打开parse-js.js文件，您会看到这样一些代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;/* -----[ Tokenizer (constants) ]----- */

&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;KEYWORDS = array_to_hash([
    ...
]);

&lt;span style="color: blue"&gt;var &lt;/span&gt;RESERVED_WORDS = array_to_hash([
    ...
]);

...

&lt;span style="color: blue"&gt;function &lt;/span&gt;parse($TEXT, exigent_mode, embed_tokens) {
    ...
}

...

&lt;span style="color: #006400"&gt;/* -----[ Exports ]----- */&lt;/span&gt;

exports.tokenizer = tokenizer;
exports.parse = parse;
exports.slice = slice;
exports.curry = curry;
exports.member = member;
exports.array_to_hash = array_to_hash;
exports.PRECEDENCE = PRECEDENCE;
exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
exports.RESERVED_WORDS = RESERVED_WORDS;
exports.KEYWORDS = KEYWORDS;
exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
exports.OPERATORS = OPERATORS;
exports.is_alphanumeric_char = is_alphanumeric_char;
exports.set_logger = &lt;span style="color: blue"&gt;function&lt;/span&gt;(logger) {
        warn = logger;
};&lt;/pre&gt;

&lt;p&gt;UglifyJS是基于CommonJS模块机制编写的，这一个文件其实就是个模块，它对外的方法通过exports暴露出来。如果我们将其作为普通的JavaScript文件引入到浏览器中，显然会报“export未定义”异常。理论上说，如果定义一个exports对象，甚至去除和exports有关的代码就能正常使用parse方法了。不过这么做也有个严重的问题，那就是对根对象的“污染”实在是太严重了，例如在浏览器中所有的函数，变量都出现在window上，再引入一些其他类库，造成冲突的可能性相当高。&lt;/p&gt;

&lt;p&gt;因此，我们必须对代码进行一些修改。幸运的是，在JavaScript中解决这类“作用域”问题十分容易，例如我这样将parse-js.js的代码包围了起来：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;UglifyJS = {};

(&lt;span style="color: blue"&gt;function &lt;/span&gt;(exports) {

&lt;span style="color: #006400"&gt;/* original code here */

&lt;/span&gt;})(UglifyJS);&lt;/pre&gt;

&lt;p&gt;这样就解决了作用域问题，如今我们就能访问UglifyJS对象上的KEYWORDS集合以及parse等成员了。&lt;/p&gt;

&lt;h1&gt;性能&lt;/h1&gt;

&lt;p&gt;然后再说说性能。JavaScript一直被认为是一门执行效率低下的语言——这其实是个错误的观点。其实从语言设计上说，JavaScript比Python和Ruby都要快，只不过由于历史原因各大浏览器对它都不太重视而已。不过如今情况早就有所改变，在V8的带领下，现代的JavaScript引擎执行速度都已经超过了目前最快的Python和Ruby实现。话不多说，现在我们就来比较一下UglifyJS的解析器与Narcissus在各浏览器下的表现吧。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://files.zhaojie.me/demos/js-parsers/benchmark.html"&gt;测试页面在此&lt;/a&gt;，您也可以自行尝试，测试场景是使用两者分别解析十次Narcissus的源代码——大约1500行未压缩的JavaScript代码（值得一提的是，我试了许多压缩后的代码，如jquery-min.js，它们用UgilifyJS可以正常解析，而Narcissus却解析失败）。我使用两台公司配置的标准工作机，测试了IE、Chrome和Firefox各两个版本共6种浏览器。每个浏览器我都会运行多遍测试，去除偏差大的结果，取中游数值。遗憾的是，由于条件所限，两台机器的操作系统有所不同，虽然我认为并不会对结果有什么影响，但如果您足够顶真，也不妨再自行评测一把。&lt;/p&gt;

&lt;p&gt;首先我在Win 7下测试了Chrome 10、FireFox 3和IE9，结果如下：&lt;/p&gt;

&lt;iframe width='100%' height='520' frameborder='0' src='https://spreadsheets.google.com/pub?hl=en&amp;hl=en&amp;key=0AuVfB8TyGeJ1dFRLOHlOMXNFRnVXYkdTNkYxTWMtUVE&amp;single=true&amp;gid=1&amp;output=html&amp;widget=true'&gt;&lt;/iframe&gt;

&lt;p&gt;对于UglifyJS来说，Chrome 10的表现最好，IE 9相比略慢少许，而Firefox 3耗时则是前两者的数倍。对于Narcissus来说，则是IE 9表现最好，仅为Chrome 10的五分之一，和Firefox 3相比更是数量级上的领先。有趣的是，Chrome 10和Firefox 3下两个解析器的耗时都是一比十左右，而IE 9下则相差无几。&lt;/p&gt;

&lt;p&gt;然后是Win XP下Chromium 12、Firefox 4及IE 8，结果如下：&lt;/p&gt;

&lt;iframe width='100%' height='520' frameborder='0' src='https://spreadsheets.google.com/pub?hl=en&amp;hl=en&amp;key=0AuVfB8TyGeJ1dFRLOHlOMXNFRnVXYkdTNkYxTWMtUVE&amp;single=true&amp;gid=2&amp;output=html&amp;widget=true'&gt;&lt;/iframe&gt;

&lt;p&gt;对于UglifyJS来说，Chromium 12的表现依旧抢眼，胜过Firefox 4不少，不过使用Narcissus的情况则正好相反。同样可以看出，IE 8的JavaScript引擎性能全面不敌其他浏览器，不过它和IE 9、Firefox 4（以及后面的Safari）的情况类似，即UglifyJS和Narcissus的耗时并没有太大差别。&lt;/p&gt;

&lt;p&gt;为了便于观察，我将两次测试的结果放在一起（除了&lt;a href="http://www.chromium.org/developers/calendar"&gt;非正式版本&lt;/a&gt;的Chromium 12）：&lt;/p&gt;

&lt;iframe width='100%' height='560' frameborder='0' src='https://spreadsheets.google.com/pub?hl=en&amp;hl=en&amp;key=0AuVfB8TyGeJ1dFRLOHlOMXNFRnVXYkdTNkYxTWMtUVE&amp;single=true&amp;gid=4&amp;output=html&amp;widget=true'&gt;&lt;/iframe&gt;

&lt;p&gt;总体而言，Chrome 10、IE 9和Firefox 4为第一军团。IE 9在UglifyJS上小负于Chrome 10，但在Narcissus上优势明显；Chrome 10在UglifyJS上表现最佳，但在Narcissus却落后IE 9和Firefox 4较多；Firefox 4虽然都不是“最佳”，但差距也并不太大。至于IE 8和Firefox 3，在JavaScript的执行效率方面的确已经落后于这个时代了。必须承认，如今的浏览器大战的确大大提高了各方的质量。&lt;/p&gt;

&lt;p&gt;此外我还测试了公司iMac上的Chrome 10、Firefox 3以及Safari 5，在此列出结果：&lt;/p&gt;

&lt;iframe width='100%' height='520' frameborder='0' src='https://spreadsheets.google.com/pub?hl=en&amp;hl=en&amp;key=0AuVfB8TyGeJ1dFRLOHlOMXNFRnVXYkdTNkYxTWMtUVE&amp;single=true&amp;gid=0&amp;output=html&amp;widget=true'&gt;&lt;/iframe&gt;

&lt;p&gt;虽然浏览器的表现各有高低，差距也有所不同，但可以确定的是，UglifyJS解析器的性能的确比Narcissus要高。因此，我打算在接下来几天里用UglifyJS替换掉目前Jscex里使用的Narcissus。&lt;/p&gt;

&lt;h1&gt;总结&lt;/h1&gt;

&lt;p&gt;由于前端开发和JavaScirpt的流行，越来越多的人开始用JavaScript做一些有趣的事情。我很不喜欢如今许多所谓的前端实践，纠缠于大量的hack以及各种浏览器的表现，甚至是JavaScript里某种特定写法的性能更高——例如，居然有消息称，对于字符串连接操作来说，a += b的性能比a = a + b要高（或反之）。在我看来这些东西是最无用的，知道了又如何？随着浏览器更新换代，这些“经验”瞬间就毫无作用了。&lt;/p&gt;

&lt;p&gt;这也是我为什么喜欢玩JavaScript，却死也不愿去做前端开发，尤其是HTML、CSS。同样，如IE 6这种浏览器在我眼中也是必须消灭的东西。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/04/uglifyjs-has-a-good-javascript-parser.html#comments</comments>
      <pubDate>Fri, 01 Apr 2011 07:40:06 GMT</pubDate>
      <lastBuildDate>Sat, 02 Apr 2011 03:37:57 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>使用Jscex实现排序算法动画</title>
      <link>http://blog.zhaojie.me/2011/03/sorting-animations-with-jscex.html</link>
      <guid>http://blog.zhaojie.me/2011/03/sorting-animations-with-jscex.html</guid>
      <description>&lt;p&gt;用动画来观察排序算法是一件很酷的事情，例如&lt;a href="http://www.sorting-algorithms.com/"&gt;有人便为各种排序算法提供了动画效果&lt;/a&gt;。只可惜这些效果都是实现准备好的gif图片，并非由代码写成。在大部分平台上编写这样的程序并没有太大困难，只要在绘制出图形之后短暂地阻塞线程就行了。可惜，在JavaScript中我们只能“一蹴而就”，要暂停的话，只能使用setTimeout进行回调了。不过，这也正是&lt;a href="http://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;的用武之地，用Jscex编写的代码需要“暂停”，只需要简单地调用sleep异步方法，一切都很直接。&lt;/p&gt;

&lt;p&gt;例如，传统的冒泡排序算法，使用JavaScript实现可能是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSort = &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(array[y] &amp;gt; array[y + 1]) {
                swap(array, y, y + 1);
            }
        }
    }
}&lt;/pre&gt;

&lt;p&gt;如果使用Jscex，则变成了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;bubbleSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;x = 0; x &amp;lt; array.length; x++) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;y = 0; y &amp;lt; array.length - x; y++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[y], array[y + 1]));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) {
                $await(swapAsync(array, y, y + 1));
            }
        }
    }
}));&lt;/pre&gt;

&lt;p&gt;算法实现上唯一的变化可能就是“比较”方法被独立成单独的compareAsync方法了。它和swapAsync方法的代码如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;compareAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(x, y) {
    $await(Jscex.Async.sleep(10));
    &lt;span style="color: blue"&gt;return &lt;/span&gt;x - y;
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;swapAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array, x, y) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;t = array[x];
    array[x] = array[y];
    array[y] = t;

    repaint(array);

    $await(Jscex.Async.sleep(20));
}));&lt;/pre&gt;

&lt;p&gt;简单地说，一个排序算法的性能如何，关键看它的比较和元素交换的次数。因此，我在compareAsync和swapAsync方法中都使用sleep进行了一定时间的停顿（其中后者更长一些）。由于改变了元素位置，因此在swapAsync方法中我还重新绘制了图案。有了这两个方法，我们只要将其简单地组合进bubbleSortAsync方法中即可。&lt;/p&gt;

&lt;p&gt;我们轻松实现的冒泡排序算法，Jscex编译器则会将其转化为复杂的Monadic代码，我本想贴在这里，但发现过于复杂凌乱，也太占地方。如果您感兴趣可以打开浏览器的console窗口，查看其输出结果。基于Jscex实现的选择排序和快速排序也和传统实现可谓毫无二致：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;selectionSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;j = 0; j &amp;lt; array.length - 1; j++) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;mi = j;
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = j + 1; i &amp;lt; array.length; i++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[i], array[mi]));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;lt; 0) { mi = i; }
        }
        $await(swapAsync(array, mi, j));
    }
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;quickSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array) {
    $await(_quickSortAsync(array, 0, array.length - 1));
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;_quickSortAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array, begin, end) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;index = $await(_partitionAsync(array, begin, end));

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(begin &amp;lt; index - 1) {
        $await(_quickSortAsync(array, begin, index - 1));
    }

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(index &amp;lt; end) {
        $await(_quickSortAsync(array, index, end));
    }
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;_partitionAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(array, begin, end) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;i = begin;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;j = end;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;pivot = array[Math.floor((begin + end) / 2)];

    &lt;span style="color: blue"&gt;while &lt;/span&gt;(i &amp;lt;= j) {
        &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[i], pivot));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;lt; 0) { i++; } &lt;span style="color: blue"&gt;else &lt;/span&gt;{ &lt;span style="color: blue"&gt;break&lt;/span&gt;; }
        }

        &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;r = $await(compareAsync(array[j], pivot));
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(r &amp;gt; 0) { j--; } &lt;span style="color: blue"&gt;else &lt;/span&gt;{ &lt;span style="color: blue"&gt;break&lt;/span&gt;; }
        }

        &lt;span style="color: blue"&gt;if &lt;/span&gt;(i &amp;lt;= j) {
            $await(swapAsync(array, i, j));
            i++;
            j--;
        }
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;i;
}));&lt;/pre&gt;

&lt;p&gt;言止于此，我们现在就来看一下（&lt;a href="http://files.zhaojie.me/demos/jscex/samples/async/sorting-animations.html?bubble" target="_blank"&gt;冒泡&lt;/a&gt;，&lt;a href="http://files.zhaojie.me/demos/jscex/samples/async/sorting-animations.html?selection" target="_blank"&gt;选择&lt;/a&gt;，&lt;a href="http://files.zhaojie.me/demos/jscex/samples/async/sorting-animations.html?quick" target="_blank"&gt;快速&lt;/a&gt;）三种排序方式的动画吧（请使用支持canvas的浏览器，例如IE 9、FireFox、Chrome、Safari）。我很难想象，使用传统方式实现一个快速排序的动画会是什么情况。&lt;/p&gt;

&lt;p&gt;我最近愈发懒惰，愈发不愿意去想这些事情了。如今Jscex已经支持JavaSript语言中绝大部分语言特性，包括条件判断，各种循环（以及循环中的break和continue），还有异常处理等等。对于Jscex带来的编程体验我有十足的信心，但现在的最大问题却是不知道该如何推广。我也希望可以有更多人可以参与并接受这个项目。之前想过用它来实现一个小游戏等等，但素材方面却总是难以落实。我十分希望能从您那里得到各方面的意见和建议。&lt;/p&gt;

&lt;p&gt;最后还是一则&lt;span style="color: red"&gt;招聘广告&lt;/span&gt;：盛大创新院某英语学习方向的项目招聘1至2名ASP.NET程序员：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;岗位职责：负责服务器及数据库的搭建、维护工作，并配合前端工程师完成网站架构。 &lt;/li&gt;

  &lt;li&gt;岗位要求：熟悉.NET 3.5开发，有关系型数据库（MySQL优先）开发管理经验。有MongoDB等NoSQL开发经验者优先。有网站数据负载均衡经验者优先。 &lt;/li&gt;

  &lt;li&gt;办事认真踏实，对行业充满热情，了解行业最新动态。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;很显然，这不是我的招聘需求，不过我也帮忙筛选面试。欢迎大家自荐或推荐，邮件请至：&lt;img class="embed" src="http://services.nexodyne.com/email/icon/T3pYH0sugOKOkgan4fyp/GoPfEek%3D/R01haWw%3D/0/image.png" /&gt;。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2011/03/sorting-animations-with-jscex.html#comments</comments>
      <pubDate>Thu, 10 Mar 2011 15:35:09 GMT</pubDate>
      <lastBuildDate>Fri, 11 Mar 2011 03:41:49 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/extension/">项目扩展</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <title>基于Jscex.Async的JavaScript动画/游戏</title>
      <link>http://blog.zhaojie.me/2010/12/animations-and-games-based-on-jscex-async.html</link>
      <guid>http://blog.zhaojie.me/2010/12/animations-and-games-based-on-jscex-async.html</guid>
      <description>&lt;p&gt;首先和大家宣布一个消息，Jscex的代码已经&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;提交至Github上&lt;/a&gt;了，感兴趣的朋友下载来Dog Fooding一把，并欢迎提出反馈意见。Jscex受到F#计算表达式的启发，是一个面向JavaScript语言的monadic扩展，最常见的用途便是编写异步程序，尤其是逻辑复杂的异步程序。不过除此之外，使用这套异步库来编写动画或是游戏也是十分容易的事情。例如，一个人物的走动或是爆炸效果，其实可以视为一个贴图随时间不断变化的过程。这个变化的过程是异步的，但是有了Jscex.Async，我们只需使用最直接的同步形式编写代码就行了。&lt;/p&gt;

&lt;p&gt;例如这里有个&lt;a href="http://files.zhaojie.me/demos/jscex/samples/async/bullet.html"&gt;我用一个多小时编写的示例&lt;/a&gt;（需要支持canvas的浏览器，例如IE 9，FireFox，Chrome，Safari等等），类似于“是男人就撑过20秒”，其中有这么一段代码：&lt;/p&gt;

&lt;pre class="code"&gt;BulletGame.prototype._bulletFlyAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(f) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;options = &lt;span style="color: blue"&gt;this&lt;/span&gt;._options;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;bullet = {pos: {x: 0, y: 0}};

    &lt;span style="color: blue"&gt;this&lt;/span&gt;._addBullet(bullet);
    
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; &lt;span style="color: blue"&gt;this&lt;/span&gt;._playing &amp;amp;&amp;amp; &lt;span style="color: blue"&gt;this&lt;/span&gt;._inArea(bullet.pos); t += 20) {
        $await(&lt;span style="color: blue"&gt;this&lt;/span&gt;._timer.setTimeoutAsync(20));
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._tryHit(bullet.pos); &lt;span style="color: green"&gt;// 判断是否击中&lt;/span&gt;
        bullet.pos = f(t); &lt;span style="color: green"&gt;// 改变子弹位置&lt;/span&gt;
    }

    &lt;span style="color: blue"&gt;this&lt;/span&gt;._removeBullet(bullet);
}));&lt;/pre&gt;

&lt;p&gt;这段代码是一个异步程序，表示一颗子弹的飞行过程。子弹的飞行轨迹（某一时刻的位置）由函数f来决定，因此我们只需要写一个for循环，并且在循环内部“暂停”一段时间就行了，就这么简单。在循环之前，我们将子弹添加到一个容器里，这样在一个不断循环的paint方法里就会把这颗子弹绘制在canvas上面。循环结束后，我们将子弹从容器中移出。这一切都十分顺其自然，虽然这段代码的执行过程完全是异步的。&lt;/p&gt;

&lt;p&gt;Jscex可以使用各种方式与现有代码组合至一块儿，例如这里我为BulletGame的prototype对象扩展了一个方法，为此我晚上还改进了一下Jscex的编译逻辑，以及Jscex.Async的实现，其目的就是在_bulletFlyAsync这样的函数内部保留this对象在JavaScript语义上的正确性。JavaScript语言中this引用的动态特征是种很强大的特性，但是对于“语言改造者”来说，在一个充满回调的过程中保持语义正确并不是件十分容易的事情（当然，也不算十分困难，只要“想明白”即可）。上面的代码经过编译之后，就类似于我们直接编写了这样的代码（暂时如此，未来会有修改和优化）：&lt;/p&gt;

&lt;pre class="code"&gt;BulletGame.prototype._bulletFlyAsync = &lt;span style="color: blue"&gt;function &lt;/span&gt;(f) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Start(&lt;span style="color: blue"&gt;this&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;options = &lt;span style="color: blue"&gt;this&lt;/span&gt;._options;
        &lt;span style="color: blue"&gt;var &lt;/span&gt;bullet = { pos: { x: 0, y: 0} };
        &lt;span style="color: blue"&gt;this&lt;/span&gt;._addBullet(bullet);
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Combine(
            $async.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0;
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Loop(
                    &lt;span style="color: blue"&gt;function &lt;/span&gt;() { &lt;span style="color: blue"&gt;return this&lt;/span&gt;._playing &amp;amp;&amp;amp; &lt;span style="color: blue"&gt;this&lt;/span&gt;._inArea(bullet.pos); },
                    &lt;span style="color: blue"&gt;function &lt;/span&gt;() { t += 20; },
                    $async.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(&lt;span style="color: blue"&gt;this&lt;/span&gt;._timer.setTimeoutAsync(20), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                            &lt;span style="color: blue"&gt;this&lt;/span&gt;._tryHit(bullet.pos);
                            bullet.pos = f(t);
                            &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return();
                        });
                    })
                );
            }),
            $async.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: blue"&gt;this&lt;/span&gt;._removeBullet(bullet);
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return();
            })
        );
    });
};&lt;/pre&gt;

&lt;p&gt;此外，JavaScript是一个很容易编写此类动画或是游戏的平台，因为它所有的代码都在UI线程上执行，因此不会有任何多线程方面的问题。例如上面的代码，我们可以放心地向容器里添加或删除“子弹”对象，试想在一个并发环境里构造一个线程安全的双向链表是件多么麻烦的事情。&lt;/p&gt;

&lt;p&gt;最大的问题其实是浏览器环境里的性能问题，不过我对此并不怎么担心，因为V8已经给我们开了个好头，并且还在&lt;a href="http://blog.chromium.org/2010/12/new-crankshaft-for-v8.html"&gt;不断前进&lt;/a&gt;。此外对于动画和游戏来说，最大的性能问题更可能是canvas的绘图性能。现在这个简单的例子性能自然不会有太大问题，不过一旦加上贴图和素材，性能问题就会凸现出来了。对于目前的示例，我在安装Windows 7的台式机里试验了多种浏览器，帧数（fps）各有高低：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Firefox：110左右 &lt;/li&gt;

  &lt;li&gt;Chrome：210左右 &lt;/li&gt;

  &lt;li&gt;IE 9：300至800不等，比较常见的是500左右 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而在我的MBP上：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Firefox：70左右 &lt;/li&gt;

  &lt;li&gt;Safari：60左右 &lt;/li&gt;

  &lt;li&gt;Chrome：190左右 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;此外还有同事测试了Safari最新的Nightly Build，帧数与Chrome不相上下。从中可以看出，IE 9的表现最为出色，可见它的GPU加速的确不是在吹牛的。值得一提的是，如果图像上没有子弹，那么帧数大约只有60出头，一旦同时出现了几十颗子弹，帧数变会飙升至500以上，峰值甚至可以到800。由此推测，IE 9也是在“按需”使用GPU，十分周全。&lt;/p&gt;

&lt;p&gt;Jscex的源码及示例都在Github上，欢迎尝试。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/12/animations-and-games-based-on-jscex-async.html#comments</comments>
      <pubDate>Wed, 15 Dec 2010 17:03:28 GMT</pubDate>
      <lastBuildDate>Thu, 16 Dec 2010 05:14:09 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/dotnet/">.Net框架</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>演出季上“异步编程模型的演变”幻灯片</title>
      <link>http://blog.zhaojie.me/2010/12/event-season-async-evolution-slides.html</link>
      <guid>http://blog.zhaojie.me/2010/12/event-season-async-evolution-slides.html</guid>
      <description>&lt;p&gt;&lt;a href="http://blog.zhaojie.me/2010/11/the-coming-talks-and-jscex.html"&gt;演出季&lt;/a&gt;终于过去了，现在就来做一个收尾吧。这次的主题是“异步编程模型的演变”，主要回顾了微软在.NET平台上异步编程上的进化：基于回调，基于迭代生成器，基于类库，基于语言。不过这样的编程模型其实并非微软独有，而是一些运用比较广泛的异步编程方式，因此在SD 2.0大会上我其实完全用JavaScript进行演示。从结果上来看，除了最早的TUP，其他两场演讲（&lt;a href="http://www.net-china.org/"&gt;.NET技术大会&lt;/a&gt;和&lt;a href="http://sd2china.csdn.net/"&gt;SD 2.0&lt;/a&gt;）的反响都不错。&lt;/p&gt;

&lt;p&gt;严格说来，这场演讲在过去一个月中讲了四次，其中第一次是在CSDN举办的&lt;a href="http://tup.csdn.net/"&gt;TUP&lt;/a&gt;活动上。您可能注意到“异步编程”是我后半年关注的重点，例如在&lt;a href="http://nbazaar.org"&gt;创新院赞助的nBazaar交流会&lt;/a&gt;上我分享的话题都是关于异步编程的，这次要在1小时内覆盖之前两场演讲，自然信息量很大。于是在TUP活动上的演讲便不太理想，因为内容实在太多，结果绝大部分内容几乎都是匆匆扫过。不幸中的万幸，这次演讲的幻灯片倒成了一个“阅读材料”（&lt;a href="http://files.zhaojie.me/slides/async-evolution/async-evolution-full.pdf"&gt;下载&lt;/a&gt;）。&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_6103768"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="The Evolution of Async-Programming on .NET Platform (TUP, Full)" href="http://www.slideshare.net/jeffz/async-evolutionfull"&gt;The Evolution of Async-Programming on .NET Platform (TUP, Full)&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse6103768" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-full-101210113950-phpapp02&amp;stripped_title=async-evolutionfull&amp;userName=jeffz" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse6103768" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-full-101210113950-phpapp02&amp;stripped_title=async-evolutionfull&amp;userName=jeffz" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;我将异步编程模型的演变过程分为四个部分：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;基于回调：&lt;/strong&gt;这是最传统的异步编程模型，“回调”是“异步”的天然属性，例如.NET里的Begin/End异步模型或是基于事件的异步模型。在前端开发中，XMLHttpRequest或是DOM事件，事实上都是基于事件的异步编程模型。这种编程模型破坏了代码的局部性，写起来非常麻烦，更别说是异步操作的组合、异常处理、取消等操作了。 &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;基于迭代生成器：&lt;/strong&gt;我把这个单独提出来说，是因为迭代生成器几乎已经成为现代语言的标配了，&lt;a href="http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html"&gt;在JavaScript 1.7里也有相应的特性&lt;/a&gt;——自然，可怜的Java语言是不曾提供支持的。有了迭代生成器，我们就可以在发起异步操作的时候，将控制权交给外部，由外部来决定代码什么时候继续执行，这就在一定程度上保持了代码的局部性。 &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;基于类库：&lt;/strong&gt;由于异步编程十分困难，有时候语言层面的支持有限，聪明的程序员们想尽了各种办法来简化异步编程。其中主要的办法就是总结出一种异步模型，并围绕这种模型提供一种异步类库。其中的典型便是&lt;a href="http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html"&gt;“推集合模型”以及“响应式编程”&lt;/a&gt;。只可惜想要基于死板的Java语言开发出好用的异步类库也总是使不出劲，与它形成鲜明对比的便是Scala语言，基于同样的平台，生产力却天下地下。 &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;基于语言：&lt;/strong&gt;有时候，语言设计者会直接在语言层面上简化异步编程的问题，其中的典型便是Erlang语言以及&lt;a href="http://blog.zhaojie.me/2010/10/pdc2010-the-future-of-csharp-and-vb-by-anders-hejlsberg-1.html"&gt;下个版本的C#&lt;/a&gt;。不过这里我主要讨论的是&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html"&gt;F#语言里的异步工作流&lt;/a&gt;。不过严格来说，F#的异步工作流是个类库，它使用了F#的“计算表达式”特性，这才是个语言特性。我参考了F#的异步工作流，开发Jscex（一个JavaScript至JavaScript编译器）以及Jscex.Async组件（基于Jscex的异步类库）。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;由于TUP上的失策，我为一个星期后的.NET技术大会上重新组织了内容，减少了“代码赏析”的数量，增加了演示用的示例。我的演示是在Mac OS上基于Mono 2.8和MonoMac编写的带有Mac OS原生界面的应用程序，以此体现异步编程对于各平台上UI编程的重要性。有了TUP的经验，这次的演讲反响不错，我也被评为这次会议Top 5讲师。幻灯片如下（&lt;a href="http://files.zhaojie.me/slides/async-evolution/async-evolution-netchina.pdf"&gt;下载&lt;/a&gt;）：&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_6103757"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="The Evolution of Async-Programming on .NET Platform (.Net China, C#)" href="http://www.slideshare.net/jeffz/async-evolutiondotnetconf"&gt;The Evolution of Async-Programming on .NET Platform (.Net China, C#)&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse6103757" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-dotnetconf-101210113849-phpapp02&amp;stripped_title=async-evolutiondotnetconf&amp;userName=jeffz" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse6103757" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-dotnetconf-101210113849-phpapp02&amp;stripped_title=async-evolutiondotnetconf&amp;userName=jeffz" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;我为SD 2.0大会使用JavaScript重新编写了所有的示例，并在创新院内部的分享交流会上进行了试讲。试讲的反馈是“干货太多”，这使得我重新提炼了一下示例。不过在大会的前一天，我还是补充了一个基于node.js开发的最简单的静态文件服务器，以此表示Jscex并非只能在浏览器里使用。幻灯片如下（&lt;a href="http://files.zhaojie.me/slides/async-evolution/async-evolution-sd2.pdf"&gt;下载&lt;/a&gt;）：&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_6103746"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="The Evolution of Async-Programming (SD 2.0, JavaScript)" href="http://www.slideshare.net/jeffz/async-evolutionsd2"&gt;The Evolution of Async-Programming (SD 2.0, JavaScript)&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse6103746" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-sd2-101210113722-phpapp01&amp;stripped_title=async-evolutionsd2&amp;userName=jeffz" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse6103746" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=async-evolution-sd2-101210113722-phpapp01&amp;stripped_title=async-evolutionsd2&amp;userName=jeffz" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;有趣的是，在我之前的一场演讲接近尾声的时候，会场里大约只坐了一半人，因此我一开始还担心没人来听。幸运的是在演讲开始时位子已经基本坐满了，后来还有一些人站在一旁。演讲之后的反馈很少，不过都是正面的。会后我还和业界的一些JavaScript大牛交流了一下Jscex，这也是我接下来一段时间的一个工作重点，希望能够做大做好。&lt;/p&gt;

&lt;p&gt;这个演出季已经过去了，接下来又到了nBazaar交流会的时间了。第三届交流会将在1月15日举行，具体消息将在不久之后公开，敬请关注。我们又准备了四场有价值的演讲，一定能让您满意。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/12/event-season-async-evolution-slides.html#comments</comments>
      <pubDate>Sat, 11 Dec 2010 14:01:05 GMT</pubDate>
      <lastBuildDate>Mon, 13 Dec 2010 07:16:55 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <title>适合JavaScript 1.7中迭代生成器的异步编程机制</title>
      <link>http://blog.zhaojie.me/2010/12/javascript-17-yield-async-programming.html</link>
      <guid>http://blog.zhaojie.me/2010/12/javascript-17-yield-async-programming.html</guid>
      <description>&lt;p&gt;&lt;a href="http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html"&gt;上篇文章&lt;/a&gt;我提出了一种基于JavaScript 1.7中迭代生成器（yield）的异步编程方式，它可以让混乱的异步代码逻辑变得清晰一些。不过之前的AsyncIterator其实是对&lt;a href="http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield.html"&gt;基于C# 2.0的AsyncEnumerator&lt;/a&gt;的仿制品，在公司的分享会上进行交流以后，同事&lt;a href="http://hax.javaeye.com/"&gt;hax&lt;/a&gt;提出其实可以实现地更漂亮一些。在他的提示下，我了解到JavaScript 1.7中不同于C# 2.0里的特性，因而对这种异步编程机制提出了改进。只可惜yield特性被ECMAScript 5排除了，这实在可以说是&lt;a href="http://en.wikipedia.org/wiki/Design_by_committee"&gt;委员会设计模式&lt;/a&gt;的又一次伟大胜利。&lt;/p&gt;

&lt;h1&gt;JavaScript 1.7的yield&lt;/h1&gt;

&lt;p&gt;与C#中yield功能不同的是，JavaScript 1.7的yield语句可以让生成器内部获得一个值。例如这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;numbers = &lt;span style="color: blue"&gt;function &lt;/span&gt;(min, max) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = min; i &amp;lt;= max; i++) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;sum = &lt;span style="background-color: yellow; color: blue"&gt;yield&lt;/span&gt; i;
        document.write(sum + &lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;quot;&lt;/span&gt;);
    }
}

&lt;span style="color: blue"&gt;var &lt;/span&gt;iterator = numbers(1, 10);
&lt;span style="color: blue"&gt;var &lt;/span&gt;sum = iterator.next();
&lt;span style="color: blue"&gt;try &lt;/span&gt;{
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;i = iterator.&lt;font color="#ff0000"&gt;send&lt;/font&gt;(sum);
        document.write(i + &lt;span style="color: maroon"&gt;&amp;quot; - &amp;quot;&lt;/span&gt;);
        sum += i;
    }
} &lt;span style="color: blue"&gt;catch &lt;/span&gt;(err &lt;span style="color: blue"&gt;if &lt;/span&gt;err &lt;span style="color: blue"&gt;instanceof &lt;/span&gt;StopIteration) { }&lt;/pre&gt;

&lt;p&gt;执行后的结果是：&lt;/p&gt;

&lt;pre class="code"&gt;1
2 - 3
3 - 6
4 - 10
5 - 15
6 - 21
7 - 28
8 - 36
9 - 45
10 - 55&lt;/pre&gt;

&lt;p&gt;在JavaScript 1.7中，yield除了停止流程，将控制权交给外部之外，还能够返回一个值。这个值由外部控制的代码send至迭代器内部——不过，“启动”一个迭代器则不能使用带参数的send。如上面的代码，我们在while循环外使用next启动一个迭代器。&lt;/p&gt;

&lt;h1&gt;异步编程模型&lt;/h1&gt;

&lt;p&gt;在上一篇文章中，我们把异步编程模型统一为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; beginXxx = &lt;span style="color: blue"&gt;function&lt;/span&gt; (arg0, arg1, ..., callback) { ... }&lt;/pre&gt;

&lt;p&gt;调用这个异步方法时，我们需要显示地提供一个callback回调函数。而如今我们已经有了进一步的打算，则需要对其进行修改，如下所示：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;xxxAsync = &lt;span style="color: blue"&gt;function &lt;/span&gt;(arg1, arg2, ...) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start: &lt;span style="color: blue"&gt;function &lt;/span&gt;(callback) { ... }
    }
}&lt;/pre&gt;

&lt;p&gt;简单地说，以前的beginXxx方法会直接发起一个异步请求，而如今的xxxAsync方法，则是返回一个表示“异步任务”的对象，这个对象上有一个start方法，接受一个callback函数作为参数，并启动这个异步任务。待异步任务完成时，使用callback函数发起通知并回传结果。基于这个异步模型，我们可以封装一些常用的“异步任务”，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;sleepAsync = &lt;span style="color: blue"&gt;function &lt;/span&gt;(ms) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start : &lt;span style="color: blue"&gt;function &lt;/span&gt;(callback) {
            window.setTimeout(callback, ms);
        }
    };
}&lt;/pre&gt;

&lt;p&gt;sleepAsync获得一个“休眠”的异步任务，执行时便会使用setTimeout计时回调。另一个常见的异步任务则是AJAX请求：&lt;/p&gt;

&lt;pre class="code"&gt;XMLHttpRequest.prototype.receiveAsync = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;_this = &lt;span style="color: blue"&gt;this&lt;/span&gt;;
    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start : &lt;span style="color: blue"&gt;function&lt;/span&gt;(callback) {
            _this.onreadystatechange = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;.readyState == 4) {
                    callback(_this.responseText);
                }
            }

            _this.send();
        }
    };
}&lt;/pre&gt;

&lt;p&gt;我为XMLHttpRequest做了扩展，它的receiveAsync方法将获得一个异步任务，这个异步任务的执行结果便是这个请求的responseText值。&lt;/p&gt;

&lt;h1&gt;辅助类：AsyncTask&lt;/h1&gt;

&lt;p&gt;之前的辅助对象是AsyncIterator，而现在我们只需根据一个迭代器新建一个异步任务即可：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;AsyncTask = &lt;span style="color: blue"&gt;function&lt;/span&gt;(iterator) {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._iterator = iterator;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
}
AsyncTask.prototype._loop = &lt;span style="color: blue"&gt;function&lt;/span&gt;(result) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;_this = &lt;span style="color: blue"&gt;this&lt;/span&gt;;
    &lt;span style="color: blue"&gt;try &lt;/span&gt;{
        &lt;span style="color: blue"&gt;var &lt;/span&gt;task = &lt;span style="color: blue"&gt;this&lt;/span&gt;._iterator.send(result);
        task.start(&lt;span style="color: blue"&gt;function&lt;/span&gt;(r) { _this._loop(r); });
    } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(err &lt;span style="color: blue"&gt;if &lt;/span&gt;err &lt;span style="color: blue"&gt;instanceof &lt;/span&gt;StopIteration) {  
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;._callback) &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback();
    }
}
AsyncTask.prototype.start = &lt;span style="color: blue"&gt;function&lt;/span&gt;(callback) {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback = callback;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._loop();
}&lt;/pre&gt;

&lt;p&gt;作为符合我们异步编程模型的对象，AsyncTask也包含一个start方法，它会调用_loop，在_loop中则从迭代器里获得下一个异步任务，启动，并在它执行结束时继续调用_loop自身。在新的异步模型中，构建一个辅助类库也变得非常容易。&lt;/p&gt;

&lt;h1&gt;示例1：移动HTML元素&lt;/h1&gt;

&lt;p&gt;还是使用上次的例子。实现一个移动HTML元素的逻辑其实很简单，只要根据一个时间间隔不断地改变其top和left即可。例如这样：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// Pseudocode, cannot work
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;move = &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
        e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
        e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
        sleep(50); &lt;span style="color: #006400"&gt;// cannot sleep
    &lt;/span&gt;}

    e.style.left = endPos.x;
    e.style.top = endPos.y;
}&lt;/pre&gt;

&lt;p&gt;只可惜上面这段代码是无法运行的，因为在浏览器里我们没有任何手段让当前的工作线程暂停，我们没有一个阻塞的同步的sleep方法。不过我们现在有了sleepAsync方法可以提供一个异步任务。因此，moveAsync方法只能这样编写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveAsync = &lt;span style="color: blue"&gt;function&lt;/span&gt;(e, startPos, endPos, duration) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start: &lt;span style="color: blue"&gt;function&lt;/span&gt;(callback) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0;
            &lt;span style="color: blue"&gt;var &lt;/span&gt;loop = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                &lt;span style="color: blue"&gt;if &lt;/span&gt;(t &amp;lt; duration) {
                    t += 50;
                    e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
                    e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
                    sleepAsync(50).start(loop);
                } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
                    &lt;span style="color: blue"&gt;if &lt;/span&gt;(callback) callback();
                }
            }
        
            loop();
        }
    };
}&lt;/pre&gt;

&lt;p&gt;我们无法使用for循环，只能把循环拆成loop回调。这就是异步代码破坏了代码局部性的例证。可能有些朋友会觉得这样的代码写起来没什么困难的，那么如果再加上if…else或是try…catch呢？不管怎么样，这段代码破坏了程序员编程思路，我觉得实在太丑了。幸好，使用AsyncTask和迭代生成器之后代码完全不需要这么写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// beginMove with AsyncTask
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveAsync2 = &lt;span style="color: blue"&gt;function&lt;/span&gt;(e, startPos, endPos, duration) {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;generator = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
            e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
            e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
            &lt;span style="background-color: yellow"&gt;&lt;span style="color: blue"&gt;yield&lt;/span&gt; sleepAsync(50);&lt;/span&gt;
        }

        e.style.left = endPos.x;
        e.style.top = endPos.y;
    };

    &lt;span style="color: blue"&gt;return new &lt;/span&gt;AsyncTask(generator());
}&lt;/pre&gt;

&lt;p&gt;调用beginSleep之后代码使用yield将控制权交还给了AsyncIterator，而beginSleep完成之后也会通知AsyncIterator并继续这段逻辑。有了yield，我们的代码编写起来便顺畅多了。与之前yield只是用作“交出控制权”相比，如今的yield直接返回一个异步任务，可谓干净清爽，语义优雅。&lt;/p&gt;

&lt;h1&gt;示例二：获取多个URL的内容&lt;/h1&gt;

&lt;p&gt;如果有一个URL数组，要编写一个异步方法，返回这个URL数组对应的请求内容。如今我们可以这么写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;receiveAllAsync = &lt;span style="color: blue"&gt;function&lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;result = [];

    &lt;span style="color: blue"&gt;var &lt;/span&gt;generator = &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;req = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
            req.open(&lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, urls[i]);
            &lt;span style="background-color: yellow"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;content = &lt;span style="color: blue"&gt;yield&lt;/span&gt; req.receiveAsync();&lt;/span&gt;
            result.push(content);
        }
    };

    &lt;span style="color: blue"&gt;return &lt;/span&gt;{
        start: &lt;span style="color: blue"&gt;function&lt;/span&gt;(callback) {
            (&lt;span style="color: blue"&gt;new &lt;/span&gt;AsyncTask()).start(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                callback(result);
            });
        }
    };
}&lt;/pre&gt;

&lt;p&gt;与上例有所不同的是，我们通过yield语句直接得到了receiveAsync异步任务的结果。比较可惜的一点是，无论是之前的AsyncIterator还是现在的AsyncTask都对需要返回值的异步方法不是十分友好，写法有些绕。这也是yield受到功能本身的限制，可见语言特性的确对编程模型的塑造有十分重要的影响：C# 2.0的异步编程模型不如JavaScript 1.7，JavaScript 1.7的异步编程模型却不如&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;。&lt;/p&gt;

&lt;h1&gt;总结&lt;/h1&gt;

&lt;p&gt;总体而言，有了JavaScript 1.7中的迭代生成器，在许多时候已经可以极大地简化异步操作的编程体验了。只可惜JavaScript 1.7只是Mozilla一家的语言，虽然在ECMAScript 4中也有类似功能，但在ECMAScript 5却已经被废弃了。在可预见的将来，这个语言特性不会被各浏览器或是JavaScript引擎所接受。实在可以说是&lt;a href="http://en.wikipedia.org/wiki/Design_by_committee"&gt;委员会设计模式&lt;/a&gt;的又一次伟大胜利——他们就是不希望开发人员的生活能够好过一些。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/12/javascript-17-yield-async-programming.html#comments</comments>
      <pubDate>Fri, 03 Dec 2010 11:09:53 GMT</pubDate>
      <lastBuildDate>Fri, 03 Dec 2010 11:10:16 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <title>JavaScript版本的AsyncEnumerator</title>
      <link>http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html</link>
      <guid>http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html</guid>
      <description>&lt;p&gt;地球人都知道，在C# 2.0里提供了yield关键字，可以方便好用地生成一个迭代器，更可以&lt;a href="http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield.html"&gt;简化异步操作&lt;/a&gt;——这是因为有了Jeffrey Richter开发的AsyncEnumerator。在接下来的某些演讲中我准备的主题是“异步编程模型”的演变，自然少不了这非常重要的一环。为了便于广大人民群众更好地接受，我决定使用JavaScript来进行说明。为此，我用JavaScript实现了一个AsyncEnumerator。&lt;/p&gt;

&lt;h1&gt;JavaScript 1.7里的Iterator生成器&lt;/h1&gt;

&lt;p&gt;AsyncEnumerator的关键在于yield，yield可以让开发者&lt;a href="http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html"&gt;轻易实现出一个迭代器&lt;/a&gt;。只可惜在&lt;a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm"&gt;ECMA-262&lt;/a&gt;里并没有定义这个功能，还好在&lt;a href="https://developer.mozilla.org/en/New_in_javascript_1.7"&gt;FireFox 2.0之后里实现了JavaScript 1.7&lt;/a&gt;，其中便提供了&lt;a href="https://developer.mozilla.org/en/JavaScript/Guide/Iterators_and_Generators"&gt;Iterator的生成器&lt;/a&gt;。这里我先做一个简单的介绍。&lt;/p&gt;

&lt;p&gt;例如，基于JavaScript 1.7实现无限长的菲波纳妾数列，只要：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;fibonacci() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;fn1 = 1;
    &lt;span style="color: blue"&gt;var &lt;/span&gt;fn2 = 1;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;current = fn2;
        fn2 = fn1;
        fn1 = fn1 + current;
        &lt;span style="background-color: yellow"&gt;&lt;span style="color: blue"&gt;yield&lt;/span&gt; current;&lt;/span&gt;
    }
}&lt;/pre&gt;

&lt;p&gt;在使用的时候，JavaScript 1.7里也提供了比较方便的语法，例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i &lt;span style="color: blue"&gt;in &lt;/span&gt;fibonacci()) {
    print(i);
}&lt;/pre&gt;

&lt;p&gt;就像C#中的foreach是使用了MoveNext方法和Current属性一样，其实JavaScript 1.7中针对迭代器的for…in语法也是使用了迭代器对象上的next方法。例如上面的代码便可以修改为：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;iterator = fibonacci();
&lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
    print(iterator.next());
}&lt;/pre&gt;

&lt;p&gt;您可能会想，这为什么是一个死循环？这是因为在JavaScript 1.7中，而每次调用next方法都会得到yield返回的某一项，同时通过抛出“StopIteration”来表示“迭代终止”。一般来说不需要手动维护异常，因为这些都交给for…in处理了。&lt;/p&gt;

&lt;h1&gt;异步模型&lt;/h1&gt;

&lt;p&gt;无论是哪种“异步编程模型”，首先都需要总结出一种通用的“异步任务模型”，例如.NET中的Begin/End或是基于事件的异步模型。为了简化问题，我在这里提出一种最为简单的异步编程：每个异步操作都是以下形式的：&lt;/p&gt;

&lt;pre class="code"&gt;beginXxx(arg0, arg1, ..., callback);&lt;/pre&gt;

&lt;p&gt;例如我们来扩展一下XMLHttpRequest类型，提供一个beginReceive方法，最终返回responseText：&lt;/p&gt;

&lt;pre class="code"&gt;XMLHttpRequest.prototype.beginReceive = &lt;span style="color: blue"&gt;function &lt;/span&gt;(callback) {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;.onreadystatechange = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;.readyState == 4) {
            callback(&lt;span style="color: blue"&gt;this&lt;/span&gt;.responseText);
        }
    }

    &lt;span style="color: blue"&gt;this&lt;/span&gt;.send();
}&lt;/pre&gt;

&lt;p&gt;同样，我们提供一个beginSleep方法，它仅仅是一个setTimeout函数的简单封装：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginSleep = &lt;span style="color: blue"&gt;function &lt;/span&gt;(ms, callback) {
    window.setTimeout(callback, ms);
}&lt;/pre&gt;

&lt;p&gt;调用beginXxx方法则表示发起一个异步的Xxx操作，其结果会通过callback回调函数传递过来。“回调”是“异步”的精髓，尽管它经常让我们痛苦万分。一般来说，无论是何种异步模型（甚至不关.NET还是JavaScript），最终都是基于回调函数的，最终其实也可以归纳成这里的异步调用形式。&lt;/p&gt;

&lt;h1&gt;AsyncIterator&lt;/h1&gt;

&lt;p&gt;下面我们便来仿造AsyncEnumerator编写一个AsyncIterator。AsyncEnumerator的原理很简单，我们在这里继续将其简化，由于是JavaScript，我们还不需要处理多线程之间的竞争，可谓再简单不过了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;AsyncIterator = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._result = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._iterator = &lt;span style="color: blue"&gt;null&lt;/span&gt;;
}
AsyncIterator.prototype.callback = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;_this = &lt;span style="color: blue"&gt;this&lt;/span&gt;;
    &lt;span style="color: blue"&gt;return function &lt;/span&gt;(result) {
        _this._result = result;
        &lt;span style="color: blue"&gt;try &lt;/span&gt;{
            _this._iterator.next();
        } &lt;span style="color: blue"&gt;catch &lt;/span&gt;(e) {
            _this._callback();
        }
    };
}
AsyncIterator.prototype.result = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
    &lt;span style="color: blue"&gt;return this&lt;/span&gt;._result;
}
AsyncIterator.prototype.beginInvoke = &lt;span style="color: blue"&gt;function &lt;/span&gt;(iterator, callback) {
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._iterator = iterator;
    &lt;span style="color: blue"&gt;this&lt;/span&gt;._callback = callback;

    iterator.next();
}&lt;/pre&gt;

&lt;p&gt;AsyncIterator的原理和使用方式可以用以下几句话描述清楚：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;异步代码将异步操作的结果提交至AsyncIterator提供的回调函数中（通过调用callback方法获得）。 &lt;/li&gt;

  &lt;li&gt;异步代码在发起一个异步操作之后，使用yield将控制权交还给外部（其实就是AsyncIterator）。 &lt;/li&gt;

  &lt;li&gt;异步操作完成后会执行AsyncIterator提供的回调函数，AsyncIterator则调用迭代器的next方法，继续执行代码。 &lt;/li&gt;

  &lt;li&gt;异步代码可以从AsyncIterator的result方法中获得上一个异步操作的结果。 &lt;/li&gt;

  &lt;li&gt;当next方法抛出异常时，表示迭代器执行完毕，AsyncIterator则通过回调函数进行通知。 &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了便于使用，我为AsyncIterator提供一个“静态方法”：&lt;/p&gt;

&lt;pre class="code"&gt;AsyncIterator.beginInvoke = &lt;span style="color: blue"&gt;function &lt;/span&gt;(generator, callback) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;ai = &lt;span style="color: blue"&gt;new &lt;/span&gt;AsyncIterator();
    &lt;span style="color: blue"&gt;var &lt;/span&gt;iterator = generator(ai);
    ai.beginInvoke(iterator, callback);
}&lt;/pre&gt;

&lt;p&gt;接下来，我们便来看两个实例。&lt;/p&gt;

&lt;h1&gt;示例1：移动HTML元素&lt;/h1&gt;

&lt;p&gt;实现一个移动HTML元素的逻辑其实很简单，只要根据一个时间间隔不断地改变其top和left即可。例如这样：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// Pseudocode, cannot work
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;move = &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
        e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
        e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
        sleep(50); &lt;span style="color: #006400"&gt;// cannot sleep
    &lt;/span&gt;}

    e.style.left = endPos.x;
    e.style.top = endPos.y;
}&lt;/pre&gt;

&lt;p&gt;只可惜上面这段代码是无法运行的，因为在浏览器里我们没有任何手段让当前的工作线程暂停，我们没有一个阻塞的同步的sleep方法，只有一个异步的beginSleep方法（如上文）。因此，其实beginMove方法只能这样编写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginMove = &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration, callback) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0;

    &lt;span style="color: blue"&gt;var &lt;/span&gt;loop = &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(t &amp;lt; duration) {
            t += 50;
            e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
            e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
            beginSleep(50, loop);
        } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
            callback();
        }
    }

    loop();
}&lt;/pre&gt;

&lt;p&gt;我们无法使用for循环，只能把循环拆成loop回调。这就是异步代码破坏了代码局部性的例证。可能有些朋友会觉得这样的代码写起来没什么困难的，那么如果再加上if…else或是try…catch呢？不管怎么样，这段代码破坏了程序员编程思路，我觉得实在太丑了，使用AsyncIterator便会直观许多：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginMove2 = &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration, callback) {

    &lt;span style="color: blue"&gt;var &lt;/span&gt;generator = &lt;span style="color: blue"&gt;function &lt;/span&gt;(ai) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
            e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
            e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
            beginSleep(50, &lt;span style="background-color: yellow"&gt;ai.callback()&lt;/span&gt;);
            &lt;span style="background-color: yellow"&gt;&lt;span style="color: blue"&gt;yield &lt;/span&gt;0;&lt;/span&gt;
        }

        e.style.left = endPos.x;
        e.style.top = endPos.y;
    };

    AsyncIterator.beginInvoke(generator, callback);
}&lt;/pre&gt;

&lt;p&gt;调用beginSleep之后代码使用yield将控制权交还给了AsyncIterator，而beginSleep完成之后也会通知AsyncIterator并继续这段逻辑。有了yield，我们的代码编写起来便顺畅多了。&lt;/p&gt;

&lt;h1&gt;示例2：批量请求数据&lt;/h1&gt;

&lt;p&gt;如果现在有一个urls数组，其中包含了目标地址，您如何将它们的结果也通过一个数组返回过来呢？伪代码如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #006400"&gt;// Pseudocode, cannot work
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;receiveMany = &lt;span style="color: blue"&gt;function &lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;results = [];

    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;req = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
        req.open(&lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, urls[i]);
        &lt;span style="color: blue"&gt;var &lt;/span&gt;r = req.receive(); &lt;span style="color: #006400"&gt;// cannot recieve
        &lt;/span&gt;results.push(r);
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;results;
}&lt;/pre&gt;

&lt;p&gt;很显然实际可以运行的代码只能是异步的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginReceiveMany = &lt;span style="color: blue"&gt;function &lt;/span&gt;(urls, callback) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;results = [];

    &lt;span style="color: blue"&gt;var &lt;/span&gt;loop = &lt;span style="color: blue"&gt;function &lt;/span&gt;(i) {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(i &amp;lt; urls.length) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;req = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
            req.open(&lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, urls[i]);
            req.beginReceive(&lt;span style="color: blue"&gt;function &lt;/span&gt;(r) {
                results.push(r);
                loop(i + 1);
            });
        } &lt;span style="color: blue"&gt;else &lt;/span&gt;{
            callback(results);
        }
    }

    loop(0);
}&lt;/pre&gt;

&lt;p&gt;之前我们扩展了XMLHttpRequest，提供了一个beginReceive方法。于是我们在回调中驰骋，最终实现了beginReceiveMany方法。如果用AsyncIterator则会方便一些，只可惜它对“带有返回值”的异步操作支持并不友好，因此还是有点绕：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;beginReceiveMany2 = &lt;span style="color: blue"&gt;function &lt;/span&gt;(urls, callback) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;results = [];

    &lt;span style="color: blue"&gt;var &lt;/span&gt;generator = &lt;span style="color: blue"&gt;function&lt;/span&gt;(ai) {
        &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;req = &lt;span style="color: blue"&gt;new &lt;/span&gt;XMLHttpRequest();
            req.open(&lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;, urls[i]);
            req.beginReceive(&lt;span style="background-color: yellow"&gt;ai.callback()&lt;/span&gt;);
            yield 0;
            results.push(&lt;span style="background-color: yellow"&gt;ai.result()&lt;/span&gt;);
        }
    }
    
    AsyncIterator.beginInvoke(generator, &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        callback(results);
    });
}&lt;/pre&gt;

&lt;p&gt;幸好有JavaScript的闭包在，总体来说还算方便。在这个例子中，AsyncIterator会得到beginReceive从回调函数中提交上来的结果，然后代码便可以从result方法里获得并保存。&lt;/p&gt;

&lt;h1&gt;更多&lt;/h1&gt;

&lt;p&gt;AsyncIterator虽好，只可惜只能在FireFox里使用，这自然无法推广。幸好我们还可以用&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;，而且这一切都没有Jscex来的简单，正如之前所提到的那样，在Jscex中一段移动HTML元素的动画只需这样写：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration) {
    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;t = 0; t &amp;lt; duration; t += 50) {
        e.style.left = startPos.x + (endPos.x - startPos.x) * t / duration;
        e.style.top = startPos.y + (endPos.y - startPos.y) * t / duration;
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sleep(50));
    }

    e.style.left = endPos.x;
    e.style.top = endPos.y;
}));&lt;/pre&gt;

&lt;p&gt;批量请求数据也是最为直观的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;getMultiContentAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(urls) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;result = [];

    &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; i &amp;lt; urls.length; i++) {
        &lt;span style="color: blue"&gt;var &lt;/span&gt;content = &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(getContentAsync(urls[i]));
        result.push(content);
    }

    &lt;span style="color: blue"&gt;return &lt;/span&gt;result;
}));&lt;/pre&gt;

&lt;p&gt;甚至&lt;a href="http://files.zhaojie.me/demos/jscex-hanoi/hanoi.html?n=5"&gt;汉诺塔&lt;/a&gt;的解法，也完全是最最直观的递归算法：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;hanoiAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(n, a, b, c) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(n &amp;gt; 0) {
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(hanoiAsync(n - 1, a, c, b));
    }

    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveDishAsync(n, a, b));

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(n &amp;gt; 0) {
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(hanoiAsync(n - 1, c, b, a));
    }
}));&lt;/pre&gt;

&lt;p&gt;关于异步编程模型的演变，以及Jscex的更多内幕，本周三下午4点我将在创新院内部开展一次分享会，&lt;span style="color:red;"&gt;欢迎创新院外的朋友前来一起交流&lt;/span&gt;。这也是我在即将举办的&lt;a href="http://sd2china.csdn.net/"&gt;SD 2.0&lt;/a&gt;大会上的一次预演。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/11/asynciterator-the-asyncenumerator-in-javascript.html#comments</comments>
      <pubDate>Mon, 29 Nov 2010 14:25:50 GMT</pubDate>
      <lastBuildDate>Mon, 29 Nov 2010 17:18:39 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/news/">新闻信息</category>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <title>关于即将到来的“演出季”以及Jscex类库</title>
      <link>http://blog.zhaojie.me/2010/11/the-coming-talks-and-jscex.html</link>
      <guid>http://blog.zhaojie.me/2010/11/the-coming-talks-and-jscex.html</guid>
      <description>&lt;p&gt;又到了一年一度的“演出季”，接下来将是各式会议扑面而来的一个月。作为“与会爱好者”我自然也进入了繁忙的准备工作。接下来我将在&lt;a href="http://tup.csdn.net/"&gt;TUP&lt;/a&gt;（11月27日）、&lt;a href="http://www.net-china.org/"&gt;2010年第二届.NET技术大会&lt;/a&gt;（12月4~5日）以及&lt;a href="http://sd2china.csdn.net/"&gt;CSDN软件开发2.0大会&lt;/a&gt;（12月9~10日）上与大家分享四场演讲。不过除了一场是关于Windows并发编程的基础以外，其余三场的话题都是围绕“微软在异步编程方面的演变”。在这场演讲中，我还会引入一个与该话题密切相关的JavaScript类库：Jscex。&lt;/p&gt;

&lt;p&gt;并发编程已经成为我们无法回避的一点，但是传统的编程语言在并发、异步等方面的支持非常有限，除了一些面向并发的编程语言（如Erlang），我们只能使用回调的方式，这样代码则被拆得支离破碎。如今的一些新语言，如F#，Scala，Go以及未来的C#都会在这方面有直接的支持。就我个人而言，用过了这些语言之后，就几乎无法回头，因为我实在无法体会到编程的快感。&lt;/p&gt;

&lt;p&gt;如今微软的.NET平台在异步编程领域已经开始发力，从最初十分原始的Begin/End的异步模型和基于事件的异步模型，到后来&lt;a href="http://blog.zhaojie.me/2010/03/async-and-parallel-design-patterns-in-fsharp-1-parallelizing-cpu-and-io-computations.html"&gt;F#中的异步工作流&lt;/a&gt;，.NET 4.0中的&lt;a href="http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html"&gt;Observable及响应式框架&lt;/a&gt;，以及&lt;a href="http://blog.zhaojie.me/2010/10/pdc2010-the-future-of-csharp-and-vb-by-anders-hejlsberg-1.html"&gt;未来C#中将要出现的异步特性&lt;/a&gt;，.NET平台上涌现了各种与并发和异步编程的机制。未来几场演讲的主要话题，便是关于这些异步编程模型的演变。&lt;/p&gt;

&lt;p&gt;只可惜，在某些和异步密切相关的平台上，例如使用JavaScript的浏览器平台，开发人员还是不得不利用最传统的开发模型。响应式框架通过引入一个“推模型”改进了这一点，不过我最欣赏的异步编程支持还是F#上的异步工作流。首先，它是一套“类库”（基于F#中的计算表达式特性）；其次，它让异步编程变得像传统的顺序式开发那样简单。有天我突然意识到，JavaScript其实提供一个很强大的机制：它可以使用toString方法得到一个函数的原始代码，于是我们可以对此解析并生成新的代码，最后使用eval得到新的函数。通过这个手法，JavaScript语言几乎得到了无限的灵活性。&lt;/p&gt;

&lt;p&gt;于是在未来的演讲中，我将会展示一个JavaScript类库的原型：&lt;a href="https://github.com/JeffreyZhao/jscex"&gt;Jscex&lt;/a&gt;（目前还未提交任何代码）。Jscex是&lt;font color="#ff0000"&gt;J&lt;/font&gt;ava&lt;font color="#ff0000"&gt;S&lt;/font&gt;cript &lt;font color="#ff0000"&gt;C&lt;/font&gt;omputation &lt;font color="#ff0000"&gt;Ex&lt;/font&gt;pressions的缩写，即“JavaScript的计算表达式”。计算表达式的原理，便是将一段顺序式的代码，重新编译成另外一个表达式，它便是F#中异步工作流的基础。&lt;/p&gt;

&lt;p&gt;例如，它会将下面这段JavaScript函数：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(urlA, urlB) {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;reqA = &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sendRequest(urlA, &lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;));
    &lt;span style="color: blue"&gt;var &lt;/span&gt;lengthA = reqA.responseText.length;

    &lt;span style="color: blue"&gt;var &lt;/span&gt;reqB = &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sendRequest(urlB, &lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;));
    &lt;span style="color: blue"&gt;var &lt;/span&gt;lengthB = reqB.responseText.length;

    &lt;span style="color: blue"&gt;return &lt;/span&gt;lengthA + lengthB;
}&lt;/pre&gt;

&lt;p&gt;编译成另一段代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function&lt;/span&gt; (urlA, urlB) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Delay(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(Jscex.Async.sendRequest(urlA, &lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;), &lt;span style="color: blue"&gt;function&lt;/span&gt;(reqA) {
            &lt;span style="color: blue"&gt;var &lt;/span&gt;lengthA = reqA.responseText.length;
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(Jscex.Async.sendRequest(urlB, &lt;span style="color: maroon"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;), &lt;span style="color: blue"&gt;function&lt;/span&gt;(reqB) {
                &lt;span style="color: blue"&gt;var &lt;/span&gt;lengthB = reqB.responseText.length;
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return(lengthA + lengthB);
            });
        });
    });
};&lt;/pre&gt;

&lt;p&gt;对于$async这个构造器来说，$await便是一个bind函数，在此处则会形成一个回调。此外，Jscex也必须能够正确处理循环操作，例如这个&lt;a href="http://files.zhaojie.me/demos/jscex-prototype/clock.html"&gt;时钟示例&lt;/a&gt;（建议使用IE 9，Chrome或Firefox等支持canvas的浏览器观看）其实是这样实现的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;drawClockAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(interval) {
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;) {
        drawClock(&lt;span style="color: blue"&gt;new &lt;/span&gt;Date());
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sleep(interval));
    }
}));

Jscex.Async.start(drawClockAsync(1000));&lt;/pre&gt;

&lt;p&gt;对于时钟来说，我们往往会不断使用window.setTimeout来更新界面。不过有了Jscex，一切都变得“顺其自然”。我们只要写一个“死循环”，在需要“等待”的时候，直接$await一个sleep操作即可。这种看似“阻塞”的代码，其实最终会被重新编译成新的函数：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function&lt;/span&gt;(interval) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Delay(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.While(
            &lt;span style="color: blue"&gt;function&lt;/span&gt;() { &lt;span style="color: blue"&gt;return true&lt;/span&gt;; },
            $async.Delay(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                drawClock(&lt;span style="color: blue"&gt;new &lt;/span&gt;Date);
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(Jscex.Async.sleep(interval), &lt;span style="color: blue"&gt;function&lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return();
                });
            })
        );
    });
};&lt;/pre&gt;

&lt;p&gt;实际上，它们都是异步的。&lt;/p&gt;

&lt;p&gt;其实在浏览器上有太多的异步场景。例如一个AJAX请求，或者是一段动画效果。这里我还准备了&lt;a href="http://files.zhaojie.me/demos/jscex-prototype/animation.html"&gt;第二个示例&lt;/a&gt;，其中有一段移动元素的方法（将元素e在duration时间内从startPos移动至endPos）是这样编写的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e, startPos, endPos, duration) {
    e.style.left = startPos.x;
    e.style.top = startPos.y;

    &lt;span style="color: blue"&gt;var &lt;/span&gt;time = 0;
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(time &amp;lt; duration) {
        &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(Jscex.Async.sleep(50));
        time = time + 50;
        e.style.left = startPos.x + (endPos.x - startPos.x) * time / duration;
        e.style.top = startPos.y + (endPos.y - startPos.y) * time / duration;
    }
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;moveBox = document.getElementById(&lt;span style="color: maroon"&gt;&amp;quot;moveBox&amp;quot;&lt;/span&gt;);
Jscex.Async.start(moveAsync(moveBox, { x: 0, y: 0 }, { x: 300, y: 0 }, 1000));&lt;/pre&gt;

&lt;p&gt;没有回调，只有最普通的顺序式编程。甚至我们还可以将moveAsync方法组合到另一个方法中去：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveSquareAsync = eval(Jscex.compile(&lt;span style="color: maroon"&gt;&amp;quot;$async&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;function &lt;/span&gt;(e) {
    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveAsync(e, { x: 100, y: 100 }, { x: 400, y: 100 }, 1000));
    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveAsync(e, { x: 400, y: 100 }, { x: 400, y: 400 }, 1000));
    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveAsync(e, { x: 400, y: 400 }, { x: 100, y: 400 }, 1000));
    &lt;font color="#ff0000"&gt;$await&lt;/font&gt;(moveAsync(e, { x: 100, y: 400 }, { x: 100, y: 100 }, 1000));
}));

&lt;span style="color: blue"&gt;var &lt;/span&gt;moveSquareBox = document.getElementById(&lt;span style="color: maroon"&gt;&amp;quot;moveSquareBox&amp;quot;&lt;/span&gt;);
Jscex.Async.start(moveSquareAsync(moveSquareBox));&lt;/pre&gt;

&lt;p&gt;如何让一个元素沿方形移动？“连续执行”四遍moveAsync方法即可。当然，moveSquareAsync方法最终还是由“回调”组成的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;(e) {
    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Delay(&lt;span style="color: blue"&gt;function &lt;/span&gt;() {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(moveAsync(e, {...}, {...}, 1000), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
            &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(moveAsync(e, {...}, {...}, 1000), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(moveAsync(e, {...}, {...}, 1000), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                    &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Bind(moveAsync(e, {...}, {...}, 1000), &lt;span style="color: blue"&gt;function &lt;/span&gt;() {
                        &lt;span style="color: blue"&gt;return &lt;/span&gt;$async.Return();
                    });
                });
            });
        });
    });
};&lt;/pre&gt;

&lt;p&gt;我以前在许多地方说过，目前浏览器上的开发效率已经受限于JavaScript的语言特性了。不过，正如我刚才所提到的那样，JavaScript语言的灵活性几乎是无穷的。我认为，Jscex会是一个突破。当然，现在您看到的演示只不过是个原型，如果要提供一个完整的解决方案还有太多的路要走。例如现在的Jscex还不支持for，if，try...catch等常见语言特性，代码也丑陋得很。与此对应的Jscex.Async也只是个初步的模型，且只有寥寥无几的辅助方法。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/11/the-coming-talks-and-jscex.html#comments</comments>
      <pubDate>Mon, 22 Nov 2010 12:30:57 GMT</pubDate>
      <lastBuildDate>Mon, 29 Nov 2010 14:28:28 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <title>使用Narcissus解析JavaScript代码</title>
      <link>http://blog.zhaojie.me/2010/11/narcissus-javascript-parser.html</link>
      <guid>http://blog.zhaojie.me/2010/11/narcissus-javascript-parser.html</guid>
      <description>&lt;p&gt;最近在做一个有关JavaScript的实验，需要在客户端将JavaScript代码解析为一棵语法树。换句话说，就是一个用JavaScript实现的JavaScript解析器。这方面的选择有很多，常见的yacc、lex或是bison等等都有JavaScript的版本，使用ANTLR也可以将生成目标设为JavaScript。不过我不想在这方面耗费太多时间，自然想找个现成的工具，于是最终我将目标放在了Narcissus上。&lt;/p&gt;

&lt;p&gt;Narcissus是一个JavaScript引擎，完全使用JavaScript编写，不过利用了&lt;a href="http://en.wikipedia.org/wiki/SpiderMonkey_(JavaScript_engine)"&gt;SpiderMonkey&lt;/a&gt;的一些扩展，因此无法直接在仅仅实现了ECMAScript 3的引擎上执行（例如各浏览器）。从&lt;a href="http://en.wikipedia.org/wiki/Narcissus_%28JavaScript_engine%29"&gt;它的Wikipedia页面上得知&lt;/a&gt;，Narcissus由SpiderMonkey的作者&lt;a href="http://en.wikipedia.org/wiki/Brendan_Eich"&gt;Brendan Eich&lt;/a&gt;开发，名称来源于&lt;a href="http://en.wikipedia.org/wiki/Narcissus_(mythology)"&gt;希腊神话中爱上自己倒影的人物&lt;/a&gt;，和“JavaScript编写的JavaScript引擎”的概念契合（真是太有文化了）。此外，Firefox有一个Zaphod插件，可以将浏览器的JavaScript引擎替换为Narcissus。&lt;/p&gt;

&lt;p&gt;Narcissus是个十分简单的JavaScript引擎，可以用来做一些JavaScript语言新特性的探索工作。它几乎不做任何优化，因此不能与其他引擎比拼性能，但很显然它包含完整的JavaScript分析器，正好为我所用。首先，&lt;a href="https://github.com/mozilla/narcissus"&gt;从Github上下载它的源代码&lt;/a&gt;，其中包括六个文件，而我只需要其中的三个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;jsdef.js：包含了Narcissus.definitions组件，各种Token定义等等。 &lt;/li&gt;

  &lt;li&gt;jslex.js：包含了Narcissus.lexer组件，分词器。 &lt;/li&gt;

  &lt;li&gt;jsparse.js：包含了Narcissus.parser，分析器。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;之前提到过，Narcissus不能直接在浏览器上运行，因此我们还必须对它进行修改。首先，是在jsdefs.js文件中，我们需要将开头的一段利用Object.create方法的定义：&lt;/p&gt;

&lt;pre class="code"&gt;(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
    &lt;span style="color: blue"&gt;var &lt;/span&gt;builderTypes = Object.create(&lt;span style="color: blue"&gt;null&lt;/span&gt;, {
        ...
    });

    ...

    &lt;span style="color: blue"&gt;var &lt;/span&gt;narcissus = {
        ...&lt;span style="color: blue"&gt;
    &lt;/span&gt;};

    Narcissus = narcissus;
})();&lt;/pre&gt;

&lt;p&gt;替换成直接的声明：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;Narcissus = { };&lt;/pre&gt;

&lt;p&gt;其次还是在jsdefs.js中，我们要改变defineProperty和defineGetter的实现：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;defineGetter(obj, prop, fn, dontDelete, dontEnum) {
    Object.defineProperty(...);
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) {
    Object.defineProperty(...);
}&lt;/pre&gt;

&lt;p&gt;Object的defineProperty和defineGetter方法也是SpiderMonkey的扩展，我们要把它们修改为“直接赋值”的版本：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;defineGetter(obj, prop, fn, dontDelete, dontEnum) {
    obj[prop] = fn;
}

&lt;span style="color: blue"&gt;function &lt;/span&gt;defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) {
    obj[prop] = val;
}&lt;/pre&gt;

&lt;p&gt;当然，这么做与之前的效果并不等价，不过并不影响代码的使用。您可以从jsparse.js文件中找到使用了这两个方法的地方。&lt;/p&gt;

&lt;p&gt;现在您就可以在一个页面里引入这三个JavaScript文件，并Narcissus.parser分析JavaScript代码了。Narcissus几乎没有说明文档，不过从代码中找到它的使用方法并不困难。例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;function &lt;/span&gt;parseSelf() { 
    &lt;span style="color: blue"&gt;var &lt;/span&gt;builder = &lt;span style="color: blue"&gt;new &lt;/span&gt;Narcissus.parser.DefaultBuilder();
    &lt;span style="color: blue"&gt;return &lt;/span&gt;Narcissus.parser.parse(builder, parseSelf.toString(), &lt;span style="color: maroon"&gt;&amp;quot;temp&amp;quot;&lt;/span&gt;, 1);
}

document.write(&lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;pre&amp;gt;&amp;quot; &lt;/span&gt;+ parseSelf() + &lt;span style="color: maroon"&gt;&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;textarea id="parseSelf_tree" style="display:none;"&gt;&amp;lt;pre&amp;gt;{
    type: SCRIPT,
    children: {
        type: FUNCTION,
        body: {
            type: SCRIPT,
            children: {
                type: VAR,
                children: {
                    type: IDENTIFIER,
                    children: ,
                    end: 38,
                    initializer: {
                        type: NEW,
                        children: {
                            type: DOT,
                            children: {
                                type: DOT,
                                children: {
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 55,
                                    lineno: 2,
                                    start: 46,
                                    tokenizer: [object Object],
                                    value: Narcissus
                                },{
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 62,
                                    lineno: 2,
                                    start: 56,
                                    tokenizer: [object Object],
                                    value: parser
                                },
                                end: 62,
                                lineno: 2,
                                start: 46,
                                tokenizer: [object Object],
                                value: .
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 77,
                                lineno: 2,
                                start: 63,
                                tokenizer: [object Object],
                                value: DefaultBuilder
                            },
                            end: 77,
                            lineno: 2,
                            parenthesized: true,
                            start: 46,
                            tokenizer: [object Object],
                            value: .
                        },
                        end: 77,
                        lineno: 2,
                        start: 41,
                        tokenizer: [object Object],
                        value: new
                    },
                    lineno: 2,
                    name: builder,
                    readOnly: false,
                    start: 31,
                    tokenizer: [object Object],
                    value: builder
                },
                destructurings: ,
                end: 38,
                lineno: 2,
                start: 27,
                tokenizer: [object Object],
                value: var
            },{
                type: RETURN,
                children: ,
                end: 90,
                lineno: 3,
                start: 84,
                tokenizer: [object Object],
                value: {
                    type: CALL,
                    children: {
                        type: DOT,
                        children: {
                            type: DOT,
                            children: {
                                type: IDENTIFIER,
                                children: ,
                                end: 100,
                                lineno: 3,
                                start: 91,
                                tokenizer: [object Object],
                                value: Narcissus
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 107,
                                lineno: 3,
                                start: 101,
                                tokenizer: [object Object],
                                value: parser
                            },
                            end: 107,
                            lineno: 3,
                            start: 91,
                            tokenizer: [object Object],
                            value: .
                        },{
                            type: IDENTIFIER,
                            children: ,
                            end: 113,
                            lineno: 3,
                            start: 108,
                            tokenizer: [object Object],
                            value: parse
                        },
                        end: 113,
                        lineno: 3,
                        start: 91,
                        tokenizer: [object Object],
                        value: .
                    },{
                        type: LIST,
                        children: {
                            type: IDENTIFIER,
                            children: ,
                            end: 121,
                            lineno: 3,
                            start: 114,
                            tokenizer: [object Object],
                            value: builder
                        },{
                            type: CALL,
                            children: {
                                type: DOT,
                                children: {
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 132,
                                    lineno: 3,
                                    start: 123,
                                    tokenizer: [object Object],
                                    value: parseSelf
                                },{
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 141,
                                    lineno: 3,
                                    start: 133,
                                    tokenizer: [object Object],
                                    value: toString
                                },
                                end: 141,
                                lineno: 3,
                                start: 123,
                                tokenizer: [object Object],
                                value: .
                            },{
                                type: LIST,
                                children: ,
                                end: 142,
                                lineno: 3,
                                start: 141,
                                tokenizer: [object Object],
                                value: (
                            },
                            end: 142,
                            lineno: 3,
                            start: 123,
                            tokenizer: [object Object],
                            value: (
                        },{
                            type: STRING,
                            children: ,
                            end: 151,
                            lineno: 3,
                            start: 145,
                            tokenizer: [object Object],
                            value: temp
                        },{
                            type: NUMBER,
                            children: ,
                            end: 154,
                            lineno: 3,
                            start: 153,
                            tokenizer: [object Object],
                            value: 1
                        },
                        end: 154,
                        lineno: 3,
                        start: 113,
                        tokenizer: [object Object],
                        value: (
                    },
                    end: 154,
                    lineno: 3,
                    start: 91,
                    tokenizer: [object Object],
                    value: (
                }
            },
            end: 90,
            funDecls: ,
            id: 0,
            lineno: 1,
            start: 21,
            tokenizer: [object Object],
            value: {,
            varDecls: {
                type: IDENTIFIER,
                children: ,
                end: 38,
                initializer: {
                    type: NEW,
                    children: {
                        type: DOT,
                        children: {
                            type: DOT,
                            children: {
                                type: IDENTIFIER,
                                children: ,
                                end: 55,
                                lineno: 2,
                                start: 46,
                                tokenizer: [object Object],
                                value: Narcissus
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 62,
                                lineno: 2,
                                start: 56,
                                tokenizer: [object Object],
                                value: parser
                            },
                            end: 62,
                            lineno: 2,
                            start: 46,
                            tokenizer: [object Object],
                            value: .
                        },{
                            type: IDENTIFIER,
                            children: ,
                            end: 77,
                            lineno: 2,
                            start: 63,
                            tokenizer: [object Object],
                            value: DefaultBuilder
                        },
                        end: 77,
                        lineno: 2,
                        parenthesized: true,
                        start: 46,
                        tokenizer: [object Object],
                        value: .
                    },
                    end: 77,
                    lineno: 2,
                    start: 41,
                    tokenizer: [object Object],
                    value: new
                },
                lineno: 2,
                name: builder,
                readOnly: false,
                start: 31,
                tokenizer: [object Object],
                value: builder
            }
        },
        children: ,
        end: 158,
        functionForm: 0,
        lineno: 1,
        name: parseSelf,
        params: ,
        start: 0,
        tokenizer: [object Object],
        value: function
    },
    funDecls: {
        type: FUNCTION,
        body: {
            type: SCRIPT,
            children: {
                type: VAR,
                children: {
                    type: IDENTIFIER,
                    children: ,
                    end: 38,
                    initializer: {
                        type: NEW,
                        children: {
                            type: DOT,
                            children: {
                                type: DOT,
                                children: {
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 55,
                                    lineno: 2,
                                    start: 46,
                                    tokenizer: [object Object],
                                    value: Narcissus
                                },{
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 62,
                                    lineno: 2,
                                    start: 56,
                                    tokenizer: [object Object],
                                    value: parser
                                },
                                end: 62,
                                lineno: 2,
                                start: 46,
                                tokenizer: [object Object],
                                value: .
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 77,
                                lineno: 2,
                                start: 63,
                                tokenizer: [object Object],
                                value: DefaultBuilder
                            },
                            end: 77,
                            lineno: 2,
                            parenthesized: true,
                            start: 46,
                            tokenizer: [object Object],
                            value: .
                        },
                        end: 77,
                        lineno: 2,
                        start: 41,
                        tokenizer: [object Object],
                        value: new
                    },
                    lineno: 2,
                    name: builder,
                    readOnly: false,
                    start: 31,
                    tokenizer: [object Object],
                    value: builder
                },
                destructurings: ,
                end: 38,
                lineno: 2,
                start: 27,
                tokenizer: [object Object],
                value: var
            },{
                type: RETURN,
                children: ,
                end: 90,
                lineno: 3,
                start: 84,
                tokenizer: [object Object],
                value: {
                    type: CALL,
                    children: {
                        type: DOT,
                        children: {
                            type: DOT,
                            children: {
                                type: IDENTIFIER,
                                children: ,
                                end: 100,
                                lineno: 3,
                                start: 91,
                                tokenizer: [object Object],
                                value: Narcissus
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 107,
                                lineno: 3,
                                start: 101,
                                tokenizer: [object Object],
                                value: parser
                            },
                            end: 107,
                            lineno: 3,
                            start: 91,
                            tokenizer: [object Object],
                            value: .
                        },{
                            type: IDENTIFIER,
                            children: ,
                            end: 113,
                            lineno: 3,
                            start: 108,
                            tokenizer: [object Object],
                            value: parse
                        },
                        end: 113,
                        lineno: 3,
                        start: 91,
                        tokenizer: [object Object],
                        value: .
                    },{
                        type: LIST,
                        children: {
                            type: IDENTIFIER,
                            children: ,
                            end: 121,
                            lineno: 3,
                            start: 114,
                            tokenizer: [object Object],
                            value: builder
                        },{
                            type: CALL,
                            children: {
                                type: DOT,
                                children: {
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 132,
                                    lineno: 3,
                                    start: 123,
                                    tokenizer: [object Object],
                                    value: parseSelf
                                },{
                                    type: IDENTIFIER,
                                    children: ,
                                    end: 141,
                                    lineno: 3,
                                    start: 133,
                                    tokenizer: [object Object],
                                    value: toString
                                },
                                end: 141,
                                lineno: 3,
                                start: 123,
                                tokenizer: [object Object],
                                value: .
                            },{
                                type: LIST,
                                children: ,
                                end: 142,
                                lineno: 3,
                                start: 141,
                                tokenizer: [object Object],
                                value: (
                            },
                            end: 142,
                            lineno: 3,
                            start: 123,
                            tokenizer: [object Object],
                            value: (
                        },{
                            type: STRING,
                            children: ,
                            end: 151,
                            lineno: 3,
                            start: 145,
                            tokenizer: [object Object],
                            value: temp
                        },{
                            type: NUMBER,
                            children: ,
                            end: 154,
                            lineno: 3,
                            start: 153,
                            tokenizer: [object Object],
                            value: 1
                        },
                        end: 154,
                        lineno: 3,
                        start: 113,
                        tokenizer: [object Object],
                        value: (
                    },
                    end: 154,
                    lineno: 3,
                    start: 91,
                    tokenizer: [object Object],
                    value: (
                }
            },
            end: 90,
            funDecls: ,
            id: 0,
            lineno: 1,
            start: 21,
            tokenizer: [object Object],
            value: {,
            varDecls: {
                type: IDENTIFIER,
                children: ,
                end: 38,
                initializer: {
                    type: NEW,
                    children: {
                        type: DOT,
                        children: {
                            type: DOT,
                            children: {
                                type: IDENTIFIER,
                                children: ,
                                end: 55,
                                lineno: 2,
                                start: 46,
                                tokenizer: [object Object],
                                value: Narcissus
                            },{
                                type: IDENTIFIER,
                                children: ,
                                end: 62,
                                lineno: 2,
                                start: 56,
                                tokenizer: [object Object],
                                value: parser
                            },
                            end: 62,
                            lineno: 2,
                            start: 46,
                            tokenizer: [object Object],
                            value: .
                        },{
                            type: IDENTIFIER,
                            children: ,
                            end: 77,
                            lineno: 2,
                            start: 63,
                            tokenizer: [object Object],
                            value: DefaultBuilder
                        },
                        end: 77,
                        lineno: 2,
                        parenthesized: true,
                        start: 46,
                        tokenizer: [object Object],
                        value: .
                    },
                    end: 77,
                    lineno: 2,
                    start: 41,
                    tokenizer: [object Object],
                    value: new
                },
                lineno: 2,
                name: builder,
                readOnly: false,
                start: 31,
                tokenizer: [object Object],
                value: builder
            }
        },
        children: ,
        end: 158,
        functionForm: 0,
        lineno: 1,
        name: parseSelf,
        params: ,
        start: 0,
        tokenizer: [object Object],
        value: function
    },
    id: 0,
    lineno: 1,
    tokenizer: [object Object],
    varDecls: 
}&amp;lt;/pre&amp;gt;&lt;/textarea&gt; 

&lt;p&gt;在JavaScript中调用一个函数的toString方法会得到它的代码，于是执行上面这段代码会打印出&lt;a href="javascript:;" onclick="__showInNewWindow(document.getElementById('parseSelf_tree').value)"&gt;parseSelf方法的语法树&lt;/a&gt;。剩下的我就不多说了，爱玩的同学自然知道可以做什么。&lt;/p&gt;

&lt;p&gt;补：经过实验，Narcissus还是过于依赖SpiderMonkey引擎的特性，如果要在IE上运行还是需要修改更多内容。此外，最新的Narcissus源码还有一些bug，如果您想要使用合适的实现，不妨参考&lt;a href="http://www.neilmix.com/narrativejs/doc/"&gt;NarrativeJS&lt;/a&gt;中旧版的Narcissus代码。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/11/narcissus-javascript-parser.html#comments</comments>
      <pubDate>Wed, 17 Nov 2010 10:08:03 GMT</pubDate>
      <lastBuildDate>Mon, 22 Nov 2010 11:24:36 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>盛大创新院赞助第二届.NET技术交流会 - 演讲录像及下载</title>
      <link>http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-videos.html</link>
      <guid>http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-videos.html</guid>
      <description>&lt;p&gt;经过一个多星期的努力，我们在此为大家奉上&lt;a href="http://blog.zhaojie.me/2010/01/1651772.html"&gt;盛大创新院&lt;/a&gt;赞助&lt;a href="http://blog.zhaojie.me/2010/08/2nd-snda-dotnet-conference-sign-up.html"&gt;第二届.NET技术交流会&lt;/a&gt;的演讲录像。由于录像过程中的一些失误，我们在在讲师录像方面存在着很大问题，经过补救，也只能得到后两场演讲中使用手持设备拍摄下来的录像。在此向大家表示深深的歉意，有了这次的教训，我们以后会更加重视每一个环节的预防及补救措施，尽力避免如现在这样无可挽回的结果。&lt;/p&gt;

&lt;h1&gt;响应式编程与响应式框架&lt;/h1&gt;

&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/zhaojie.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/zhaojie.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;p&gt;讲师：赵劼，盛大创新院，研究员。关注前沿技术，并致力于开源社区与微软平台的组合优化。对函数式编程，并行程序开发，代码之美以及程序员能力与修养等相关问题也有着浓厚的兴趣，同时非常希望能够写程序到60岁。最近致力于F#，Scala语言及mono平台在社区中的推广。&lt;/p&gt;

&lt;p&gt;简介：异步编程改变了我们的编程方式，也为我们带来的许多挑战，同时让一些编程模型重新焕发了生机。与传统的“拉”模型不同，响应式编程将异步事件流视为可观察的集合，这是一种“推”模型。微软为了提高云时代的编程体验而设计了响应式框架，其目的是为了简化复杂事件处理之间混合操作。从中我们了解到一些异步编程的模式与LINQ使用技巧，并可以将这种编程模型普及到JavaScript等其他平台上去。&lt;/p&gt;
&lt;embed src="http://player.youku.com/player.php/sid/XMjA3OTIzNzI0/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash"&gt;&lt;/embed&gt; 

&lt;p&gt;&lt;a href="http://dlc2.sdo.com/FTP/cop/20100928/1/rx.mov"&gt;高清格式下载&lt;/a&gt;（mov格式，1280 * 720，265M）&lt;/p&gt;

&lt;h1&gt;大话程序员可用的算法&lt;/h1&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/chengshaofei.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/chengshaofei.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;p&gt;讲师：程劭非，盛大创新院，研究员。网名winter，无忧脚本版主。Web前端技术的积极倡导者。学生时代曾经热衷于参加ACM/ICPC。目前工作在Bambook电子书项目，主要负责文字排版和浏览器引擎WebKit相关。之前曾负责在Windows CE系统上的IE开发。&lt;/p&gt;

&lt;p&gt;简介：俗话说“数据结构+算法=程序”，算法是什么？算法书里满篇是看不懂的形式化推导，网上一些&amp;quot;高人&amp;quot;写的关于算法文章高深莫测，大公司面 试最让人讨厌的就是考算法题，“我做了这么多年，跟本在实际开发中就没用过算法！”，算法真的是距离我们如此遥远的东西吗？且听这回演讲， 算法究竟如何影响我们的开发。&lt;/p&gt;
&lt;embed src="http://player.youku.com/player.php/sid/XMjA3OTI3MjAw/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash"&gt;&lt;/embed&gt; 

&lt;p&gt;&lt;a href="http://dlc2.sdo.com/FTP/cop/20100928/1/algorithms.mov"&gt;高清格式下载&lt;/a&gt;（mov格式，1280 * 720，128M）&lt;/p&gt;

&lt;h1&gt;Windows内核技术介绍&lt;/h1&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/panaimin.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/panaimin.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;p&gt;讲师：潘爱民，盛大创新院专家，微软学者，集团COO专家顾问。长期从事软件和系统技术的研究和开发工作，撰写了大量软件技术文章，并著译了多部经典计算机图书。在MSR/清华等从事多年科研工作，在北大和清华多年执教经验。数学学士学位和计算机科学博士，主要研究领域包括软件设计、信息安全、操作系统和Internet技术。&lt;/p&gt;

&lt;p&gt;简介：Windows操作系统经过二十年的发展，已臻成熟。Microsoft在推动Windows内核方面做了大量工作，譬如于2006年夏季向教育界开放了当时最为先进的内核源代码（Windows Research Kernel）。主讲者在这次讲座中，结合这些可利用的资源，分享对Windows内核研究的体会，尤其将重点讨论Windows中的I/O模型和环境子系统。&lt;/p&gt;
&lt;embed src="http://player.youku.com/player.php/sid/XMjEyOTkyMzgw/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash"&gt;&lt;/embed&gt; 

&lt;p&gt;&lt;a href="http://dlc2.sdo.com/FTP/cop/20100928/1/win-kernal.mov"&gt;高清格式下载&lt;/a&gt;（mov格式，1280 * 720，480M）&lt;/p&gt;

&lt;h1&gt;面向对象与生活&lt;/h1&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/gaoxiang.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/gaoxiang.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;p&gt;讲师：高翔，5173.com&amp;#160; 项目经理。关注前沿技术和技术人员的非技术生活。对面向对象、模式和建模技术有浓厚兴趣，并对游戏设计和图形学方面也比较感兴趣。最近在学习F#，Lua以及关注一些关于职业生涯规划方面的话题。&lt;/p&gt;

&lt;p&gt;简介：面向对象这个话题虽然很热，但与哲学一样，很难给其一个很准确的定义。也因为如此，每个人对它都有自己的理解。本次演讲将从一个实际的例子出发，逐步引入面向对象的三个特征，结合对象的生命周期，以及基于事件的对象扩展方式等方面，探讨其与设计模式，与生活之间的联系。&lt;/p&gt;
&lt;embed src="http://player.youku.com/player.php/sid/XMjA2NjIxMzky/v.swf" quality="high" width="480" height="400" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash"&gt;&lt;/embed&gt; 

&lt;p&gt;&lt;a href="http://dlc2.sdo.com/FTP/cop/20100928/1/ooad.mov"&gt;高清格式下载&lt;/a&gt;（mov格式，1280 * 720，320M）&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/08/2nd-snda-dotnet-conference-sign-up.html"&gt;盛大创新院赞助第二届.NET技术交流会开始报名了！&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-is-coming.html"&gt;盛大创新院赞助第二届.NET技术交流会即将召开&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-all-slides.html"&gt;盛大创新院赞助第二届.NET技术交流会 - 各场演讲幻灯片&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;盛大创新院赞助第二届.NET技术交流会 - 演讲录像及下载 &lt;/li&gt;

  &lt;li&gt;盛大创新院赞助首届.NET技术交流会：&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;报名&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-is-coming.html"&gt;预告&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html"&gt;幻灯片&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html"&gt;演讲录象及下载&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-videos.html#comments</comments>
      <pubDate>Tue, 21 Sep 2010 03:27:11 GMT</pubDate>
      <lastBuildDate>Tue, 21 Sep 2010 03:27:11 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/language/">语言编程</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <title>异步编程与响应式框架</title>
      <link>http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html</link>
      <guid>http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html</guid>
      <description>&lt;p&gt;本文原发表于《程序员》杂志9月刊，现将全文公开至此。前几天的技术交流会上我也讲了这部分内容，配合着看会有更好效果。&lt;/p&gt;

&lt;p&gt;此外我想再多几句。《程序员》杂志在某些方面必须尽快做出调整。就拿我这篇文章来说，代码除了被三栏的版式搞得支离破碎以外，其中必要的空格也莫名奇妙地少了许多。我认为《程序员》如今在内容方面已经有了显著提高，但是在这种“表面”工夫上也得抓紧才好，这也是国内同类杂志中“最专业者”所应有的风范。&lt;/p&gt;

&lt;h1&gt;前言&lt;/h1&gt;

&lt;p&gt;异步操作是强大的，它是许多高伸缩性架构的基石。异步操作在许多情况下是必须的，例如在客户端保持用户界面的响应能力，以及在日益兴起的云计算场景中。但是，异步编程又是十分困难的，它让这让许多程序员敬而远之。因此，越来越多的编程语言都对异步编程提供了相当程度的支持，其中的典型代表便是F#中的异步工作流以及Scala的Actor模型。不过目前的一些主流编程语言，如C#或是JavaScript，它们在设计之时并没有在异步编程上考虑太多，我们便会根据它们的语言特性，提供合适的异步编程模型及其实现。而本文介绍的便是其中一例：响应式编程（Reactive Programming）模型及响应式框架（Reactive Framework，简称Rx）。&lt;/p&gt;

&lt;h1&gt;异步编程的难点&lt;/h1&gt;

&lt;p&gt;异步编程之所以困难，主要有三大难点。&lt;/p&gt;  &lt;p&gt;首先是对于状态的维护。在普通编程中，我们已经习惯了根据各种状态采取不同做法的编程方式。在异步编程中，状态对于操作的影响则往往更为复杂。例如，我们在编写一个鼠标“拖动及绘图”的行为时，一般会采用这样的逻辑：&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;在MouseDown事件中将isDragging标记设为true，表示“拖动开始”，并记录当前鼠标位置prevPos。 &lt;/li&gt;    &lt;li&gt;在MouseUp事件中将isDragging标记设为false，表示“拖动结束”。 &lt;/li&gt;    &lt;li&gt;在MouseMove事件中检查isDragging标记，如果为true，根据鼠标当前位置currPos和之前记录的prevPos进行绘图，并将currPos的值写入prevPos。 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;仅在这样一个最基本的场景中，我们便需要编写三个事件处理器（Event Handler），控制isDragging，prevPos等外部状态，并根据这些状态决定事件触发时的效果。这样的例子数不胜数，尤其是在各式拖放操作中，几乎都会涉及大量状态的控制（例如，判断物体是否进入某个特定区域）。&lt;/p&gt;  &lt;p&gt;异步编程的另一个难点，在于异步操作之间的组合及交互。例如在如上的简单拖放操作中，我们便涉及到了MouseDown，MouseUp及MouseMove三个事件。从某些角度来说，客户端的UI事件还是比较容易处理的，因为它们往往都是在单一线程上依次执行。但是在另外一些场景中，如云计算时，我们往往会同时发起多个异步操作，并根据这些操作的结果进行后续处理，甚至还会有一个额外的超时监控，这样便很有可能会出现并发操作的竞争（Race）情况，这将会成为程序复杂度的灾难。&lt;/p&gt;  &lt;p&gt;此外，异步操作还会破坏“代码局部性（Code Locality）”，这可能也是异步操作中最为常见的阻碍。程序员早已习惯了“线性”地表达逻辑，但即便是多个顺序执行的异步操作，也会因为大量的回调函数而将算法拆得支离破碎，更何况还会出现各种循环及条件判断。同时，在线性的代码中，我们可以使用“局部变量”保存状态，而在编写异步代码时则需要手动地在多个函数中传递状态。此外，由于逻辑被拆分至多个方法，因此我们也无法使用传统的try/catch进行统一异常处理。&lt;/p&gt;

&lt;h1&gt;推模型与拉模型&lt;/h1&gt;

&lt;p&gt;平时我们使用最多的便是“交互式（Interactive）”的编程方式，采用的是组件之间的相互调用来表现逻辑。例如，对象A向对象B请求数据并等待返回，待对象B完成并返还数据之后A才继续进行后面的操作。交互式编程的一个典型应用便是GoF23中的迭代器（Iterator）模式，它在.NET中的实现为IEnumerable及IEnumerator接口，例如：&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;void &lt;/span&gt;Traverse(&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; source)
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;etor = source.GetEnumerator();
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(etor.MoveNext())
    {
        &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(etor.Current);
    }
}&lt;/pre&gt;

&lt;p&gt;为了更好地说明问题，这里我们将标准的foreach操作展开为传统的迭代器使用形式，并省略了using语句。在使用时，我们先调用一个IEnumerable对象的GetEnumerator方法，获得一个迭代器，再根据MoveNext及Current进行遍历。在调用MoveNext时，迭代器会去“准备”下一个元素，并根据存在与否返回true或者false。试想，如果其中某个MoveNext的“准备”工作涉及到一个耗时较长的操作，则迭代器的使用者也必须眼巴巴地等待其返回。&lt;/p&gt;

&lt;p&gt;这是一种“拉（Pull）”模型，数据由消费者（Consumer）从生产者（Producer）那里主动“拉”来。这是一种同步的交互方式，数据消费者会依赖于数据生产者的表现。这就好比我们去食堂吃饭时必须主动去取餐，此时则必须从队伍的最后排起，我们什么时候能结束等待并进行下一步操作（即“吃饭”），则要看食堂的生产速度如何。很显然，有些时候这种交互方式是不可接受的，例如我们在实现一个搜索引擎的“关键字提示”功能时，不可能让用户在输入一个字符后，必须等待远程的提示请求返回才能继续输入下一个字符。&lt;/p&gt;

&lt;p&gt;而与交互式编程对应的便是“响应式（Reactive）”编程。响应式编程是一种基于“改变”的编程方式。例如在交互式编程中，A = B + C这样的表达式意味着将B与C之和赋给A，而此后B与C的改变都与A无关。而在响应式编程中，A会去“响应”B或C的变化，即一旦B或C改变之后，A的值也会随之变化。响应式编程的一个典型应用便是GoF23中的观察者（Observer）模式。与迭代器的IEnumerable/IEnumerator不同，在之前的.NET框架中并没有对这样一种编程模型指定“标准化（Formallized）”接口，不过在.NET 4.0的基础类库中增加了IObservable及IObserver接口，签名如下：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IObservable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;out &lt;/span&gt;T&amp;gt;
{
    &lt;span style="color: #2b91af"&gt;IDisposable &lt;/span&gt;Subscribe(&lt;span style="color: #2b91af"&gt;IObserver&lt;/span&gt;&amp;lt;T&amp;gt; observer);
}

&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IObserver&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;in &lt;/span&gt;T&amp;gt;
{
    &lt;span style="color: blue"&gt;void &lt;/span&gt;OnCompleted();
    &lt;span style="color: blue"&gt;void &lt;/span&gt;OnError(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;error);
    &lt;span style="color: blue"&gt;void &lt;/span&gt;OnNext(T value);
}&lt;/pre&gt;

&lt;p&gt;如果我们仔细比较“迭代器”与“观察者”的标准化接口，则会发现它们是完全“对偶（dual）”的：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;IEnumerable.GetEnumerator方法“输出”一个IEnumerater对象；IObservable.Subscribe方法“输入”一个IObserver对象。 &lt;/li&gt;

  &lt;li&gt;在遍历元素用尽时，IEnumerator.MoveNext方法返回false；在响应内容用尽时，IObserver.OnCompleted方法被调用。 &lt;/li&gt;

  &lt;li&gt;在有新元素需要遍历时，IEnumerator.MoveNext方法返回true，并通过Current属性“输出”；在有新元素需要响应时，IObserver.OnNext方法被调用，并通过参数“输入”。 &lt;/li&gt;

  &lt;li&gt;在出现错误时，IEnumerator.MoveNext方法会“抛出”一个异常；在出现错误时，IObserver.OnError方法会被调用，并通过参数“接受”异常信息。 &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;至于IObservable.Subscribe方法返回的IDisposable对象，则用于“退定”操作，即让输入的IObserver对象再也不需要继续响应IObservable对象的新元素了。&lt;/p&gt;

&lt;p&gt;从比较中可以看出，如果说IEnumerator对象是由数据消费者使用的话，那么IObserver对象则是由数据的生产者，即IObservable对象使用的。换句话说，数据是由数据的生产者“推”给数据消费者的，是一种“推（Push）”模型。在这种异步的交互方式中，数据消费者不必依赖于数据生产者的表现。这就好比我们去饭店吃饭，点菜后便可坐下和同伴聊聊天或是用手机上上网，而作为菜品的生产者，饭店，则会在产出之后主动端上桌来。这么做无疑解放了数据的消费者，例如用户可以在文本框里不断地输入字符，而只需等远程服务器将提示结果“推”给客户端后再显示即可。&lt;/p&gt;

&lt;p&gt;许多模型都可以统一至标准的生产者接口IObservable上，如MouseMove事件便可以认做是“永不停止的MouseEventArgs对象的生产者”。而另一方面，单个异步操作则可以被视为“只产出单个数据便结束的生产者”。&lt;/p&gt;

&lt;h1&gt;LINQ to Observable&lt;/h1&gt;

&lt;p&gt;在许多人眼中，C# 3.0中新增的LINQ特性只是一种用于操作数据的DSL，它的主要作用也仅仅是针对IEnumerable或是IQueryable的数据操作。事实上，LINQ本身的能力远不止此。LINQ是一种非常简单的语言特性，编译器只是将LINQ查询语句转化为“字面等价”的“LINQ标准方法”调用（如Where，Select等等）和“和Lambda表达式”参数（如x =&amp;gt; x &amp;gt; 0）而已。但是这里的关键便是“字面等价”四个字，LINQ本身并不规定“LINQ标准方法”是对象的实例方法还是扩展方法，“Lambda表达式”是构造出一个匿名函数还是表达式树，这一切都是由利用LINQ的类库来决定的。因此，微软能够基于LINQ实现了PLINQ这样的并行类库。直到最近的访谈中，LINQ的设计者Erik Meijer依旧认为LINQ还是被低估了，他们自己也还在继续挖掘LINQ的更多能力。&lt;/p&gt;

&lt;p&gt;虽然LINQ本身在语法上只是一些方法调用，但是它在语义上是针对数据流的一系列操作，因此LINQ to Object，LINQ to SQL以及PLINQ可以认为是最自然，最符合LINQ语义的应用。如今，微软的“云编程能力团队（Cloud Programmability Team）”在其“响应式框架（Reactive Framework，简称Rx）”提供了LINQ to Observable，这又是另一个LINQ的经典使用案例。IObservable本身可以被认为是一股“推送数据”的数据流，因此也可以对其进行“过滤”或是“投影”等操作，这便是LINQ的应用场景，LINQ to Observable是对IObservable接口实现的一系列LINQ标准方法。因此，我们可以认为，LINQ to Observable是一套与LINQ to Object对偶的类库，事实上在响应式框架中，还有一套与LINQ to Queryable对偶的LINQ to Qbservable，请注意第一个字母是Q，我们可以把它看作是LINQ to Observable的“远程查询”版本。&lt;/p&gt;

&lt;p&gt;利用LINQ to Object可以编写出声明式（表示“做什么”）的代码，其可读性往往远高于等价的命令式（表示“怎么做”）代码。LINQ to Observable也有类似的效果。假设现在有一个需求：利用ADWS键控制小球的位置。传统的写法可能是这样的：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;void &lt;/span&gt;OnKeyPress(&lt;span style="color: blue"&gt;object &lt;/span&gt;sender, &lt;span style="color: #2b91af"&gt;KeyPressEventArgs &lt;/span&gt;e)
{
    // 如果游戏已经开始
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(isPlaying) &lt;span style="color: green"&gt;&lt;/span&gt;
    {
        &lt;span style="color: green"&gt;// 向左且小球没有超出边界&lt;/span&gt;
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(e.KeyChar == &lt;span style="color: #a31515"&gt;'a'&lt;/span&gt; &amp;amp;&amp;amp; ball.Left &amp;gt; 0)
        {
            ball.Left -= 5;
        }
        &lt;span style="color: green"&gt;// 向上且小球没有超出边界&lt;/span&gt;
        &lt;span style="color: blue"&gt;else if &lt;/span&gt;(e.KeyChar == &lt;span style="color: #a31515"&gt;'w'&lt;/span&gt; &amp;amp;&amp;amp; ball.Top &amp;gt; 0)
        {
            ball.Top -= 5;
        }
        &lt;span style="color: blue"&gt;else &lt;/span&gt;... &lt;span style="color: green"&gt;&lt;/span&gt;
    }
    &lt;span style="color: blue"&gt;else &lt;/span&gt;...
}&lt;/pre&gt;

&lt;p&gt;由于KeyPress事件总是不断触发，因此我们只能它的事件处理器中进行判断各种状态，采取不同措施。而如果我们利用LINQ to Observable，则几乎是另外一种思维方式：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: green"&gt;// 过滤出isPlaying时的keyPress事件
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;keyPress = GetKeyPress().Where(_ =&amp;gt; isPlaying);

&lt;span style="color: green"&gt;// 过滤出向左移动的事件
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveLeft = &lt;span style="color: blue"&gt;from &lt;/span&gt;ev &lt;span style="color: blue"&gt;in &lt;/span&gt;keyPress
               &lt;span style="color: blue"&gt;where &lt;/span&gt;ev.EventArgs.KeyChar == &lt;span style="color: #a31515"&gt;'a'
               &lt;/span&gt;&lt;span style="color: blue"&gt;where &lt;/span&gt;ball.Left &amp;gt; 0
               &lt;span style="color: blue"&gt;select &lt;/span&gt;ev;
moveLeft.Subscribe(_ =&amp;gt; ball.Left -= 5);

&lt;span style="color: green"&gt;// 过滤出向上移动的事件
&lt;/span&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;moveTop = &lt;span style="color: blue"&gt;from &lt;/span&gt;ev &lt;span style="color: blue"&gt;in &lt;/span&gt;keyPress
              &lt;span style="color: blue"&gt;where &lt;/span&gt;ev.EventArgs.KeyChar == &lt;span style="color: #a31515"&gt;'w'
              &lt;/span&gt;&lt;span style="color: blue"&gt;where &lt;/span&gt;ball.Top &amp;gt; 0
              &lt;span style="color: blue"&gt;select &lt;/span&gt;ev;
moveTop.Subscribe(_ =&amp;gt; ball.Top -= 5);&lt;/pre&gt;

&lt;p&gt;我们可以将“KeyPress事件”视为“推送KeyPressEventArgs对象”这一数据流的数据源（由GetKeyPress方法返回），那么如今的代码便是使用LINQ过滤出“需要”的数据，并针对真正需要的那部分进行响应。这么做，便将“条件”与“操作”解耦，显著增强了代码的语义表达能力。事实上，只要补充一些辅助方法，可以利用LINQ表示更为完整复杂的逻辑。例如，微软咨询师Matthew Podwysocki便&lt;a href="http://codebetter.com/blogs/matthew.podwysocki/archive/2009/11/18/introduction-to-the-reactive-framework-part-v.aspx"&gt;在博客中展示过一段代码&lt;/a&gt;，基于LINQ to Observable实现了创建一个WebRequest对象，设置属性，异步发送及下载数据的一系列操作。&lt;/p&gt;

&lt;h1&gt;更多扩展&lt;/h1&gt;

&lt;p&gt;.NET基础类库针对IEnumerable定义了大量的函数式的辅助方法，开发人员可以直接将它们组合运用在项目中。除了标准的LINQ操作方法之外，响应式框架中同样定义了大量辅助方法，可以配合LINQ to Observable组合使用。例如本文开头所设想的鼠标“拖动及绘图”功能，便可以使用如下代码完成：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseMove = GetMouseMove();
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDiff = mouseMove.Zip(mouseMove.Skip(1), (prev, curr) =&amp;gt;
    &lt;span style="color: blue"&gt;new
    &lt;/span&gt;{
        PrevPos = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Point&lt;/span&gt;(prev.EventArgs.X, prev.EventArgs.Y),
        CurrPos = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Point&lt;/span&gt;(curr.EventArgs.X, curr.EventArgs.Y)
    });

&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDrag = &lt;span style="color: blue"&gt;from &lt;/span&gt;_ &lt;span style="color: blue"&gt;in &lt;/span&gt;GetMouseDown()
                &lt;span style="color: blue"&gt;from &lt;/span&gt;diff &lt;span style="color: blue"&gt;in &lt;/span&gt;mouseDiff.TakeUntil(GetMouseUp())
                &lt;span style="color: blue"&gt;select &lt;/span&gt;diff;
mouseDrag.Subscribe(diff =&amp;gt; DrawLine(diff.PrevPos, diff.CurrPos));&lt;/pre&gt;

&lt;p&gt;在这段代码中，我们首先将mouseMove事件使用Skip跳开一项，再与自身通过Zip方法组合成mouseDiff，这是一个输出相邻两次MouseMove事件坐标的数据源；接着，我们利用LINQ从触发MouseDown事件开始，向mouseDiff数据源获取每一项diff，直至（TakeUntil）触发MouseUp事件，以此生成最终的mouseDrag；最后再将绘图功能订阅至这个数据源上。您会发现此时我们已经无须手动维护操作过程中的各种状态了，从事件的“开始”到“结束”均使用响应式框架的辅助方法“声明”而来。&lt;/p&gt;

&lt;p&gt;以上便是一个利用了Skip，Zip，TakeUntil等辅助方法的例子。当然，这些辅助方法在IEnumerable上都有语义相同的对应操作，而在响应式框架中还有更多辅助方法是针对特性异步场景的。假设我们现在要编写一个即时翻译功能，同时发起三个请求，将中文分别翻译至英语、法语及西班牙语，并显示最先返回的两个结果（真是个奇怪的需求）。此外，我们不会在用户输入每个字符的时候便发起一个远程请求，而是在用户停止输入0.5秒之后才根据当前的输入框中的文字进行提示。于是我们可以编写这样的代码：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;limit = &lt;span style="color: #2b91af"&gt;TimeSpan&lt;/span&gt;.FromSeconds(0.5);
&lt;span style="color: blue"&gt;var &lt;/span&gt;translate =
    &lt;span style="color: blue"&gt;from &lt;/span&gt;_ &lt;span style="color: blue"&gt;in &lt;/span&gt;GetKeyPress().Throttle(limit)
    &lt;span style="color: blue"&gt;let &lt;/span&gt;text = &lt;span style="color: blue"&gt;this&lt;/span&gt;.txtInput.Text
    &lt;span style="color: blue"&gt;where &lt;/span&gt;text.Length &amp;gt; 0
    &lt;span style="color: blue"&gt;let &lt;/span&gt;english = &lt;span style="color: #2b91af"&gt;Bing&lt;/span&gt;.Translate(text, &lt;span style="color: #a31515"&gt;&amp;quot;en&amp;quot;&lt;/span&gt;)
    &lt;span style="color: blue"&gt;let &lt;/span&gt;french = &lt;span style="color: #2b91af"&gt;Bing&lt;/span&gt;.Translate(text, &lt;span style="color: #a31515"&gt;&amp;quot;fr&amp;quot;&lt;/span&gt;)
    &lt;span style="color: blue"&gt;let &lt;/span&gt;spanish = &lt;span style="color: #2b91af"&gt;Bing&lt;/span&gt;.Translate(text, &lt;span style="color: #a31515"&gt;&amp;quot;es&amp;quot;&lt;/span&gt;)
    &lt;span style="color: blue"&gt;from &lt;/span&gt;result &lt;span style="color: blue"&gt;in &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Observable&lt;/span&gt;.Join(
       english.And(french).Then((en, fr) =&amp;gt;
           &lt;span style="color: blue"&gt;new &lt;/span&gt;{ English = en, French = fr, Spanish = &lt;span style="color: #a31515"&gt;&amp;quot;&amp;quot;&lt;/span&gt; }),
       english.And(spanish).Then((en, es) =&amp;gt;
           &lt;span style="color: blue"&gt;new &lt;/span&gt;{ English = en, French = &lt;span style="color: #a31515"&gt;&amp;quot;&amp;quot;&lt;/span&gt;, Spanish = es }),
       french.And(spanish).Then((fr, es) =&amp;gt;
           &lt;span style="color: blue"&gt;new &lt;/span&gt;{ English = &lt;span style="color: #a31515"&gt;&amp;quot;&amp;quot;&lt;/span&gt;, French = fr, Spanish = es }))
    &lt;span style="color: blue"&gt;select &lt;/span&gt;result;

translate.Subscribe(...);&lt;/pre&gt;

&lt;p&gt;这里用到了Throottle方法，它会过滤某个数据源的输出，确保在该数据源“静默”特定时间之后，才将最近的一条数据推送至外部。此外，这里还使用了Observable.Join方法控制多个数据源，根据返回结果的先后获得合适的结果。响应式框架提供了大量针对某种异步场景的辅助方法，例如用于定期推送数据的Interval方法，从一个数据源根据特定条件进行采样的Sample方法，合并多个数据源的ForkJoin方法，以及表示流程控制的For，While，If等等。这些方法内部会维护各种所需要的状态，为我们打理各种复杂的竞争情况，以此节省了开发人员的精力。&lt;/p&gt;

&lt;p&gt;如果这些还不能满足我们的要求，我们也可以根据自己的需要开发特定的辅助方法，就像我们在使用LINQ to Object时为IEnumerable所作的各种扩展那样。响应式框架也提供了一系列Subject类型，简化了IObservable自定义扩展的开发过程。由于响应式框架尚未正式发布，微软目前建立了&lt;a href="http://rxwiki.wikidot.com/"&gt;一个Wiki&lt;/a&gt;，用于展示关于各辅助方法及Subject类的使用示例及其他相关信息。&lt;/p&gt;

&lt;h1&gt;响应式框架的JavaScript版本&lt;/h1&gt;

&lt;p&gt;响应式编程的重要使用场景之一便是与用户交互的GUI界面。例如，Silverlight禁止任何阻塞的IO操作，换言之Silverlight中的所有网络操作都是异步的，微软也正是出于简化异步开发的目的才设计了响应式框架（事实上响应式框架已经集成到Silverlight Toolkit中）。不过与Silverlight相比，基于浏览器的原生JavaScript应用程序无疑使用地更为广泛。对于这样的应用程序来说，动画是异步的，AJAX请求也是异步的，我们几乎可以断言，如果有一套面向JavaScirpt应用程序的响应式框架，一定会比面向Silverlight的框架更有意义得多。&lt;/p&gt;

&lt;p&gt;微软也想到了这一点。之前我们讨论的“响应式框架”，其实只是响应式编程模型的一种实现。更确切地说，我们只是讨论了这套框架的.NET版本，微软还提供了JavaScript版本的响应式框架。JavaScript版本的API与.NET版本几乎完全一致，例如我们之前讨论的拖放操作，使用JavaScript即可写作：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;var &lt;/span&gt;target = $(&lt;span style="color: maroon"&gt;&amp;quot;#dragTarget&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseMove = target.toObservable(&lt;span style="color: maroon"&gt;&amp;quot;mousemove&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDiff = mouseMove.Zip(mouseMove.Skip(1), 
    &lt;span style="color: blue"&gt;function&lt;/span&gt;(prev, curr) {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;{
            PrevPos: { x: prev.clientX, y: prev.clientY },
            CurrPos: { x: curr.clientX, y: curr.clientY }
        };
    });

&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDown = target.toObservable(&lt;span style="color: maroon"&gt;&amp;quot;mousedown&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseUp = target.toObservable(&lt;span style="color: maroon"&gt;&amp;quot;mouseup&amp;quot;&lt;/span&gt;);
&lt;span style="color: blue"&gt;var &lt;/span&gt;mouseDrag = mouseDown.SelectMany(&lt;span style="color: blue"&gt;function&lt;/span&gt;() {
    mouseDiff.TakeUntil(mouseUp);
});

mouseDrag.Subscribe(...);&lt;/pre&gt;

&lt;p&gt;由于没有C#中的LINQ查询语言，我们只能直接使用展开后的方法，如SelectMany来编写逻辑。JavaScript版本的响应式框架还提供了一系列的“胶合”层，能够与jQuery，Dojo，MooTools，Prototype等流行框架同时使用。例如，上一段代码中的toObservable便是在jQuery根对象上扩展的方法。&lt;/p&gt;

&lt;h1&gt;总结&lt;/h1&gt;

&lt;p&gt;异步编程在用户交互式界面及一些云计算场景中尤其重要。微软的云编程能力团队针对.NET平台和JavaScirpt分别提供了一套响应式框架，希望以此简化异步程序的开发。不过，这套响应式框架所表现出的理念是通用的。而且，事实上只要是拥有匿名函数及闭包的语言，例如Scala，Python，Ruby等等，实现这样一套框架其实都不是十分困难的事情。&lt;/p&gt;
</description>
      <comments>http://blog.zhaojie.me/2010/09/async-programming-and-reactive-framework.html#comments</comments>
      <pubDate>Tue, 14 Sep 2010 12:14:28 GMT</pubDate>
      <lastBuildDate>Tue, 14 Sep 2010 12:14:28 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>盛大创新院赞助第二届.NET技术交流会 - 各场演讲幻灯片</title>
      <link>http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-all-slides.html</link>
      <guid>http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-all-slides.html</guid>
      <description>&lt;p&gt;昨天有160多位朋友参加了&lt;a href="http://blog.zhaojie.me/2010/01/1651772.html"&gt;盛大创新院&lt;/a&gt;赞助的&lt;a href="http://blog.zhaojie.me/2010/08/2nd-snda-dotnet-conference-sign-up.html"&gt;第二届.NET技术交流会&lt;/a&gt;，再次感谢各位对我们的支持。比较遗憾的是，这次的讲师录像方面有着很大问题，我们正在想办法进行修补，希望可以有“差强人意”的结果。现在，大家请在第一时间浏览本次活动新鲜出炉的幻灯片。&lt;/p&gt;

&lt;h1&gt;响应式编程与响应式框架&lt;/h1&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/zhaojie.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/zhaojie.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;p&gt;讲师：赵劼，盛大创新院，研究员。关注前沿技术，并致力于开源社区与微软平台的组合优化。对函数式编程，并行程序开发，代码之美以及程序员能力与修养等相关问题也有着浓厚的兴趣，同时非常希望能够写程序到60岁。最近致力于F#，Scala语言及mono平台在社区中的推广。&lt;/p&gt;

&lt;p&gt;简介：异步编程改变了我们的编程方式，也为我们带来的许多挑战，同时让一些编程模型重新焕发了生机。与传统的“拉”模型不同，响应式编程将异步事件流视为可观察的集合，这是一种“推”模型。微软为了提高云时代的编程体验而设计了响应式框架，其目的是为了简化复杂事件处理之间混合操作。从中我们了解到一些异步编程的模式与LINQ使用技巧，并可以将这种编程模型普及到JavaScript等其他平台上去。&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_5183111"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="响应式编程及框架" href="http://www.slideshare.net/jeffz/reactive-programming-and-framework"&gt;响应式编程及框架&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5183111" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=rx-100912025913-phpapp01&amp;stripped_title=reactive-programming-and-framework" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5183111" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=rx-100912025913-phpapp01&amp;stripped_title=reactive-programming-and-framework" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;相关下载：&lt;a href="http://files.zhaojie.me/slides/snda-dotnet-conf/20100911/rx-zhaojie.pdf"&gt;幻灯片&lt;/a&gt;、&lt;a href="http://files.zhaojie.me/slides/snda-dotnet-conf/20100911/rx-demo-zhaojie.zip"&gt;示例程序&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;大话程序员可用的算法&lt;/h1&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/chengshaofei.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/chengshaofei.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;p&gt;讲师：程劭非，盛大创新院，研究员。网名winter，无忧脚本版主。Web前端技术的积极倡导者。学生时代曾经热衷于参加ACM/ICPC。目前工作在Bambook电子书项目，主要负责文字排版和浏览器引擎WebKit相关。之前曾负责在Windows CE系统上的IE开发。&lt;/p&gt;

&lt;p&gt;简介：俗话说“数据结构+算法=程序”，算法是什么？算法书里满篇是看不懂的形式化推导，网上一些&amp;quot;高人&amp;quot;写的关于算法文章高深莫测，大公司面 试最让人讨厌的就是考算法题，“我做了这么多年，跟本在实际开发中就没用过算法！”，算法真的是距离我们如此遥远的东西吗？且听这回演讲， 算法究竟如何影响我们的开发。&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_5183325"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="大话程序员可用的算法" href="http://www.slideshare.net/jeffz/programmers-and-algorithms"&gt;大话程序员可用的算法&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5183325" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=random-100912045100-phpapp01&amp;stripped_title=programmers-and-algorithms" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5183325" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=random-100912045100-phpapp01&amp;stripped_title=programmers-and-algorithms" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;相关下载：&lt;a href="http://files.zhaojie.me/slides/snda-dotnet-conf/20100911/algorithms-chengshaofei.pdf"&gt;幻灯片&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;Windows内核技术介绍&lt;/h1&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/panaimin.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/panaimin.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;p&gt;讲师：潘爱民，盛大创新院专家，微软学者，集团COO专家顾问。长期从事软件和系统技术的研究和开发工作，撰写了大量软件技术文章，并著译了多部经典计算机图书。在MSR/清华等从事多年科研工作，在北大和清华多年执教经验。数学学士学位和计算机科学博士，主要研究领域包括软件设计、信息安全、操作系统和Internet技术。&lt;/p&gt;

&lt;p&gt;简介：Windows操作系统经过二十年的发展，已臻成熟。Microsoft在推动Windows内核方面做了大量工作，譬如于2006年夏季向教育界开放了当时最为先进的内核源代码（Windows Research Kernel）。主讲者在这次讲座中，结合这些可利用的资源，分享对Windows内核研究的体会，尤其将重点讨论Windows中的I/O模型和环境子系统。&lt;/p&gt;

&lt;div style="width:425px" id="__ss_5183269"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/jeffz/win-os-kernel-tech" title="Windows内核技术介绍"&gt;Windows内核技术介绍&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5183269" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=winos-kernel-techforsnda2010-9-100912042602-phpapp01&amp;stripped_title=win-os-kernel-tech&amp;userName=jeffz" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5183269" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=winos-kernel-techforsnda2010-9-100912042602-phpapp01&amp;stripped_title=win-os-kernel-tech&amp;userName=jeffz" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;相关下载：&lt;a href="http://files.zhaojie.me/slides/snda-dotnet-conf/20100911/win-kernel-panaimin.pdf"&gt;幻灯片&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;面向对象与生活&lt;/h1&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/gaoxiang.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/gaoxiang.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;p&gt;讲师：高翔，5173.com&amp;#160; 项目经理。关注前沿技术和技术人员的非技术生活。对面向对象、模式和建模技术有浓厚兴趣，并对游戏设计和图形学方面也比较感兴趣。最近在学习F#，Lua以及关注一些关于职业生涯规划方面的话题。&lt;/p&gt;

&lt;p&gt;简介：面向对象这个话题虽然很热，但与哲学一样，很难给其一个很准确的定义。也因为如此，每个人对它都有自己的理解。本次演讲将从一个实际的例子出发，逐步引入面向对象的三个特征，结合对象的生命周期，以及基于事件的对象扩展方式等方面，探讨其与设计模式，与生活之间的联系。&lt;/p&gt;

&lt;div style="width: 425px" id="__ss_5183310"&gt;&lt;strong style="margin: 12px 0px 4px; display: block"&gt;&lt;a title="面向对象与生活" href="http://www.slideshare.net/jeffz/object-orientation-and-life"&gt;面向对象与生活&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5183310" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=random-100912044502-phpapp01&amp;stripped_title=object-orientation-and-life" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5183310" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=random-100912044502-phpapp01&amp;stripped_title=object-orientation-and-life" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="padding-bottom: 12px; padding-left: 0px; padding-right: 0px; padding-top: 5px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/jeffz"&gt;jeffz&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;相关下载：&lt;a href="http://files.zhaojie.me/slides/snda-dotnet-conf/20100911/ooad-gaoxiang.pdf"&gt;幻灯片&lt;/a&gt;、附加资料“&lt;a href="http://files.zhaojie.me/slides/snda-dotnet-conf/20100911/%e4%b8%96%e7%95%8c%e7%9a%84%e5%ae%8f%e8%a7%82%e5%92%8c%e5%be%ae%e8%a7%82.pptx"&gt;世界的宏观和微观&lt;/a&gt;”&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/08/2nd-snda-dotnet-conference-sign-up.html"&gt;盛大创新院赞助第二届.NET技术交流会开始报名了！&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-is-coming.html"&gt;盛大创新院赞助第二届.NET技术交流会即将召开&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;盛大创新院赞助第二届.NET技术交流会 - 各场演讲幻灯片 &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-videos.html"&gt;盛大创新院赞助第二届.NET技术交流会 - 演讲录像及下载&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;盛大创新院赞助首届.NET技术交流会：&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;报名&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-is-coming.html"&gt;预告&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html"&gt;幻灯片&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html"&gt;演讲录象及下载&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-all-slides.html#comments</comments>
      <pubDate>Sun, 12 Sep 2010 14:32:31 GMT</pubDate>
      <lastBuildDate>Sun, 19 Dec 2010 17:32:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>盛大创新院赞助第二届.NET技术交流会即将召开</title>
      <link>http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-is-coming.html</link>
      <guid>http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-is-coming.html</guid>
      <description>&lt;p&gt;由&lt;a href="http://blog.zhaojie.me/2010/01/1651772.html"&gt;盛大创新院&lt;/a&gt;赞助的第二届.NET技术大会将于9月11号下午1点召开，本次交流会请到了四位讲师，议题覆盖了响应式编程、算法、面向对象设计及Windows内核等多个方面，其中最为突出的莫过于由潘爱民老师为大家带来的Windows内核方面的话题。我已经看过了各场演讲的幻灯片终稿，也很期待各位讲师在正式演讲中的表现。&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/conf-2-600x850.jpg" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/conf-2-600x850.jpg" width="300" /&gt;&lt;/a&gt; 

&lt;p&gt;本次大会中，我们还获得了人民邮电出版社&lt;a href="http://www.turingbook.com/Homepage/Default.aspx"&gt;图灵教育&lt;/a&gt;赠送的15册图书、数枚书签、几件T恤，再加上由会务经费购买的两本潘老师的著作，将会作为各场次的奖品，赠送给在交流会中表现积极的听众。&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/turingbook.jpg" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/turingbook.jpg" width="300" /&gt;&lt;/a&gt; 

&lt;p&gt;同样，我们还请到了&lt;a href="http://www.ku6.com/"&gt;酷六网&lt;/a&gt;的专业摄影师对演讲过程进行全程拍摄，并配合各位讲师自身的屏幕录像，将在后期合成为适合独立观看的演讲视频（&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html"&gt;效果&lt;/a&gt;），让不能到场的朋友在线或是下载后观看。&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/ku6-logo.jpg" /&gt; 

&lt;p&gt;最后，本次活动中我们还邀请了&lt;a href="http://renjian.com/"&gt;人间网&lt;/a&gt;为我们进行线上直播，我们会有专人负责直播信息的管理和发布。您也可以在活动现场，甚至在场外通过会场的无线网络或者短信发布消息。这些信息都会显示在会场的大屏幕上。&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/renjian-logo.jpg" /&gt; 

&lt;p&gt;以下是直播效果：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/renjian-live-demo.jpg" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/renjian-live-demo.jpg" width="300" /&gt;&lt;/a&gt; 

&lt;p&gt;本次会议的邀请函已经发给各位报名者，请携带邀请函至会议现场签到，没有报名的朋友可以在现场直接报名。&lt;a href="http://blog.zhaojie.me/2010/08/2nd-snda-dotnet-conference-sign-up.html"&gt;关于会议的时间、地点、交通、议程等更多信息，请关注会议的报名信息&lt;/a&gt;。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/08/2nd-snda-dotnet-conference-sign-up.html"&gt;盛大创新院赞助第二届.NET技术交流会开始报名了！&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;盛大创新院赞助第二届.NET技术交流会即将召开&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-all-slides.html"&gt;盛大创新院赞助第二届.NET技术交流会 - 各场演讲幻灯片&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-videos.html"&gt;盛大创新院赞助第二届.NET技术交流会 - 演讲录像及下载&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;盛大创新院赞助首届.NET技术交流会：&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;报名&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-is-coming.html"&gt;预告&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html"&gt;幻灯片&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html"&gt;演讲录象及下载&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-is-coming.html#comments</comments>
      <pubDate>Thu, 09 Sep 2010 03:14:43 GMT</pubDate>
      <lastBuildDate>Thu, 09 Sep 2010 03:14:43 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>关于静态资源打包后的相对路径问题</title>
      <link>http://blog.zhaojie.me/2010/09/relative-path-in-packed-static-resources.html</link>
      <guid>http://blog.zhaojie.me/2010/09/relative-path-in-packed-static-resources.html</guid>
      <description>&lt;p&gt;将多个静态资源打包为单个资源以减少请求数目，是提高页面加载速度的常用手段。于是上个星期，我就在实现网站静态资源的自动打包功能，原以为是个比较简单的问题，实现起来也没有遇到什么障碍，不过在开发完毕投入使用的时候却让我跌了下眼镜。由于静态资源在打包以后，它们的访问路径势必会改变，这样其他一些依赖于原有路径的资源就访问不到了。这方面最常见的例子，便是CSS样式表中引用的图片路径是相对于CSS文件路径的。当意识到这个问题以后，还真是让人手忙脚乱了一把。&lt;/p&gt;

&lt;p&gt;例如，有一个描述对话框组件的CSS文件，它的原有访问路径为/styles/dialog/core.css，其中有行样式表为url(header.png)，这意味这幅图片的访问路径为/styles/dialog/header.png。同样，在另一个文件/styles/menu/core.css中也有url(bg.png)这样的代码。那么好，如果我们将这两个文件打包在一起，叫做/packed/styles/dialog-menu.css，那么浏览器就会去加载/packed/styles下的header.png和bg.png，为什么？因为url(…)这段代码，在打包后出现的位置就不同了。&lt;/p&gt;

&lt;p&gt;解决这个问题似乎有多种办法，例如将资源文件复制到/packed/styles下面，但这就要处理文件重名的问题。如果使用组件化开发的方式，两个组件之间应该不去考虑资源的重名，否则就产生了依赖。另一种方式，是在CSS内部使用绝对路径，例如在/styles/menu/core.css中使用url(/styles/menu/bg.png)而不是url(bg.png)。这么做可以解决打包上的问题，但是却破坏了组件化的开发方式。例如此时dialog组件就不是独立的了，它的图片路径是死的。而在理想情况下，一个组件的CSS文件和图片等资源应该是统一的，并独立与其他条件，这也是CSS中url(…)含义的设计目的。&lt;/p&gt;

&lt;p&gt;当然，我并不是说这种“组件化”的实践是必须遵守的，如果只是在开发一个独立的项目，便可以将“绝对路径”作为项目中的约定。但是在实际情况下，这种组件化及相对路径的使用是客观存在的。例如jQuery成百上千的插件，它们的CSS样式中一定不会使用绝对路径。那么我们又该如何对它进行打包呢？&lt;/p&gt;

&lt;p&gt;其实路径问题也很容易解决，只要在“打包”的时候修改掉CSS文件内容里的路径即可。例如我们可以知道，如果浏览器是在/packed/styles/dialog-menu.css文件中，访问到原本在/styles/dialog/core.css里的内容，那么原本的url(bg.png)就必须改写成url(/styles/dialog/bg.png)这样的绝对路径，或者是url(../…/styles/dialog/bg.png)这样相对于新地址的“相对路径”。&lt;/p&gt;

&lt;p&gt;这样的修改其实并不复杂，例如在CSS中，似乎需要替换的也只有url(…)这样的地址了，一个普通的正则表达式便可以解决，而比较麻烦的可能是JavaScript文件了。在某些组件化的JavaScript中，它需要的一些资源也是根据脚本文件的地址相对计算出来的。此时，其中的路径可能只是一些普通的字符串而已，没有规律保险的替换规则。如果要解决这个问题，我们可以在项目中形成某种约定，例如，计算相对路径时都不是简单的字符串拼接，而是使用一个函数调用。而这个函数调用，在进行“打包”时便可以看作是一个待替换的标志。&lt;/p&gt;

&lt;p&gt;相对路径的问题似乎就这么解决了，我想前面这段描述也已经足够清楚，比写代码还要清楚，所以暂时就先不提供我在项目里使用的动态打包机制了。不过这里还有个小小的花絮可以一谈。&lt;/p&gt;

&lt;p&gt;负责前端开发的同事很重视静态资源的打包问题，因此也就一直催促我实现这方面的功能。之前他说，他需要一个工具，输入一个配置文件，便可以将指定文件打包，然后在正式站点发布时修改页面上引用的地址就行了。我说这样不行，会死人的。例如，一个组件会在多个页面上使用，那么它的更新会导致多次打包，还要修改多个页面文件，这对于站点的快速升级更新也是一个灾难。此外，在页面上所使用的静态资源信息，还需要在打包工具的配置文件中重复，&lt;a href="http://blog.zhaojie.me/2010/07/dry-principle-everywhere.html"&gt;这也是一种违反DRY的情况&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;我理想中的打包机制要有以下几个条件：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;遵守DRY原则，例如，一张页面需要加载哪些脚本，此类信息只会出现在对应页面中。&lt;/li&gt;

  &lt;li&gt;在开发系统和生产系统上部署同一套代码，而不会为了生产系统修改代码。&lt;/li&gt;

  &lt;li&gt;自动打包，自动版本升级或回退。打包结果易于开发人员进行识别、分析及调试。&lt;/li&gt;

  &lt;li&gt;简单，清晰，性能高。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;上周实现的解决方案似乎基本符合这些条件，有机会我再来分享吧。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/09/relative-path-in-packed-static-resources.html#comments</comments>
      <pubDate>Sun, 05 Sep 2010 13:53:26 GMT</pubDate>
      <lastBuildDate>Sun, 05 Sep 2010 13:53:26 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/life/">生活心情</category>
      <title>为自己打造良好的文章阅读体验</title>
      <link>http://blog.zhaojie.me/2010/08/get-a-better-style-for-reading.html</link>
      <guid>http://blog.zhaojie.me/2010/08/get-a-better-style-for-reading.html</guid>
      <description>&lt;p&gt;我的上一篇文章是《&lt;a href="http://blog.zhaojie.me/2010/08/my-view-of-good-blogging-theme.html"&gt;为读者打造良好的文章阅读体验&lt;/a&gt;》，是为广大“作者”所写的，希望大家的文章都能长得漂亮。但无论怎么样，我们作为读者一定还是会遇到大量阅读体验糟糕的的文章，它们可能是一些只重视搜索引擎流量的无良小站，或是很难推动调整的大站、或是审美情趣有所特色的个人站长等等。根据我的个人经验，如果能够为自己打造一个舒适习惯的阅读体验，会让文章阅读的效率更高，甚至心情也能变得愉快放松。现在这篇文章，便是为了广大“读者”朋友写的，如果能够帮助您“爱上阅读”，那便善莫大焉了。&lt;/p&gt;

&lt;h1&gt;Readability&lt;/h1&gt;

&lt;p&gt;有时候，您在打开某篇文章之后，是否会感到有些心烦意乱？例如&lt;a href="http://news.csdn.net/a/20100823/278511.html"&gt;这篇&lt;/a&gt;：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/reading-style/before-readability.png" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/reading-style/before-readability-s.png" /&gt;&lt;/a&gt; 

&lt;p&gt;四处是广告（还是&lt;a href="http://blog.zhaojie.me/2010/04/why-i-say-no-to-aptech.html"&gt;令人厌恶的北大青鸟&lt;/a&gt;），还有旁边的相关链接，下方的评论以及其它一些内容，说实话，它们对我来说都是没有太多用处的东西，亮闪闪的反而影响我阅读。其实平心而论，这张页面的格式并不能算是最差（例如字体大小和行/段间距其实还都不错），还有许多站点可谓是点开便觉恶心。但是无论是哪张页面，我们都有办法将其归至一统——注意到图片中浏览器右上方的“R”字样按钮吗？点击之后，页面的主要内容便会被提取出来了，如下：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/reading-style/after-readability.png" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/reading-style/after-readability-s.png" /&gt;&lt;/a&gt; 

&lt;p&gt;现在看起来便舒服多了，这便是Readability的神奇效果。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lab.arc90.com/experiments/readability/"&gt;Readablity&lt;/a&gt;是一款用于提取页面主体阅读部分的工具，它&lt;a href="http://www.apple.com/safari/whats-new.html#reader"&gt;内置于Safari 5中&lt;/a&gt;，也以&lt;a href="https://chrome.google.com/extensions/detail/mopngoefggcibgpnefgiojkehfmdfcjn"&gt;Chrome插件&lt;/a&gt;及&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/46442/"&gt;Firefox插件&lt;/a&gt;的形式供用户使用，而IE等其他浏览器也可以直接将主站上的一段脚本加入书签栏，在需要的时候执行这条书签即可。除了去除大量垃圾之外，Readability的功效便是将各种阅读样式进行了统一，我们在习惯了这样的阅读版式之后效率便会有很大提高。&lt;/p&gt;

&lt;p&gt;Readability提供了几种不同的样式、边距和字体大小供选择。如果您觉得还不够，也可以进行一些简单的改造。例如对于程序员朋友来说，Readability对于&amp;lt;pre&amp;gt;的各种样式都不适合代码展示。Readability并没有什么特别神奇的地方，无论是主站提供的脚本还是浏览器插件，其实它都是在页面上执行了一段JavaScript，从页面的DOM中找出文章主体部分，然后改造页面而已。JavaScirpt以及样式表等文件都是完全透明的，我们只要根据自己需要进行修改即可。&lt;/p&gt;

&lt;p&gt;只可惜，对于少数页面来说，Readability无法正确提取出文章的主体部分。不过这也没关系，已经基本够用了，特殊的页面我们可以使用特殊的办法处理（就不在本文详述了）。&lt;/p&gt;

&lt;h1&gt;Stylish及Greasemonkey&lt;/h1&gt;

&lt;p&gt;Firefox下有两个非常著名的插件：&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/2108/"&gt;Stylish&lt;/a&gt;和&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/748/"&gt;Greasemonkey&lt;/a&gt;（Chrome中也有对应插件或功能）。Stylish可以让我们在指定URL的页面中自动运用一段样式代码，而Greasemonkey则是自动执行一段脚本。因此从理论上说，任何样式令人不爽的页面，我们都可以将其改造至符合个人口味——只要为其有针对性地编写一段脚本或样式表（甚至仅仅是样式表）即可。当然，为每个站点都订制一番是不可能的，但我们可以先将其进行“聚合”，然后只要统一处理就行了。&lt;/p&gt;

&lt;p&gt;最常见的“聚合”工具应该就是&lt;a href="http://www.google.com/reader"&gt;Google Reader&lt;/a&gt;了，但是我不是很能接受它在文章展现样式上的品味，例如：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/reading-style/before-stylish.png" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/reading-style/before-stylish-s.png" /&gt;&lt;/a&gt; 

&lt;p&gt;首先，它对于浏览器横向区域的利用很不彻底，因为它将一些容器的max-width设为了650px，这让我很疑惑。此外，它的字体太小，行距也显得过于紧凑。这些都是&lt;a href="http://blog.zhaojie.me/2010/08/my-view-of-good-blogging-theme.html"&gt;我在上一篇文章里所反对的&lt;/a&gt;。由于这个原因，我之前也花费了不少精力来寻找能够和Google Reader同步的客户端工具，甚至有花钱买的冲动，但始终没有找到令人满意的。不过有了Stylish之后，我便可以为这张页面自动添加一些特定的样式：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;@-moz-document &lt;/span&gt;url-prefix('http://www.google.com/reader/view/') {

    &lt;span style="color: maroon"&gt;.entry .entry-body&lt;/span&gt;,
    &lt;span style="color: maroon"&gt;.entry .entry-title&lt;/span&gt;,
    &lt;span style="color: maroon"&gt;.entry .entry-likers &lt;/span&gt;{
        &lt;span style="color: red"&gt;max-width&lt;/span&gt;:&lt;span style="color: blue"&gt;none !important&lt;/span&gt;;
    }
    
    &lt;span style="color: maroon"&gt;.entry-main .entry-body &lt;/span&gt;{
        &lt;span style="color: red"&gt;font&lt;/span&gt;:&lt;span style="color: blue"&gt;14px/180% &amp;quot;Trebuchet MS&amp;quot;,Georgia,&amp;quot;Times New Roman&amp;quot;,Times,sans-serif&lt;/span&gt;;
    }
    
    &lt;span style="color: maroon"&gt;.entry-main .entry-body p &lt;/span&gt;{
        &lt;span style="color: red"&gt;line-height&lt;/span&gt;:&lt;span style="color: blue"&gt;180%&lt;/span&gt;;
        &lt;span style="color: red"&gt;margin&lt;/span&gt;:&lt;span style="color: blue"&gt;12px auto&lt;/span&gt;;
    }
}&lt;/pre&gt;

&lt;p&gt;简单地说，这段代码便是为Google Reader的特定URL引入了改变max-width和字体大小的CSS样式，于是Google Reader的文章浏览界面就变成了：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/reading-style/after-stylish.png" target="_blank"&gt;&lt;img src="http://img.zhaojie.me/blog/reading-style/after-stylish-s.png" /&gt;&lt;/a&gt;

&lt;p&gt;事实上，我认为浏览器对于常用标签的默认样式往往都很不错，作为阅读来说也是比较合适的。因此做出这点简单的调整，对我来说就已经差不多了。更多的调整，还是等遇上哪篇难以接受的文章再说吧——我不做过早优化。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/08/get-a-better-style-for-reading.html#comments</comments>
      <pubDate>Tue, 24 Aug 2010 08:55:33 GMT</pubDate>
      <lastBuildDate>Sat, 27 Nov 2010 10:20:24 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/life/">生活心情</category>
      <title>为读者打造良好的文章阅读体验</title>
      <link>http://blog.zhaojie.me/2010/08/my-view-of-good-blogging-theme.html</link>
      <guid>http://blog.zhaojie.me/2010/08/my-view-of-good-blogging-theme.html</guid>
      <description>&lt;p&gt;几个月前我在微软“社区精英计划”上谈过“&lt;a href="http://blog.zhaojie.me/2010/03/how-to-become-a-good-tech-blogger.html"&gt;如何打造受人欢迎的博客&lt;/a&gt;”，其中一点便是“样式美观”，这也是如&lt;a href="http://www.hanselman.com/blog/"&gt;Scott Hanselman&lt;/a&gt;这样的业界著名博主&lt;a href="http://www.hanselman.com/blog/BlogInteresting32WaysToKeepYourBlogFromSucking.aspx"&gt;给出的建议之一&lt;/a&gt;。对于写文章、读文章来说，我可谓完全是外貌协会的高级会员，一旦遇上样式排版不好的文章、再好的内容我也难以坚持阅读下去——我也相信很多人多多少少有和我一样的习惯。有人说，我们是程序员，做不来样式怎么办？其实在我看来，打造一个良好的文章阅读体验并不困难，甚至只有那么几点而已。虽然我不是设计师，但我现在也想来谈一下我在这方面的习惯和“品味”。&lt;/p&gt;

&lt;h1&gt;选择一个好的主题&lt;/h1&gt;

&lt;p&gt;其实，要写出样式良好的文章非常容易，一般只要选择一个样式美观的主题即可。各个BSP一般都已经提供了部分不错的主题，而对于像Wordpress这样的博客系统，华丽优雅的主题更加数不胜数，甚至已经形成了一个产业。因此，如果您可以找到这样一个主题，那么八成的工作就已经完成了。但是，有些时候令人满意的主题并不那么容易找到，而且适合英文内容的主题，也不一定适合中文博客（例如我现在的皮肤便是经过一番修改的）。稍后我也会提出一些“准则”，这可能有助于您订制出更容易令人接受的阅读效果。&lt;/p&gt;

&lt;p&gt;选择了皮肤，其实就是选择了一套CSS样式，Web标准已经帮我们订制了HTML + CSS的黄金搭档（画外音：我TMD还脑白金呢）。CSS负责样式，HTML则提供内容，换句话说，我们文章的内容就是以纯粹的HTML形式提供的，不应该牵扯到样式。这大大减少了我们写作文章时需要考虑的因素，因为我们几乎只需要使用p、span、h1、blockquote、strong、a、span、ul、ol、li等最最简单的几种标记即可。因此我认为，最适合写博客的工具是Windows Live Writer，而不是Word或是其他的网页编辑器，因为它们都太复杂了，容易出现各种无用的标签及样式。&lt;/p&gt;

&lt;p&gt;最后，在写文章的时候，也应该使用正确的标记。例如自然段就使用p，而不是使用div或是br。各级标题就使用h1等等，而不是使用p + strong的形式。如果您真希望标题和正文的文字大小一致，那么应该使用“样式”去控制，我们的文章内容应该结构良好，语义清晰。&lt;/p&gt;

&lt;p&gt;如果您写好了一篇文章想要发布，也不放在最后检查一下它的HTML。应该是十分整洁的“顺序标记”，而不是混乱的标记、属性以及大量的嵌套——我们只是在写文章，不是在做网页。&lt;/p&gt;

&lt;h1&gt;怎样才是良好的阅读体验&lt;/h1&gt;

&lt;p&gt;各花入各眼，可能每个人眼中“良好的阅读体验”是不同的，我现在来谈一下“我眼中”良好阅读体验的几个特点。&lt;/p&gt;

&lt;p&gt;首先，&lt;strong&gt;“文字”要大，“行间距”不能太小，“段间距”更要明显&lt;/strong&gt;。我一直觉得中文在Web设计领域一直难以舒展手脚，且不说英文在网页上可以使用那么多的字体，而中文字体几乎只能使用单调的样式，就论英文和中文的大小吧，英文字体可以很小同时做到清晰美观，而中文字体却不能太小，否则便难以辨认。因此很多时候一个适合英文的博客皮肤，便不一定适合直接套用在中文内容上。此外，“行间距”和“段间距”过小，会给人头皮发麻的感觉，眼睛在扫视的时候很容易失去焦点，难以让人坚持阅读下去。&lt;/p&gt;

&lt;p&gt;其次，对于程序员来说，&lt;strong&gt;代码格式也相当重要&lt;/strong&gt;。代码且一定要使用“等宽字体”，如Consolas、Monaco等等都是不错的代码字体。代码的行间距则不能太大，如果说正文的line-height往往是160%到180%的话，那么代码的行间距则是120%到140%会比较合适。还有一点便是代码的着色，&lt;a href="http://blog.zhaojie.me/2009/12/syntax-highlighting-server-side.html"&gt;服务器端着色&lt;/a&gt;和&lt;a href="http://blog.zhaojie.me/2009/12/syntax-highlighting-client-side.html"&gt;客户端着色&lt;/a&gt;都可以，事实上在我看来使用&lt;a href="http://alexgorbatchev.com/SyntaxHighlighter/"&gt;SyntaxHighlighter&lt;/a&gt;已经可以满足大部分要求了。&lt;/p&gt;

&lt;p&gt;一般来说，&lt;strong&gt;文章的各部分内容都要样式良好&lt;/strong&gt;，且容易识别。例如代码段和正文要容易区分，链接也要使用特别的颜色。此外您在代码中可能还会使用“引用（blockquote）”或“列表（ul、ol、li）”等功能，此时您就应该为这些标记定义良好的样式。这些工作往往并不麻烦，因为“良好”并不代表“华丽”或是“复杂”，一般来说我们只需要简单的样式就行了——例如一篇标记良好的“裸文”，使用浏览器默认的样式往往看上去都不错。&lt;/p&gt;

&lt;h1&gt;关于宽屏浏览器&lt;/h1&gt;

&lt;p&gt;由于现在宽屏显示器越来越多，例如，根据Google Analytics的统计，我的博客访问者中只有不到15%使用1024宽度的分辨率：&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/blog-style/screen-resolution-20100820.png" /&gt; 

&lt;p&gt;那么，如果还是使用标准的960像素宽度的设计，在我看来已经不太合适了，它会造成横向的空白太多。但是，如果使用更大的宽度，又会让1024宽度分辨率的用户看到横向的滚动条，这也是令人无法接受的体验。最后我使用的做法是让页面进行“自适应”，如果您使用1024宽度的分辨率，您会发现正文和边栏的宽度差不多，这可能不够美观，但是您不会看到横向的滚动条。如果您使用宽度更大的浏览器窗口，就会发现我的博客对宽屏的“优化效果”了（&lt;a href="http://img.zhaojie.me/blog/blog-style/width-1440.png" target="_blank"&gt;点此下载全尺寸图片&lt;/a&gt;）：&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/blog-style/width-1440-s.png" /&gt; 

&lt;p&gt;以上是1440像素宽度的显示效果。由于充分利用横向距离，我遍可以更好地利用侧边栏的空间，让用户可以浏览到更多的内容，也缓解了“侧边栏过高”的问题，如今我的绝大部分文章都是左侧正文评论区域的更高（当然这也有评论较多的原因在里面）。不过太宽也不是一件好事，这样会显得文章正文部分单行太长，内容过于松散，因此我的博客最大也只会有1600像素宽（&lt;a href="http://img.zhaojie.me/blog/blog-style/width-2560.png" target="_blank"&gt;点此下载全尺寸图片&lt;/a&gt;）：&lt;/p&gt;
&lt;img src="http://img.zhaojie.me/blog/blog-style/width-2560-s.png" /&gt; 

&lt;p&gt;以上是在iMac27上的全屏显示效果，其宽度为2560像素。此外，如果您把浏览器缩到极窄，您便会看到横向滚动条，因为我保证了博客至少也有1000像素宽。在打造博客皮肤时我在这方面花费了很多精力。我就是个这么挑剔的人。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/08/my-view-of-good-blogging-theme.html#comments</comments>
      <pubDate>Fri, 20 Aug 2010 09:01:45 GMT</pubDate>
      <lastBuildDate>Fri, 20 Aug 2010 09:01:45 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/parallel/">并行处理</category>
      <category domain="http://blog.zhaojie.me/cutting-edge/">技术尝鲜</category>
      <category domain="http://blog.zhaojie.me/speech/">培训演讲</category>
      <title>盛大创新院赞助第二届.NET技术交流会开始报名了！</title>
      <link>http://blog.zhaojie.me/2010/08/2nd-snda-dotnet-conference-sign-up.html</link>
      <guid>http://blog.zhaojie.me/2010/08/2nd-snda-dotnet-conference-sign-up.html</guid>
      <description>&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-in.png" /&gt; 

&lt;p&gt;自上次&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;盛大创新院&lt;/a&gt;赞助的&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html"&gt;首届.NET技术交流会&lt;/a&gt;到现在已经有两个月，这意味着按照原来的“一季一次”的计划也已经离第二次的活动不远了，考虑到9月份的中秋和国庆假期将工作日和休息日搞的支离破碎，于是交流会的时间会略微有些提前。第二届交流会的形式与上次相同，将为您献上四场高质量的技术演讲。当然这次在内容上有了新的尝试，除了引入了算法及面向对象设计的内容之外，这次更是请到了“传说中的大侠”为大家带来有关Windows内核的深度内容。&lt;font color="#ff0000"&gt;人数暂定为200人，事不宜迟，赶快报名吧&lt;/font&gt;。除了.NET社区的群众以外，也欢迎其他技术社区的朋友前来参与交流。事实上，我组织技术交流会的目的之一便是希望能够促进.NET社区与其他技术社区的交流及相互学习。&lt;/p&gt;

&lt;h1&gt;时间及议程安排&lt;/h1&gt;

&lt;p&gt;第二次交流会定于&lt;strong style="color: red"&gt;2010年9月11日&lt;/strong&gt;（周六）举行，具体时间及议程安排如下。本次依然安排了四场演讲，不过在内容上有了新的尝试：在与一些朋友和同事进行沟通之后，我决定在第二次交流会上引入与算法相关的议题，由创新院内部的ACM/ICPC达人来谈一下日常工作中的算法，如果您对于算法在工作的处境所有疑惑的话，这也是个共同探讨的好机会；此外，我也邀请了&lt;a href="http://www.5173.com/"&gt;5173.com&lt;/a&gt;的技术专家来讨论面向对象设计方面的问题，我看过他过去在内部演讲时使用的PPT，内容很充实；而这次的“重头戏”，便是请到了传说中的&lt;font color="#ff0000"&gt;潘爱民&lt;/font&gt;老师为大家演讲Windows内核方面的问题。潘老师是业界著名技术专家，不久前加入了盛大创新院，可谓“镇院之宝”。事实上，在9月初潘老师便会回到北京工作，而这次他也是为了交流会专门出差至上海，您怎能错过这次机会？&lt;/p&gt;

&lt;table style="text-align: center" border="1" cellspacing="0" cellpadding="5"&gt;&lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;时间&lt;/th&gt;

      &lt;th&gt;议程&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;12:30 ~ 13:00&lt;/td&gt;

      &lt;td&gt;签到&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;13:00 ~ 14:00&lt;/td&gt;

      &lt;td&gt;响应式编程与响应式框架&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;14:00 ~ 14:10&lt;/td&gt;

      &lt;td&gt;短休&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;14:10 ~ 15:10&lt;/td&gt;

      &lt;td&gt;大话程序员可用的算法&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;15:10 ~ 15:40&lt;/td&gt;

      &lt;td&gt;茶歇&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;15:40 ~ 16:40&lt;/td&gt;

      &lt;td&gt;面向对象与生活&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;16:40 ~ 16:50&lt;/td&gt;

      &lt;td&gt;短休&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;16:50 ~ 17: 50&lt;/td&gt;

      &lt;td&gt;Windows内核技术介绍&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;h1&gt;演讲内容&lt;/h1&gt;

&lt;p&gt;以下是关于四场演讲的详细描述。&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/zhaojie.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/zhaojie.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;h2&gt;响应式编程与响应式框架&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;赵劼，盛大创新院，研究员。关注前沿技术，并致力于开源社区与微软平台的组合优化。对函数式编程，并行程序开发，代码之美以及程序员能力与修养等相关问题也有着浓厚的兴趣，同时非常希望能够写程序到60岁。最近致力于F#，Scala语言及mono平台在社区中的推广。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;异步编程改变了我们的编程方式，也为我们带来的许多挑战，同时让一些编程模型重新焕发了生机。与传统的“拉”模型不同，响应式编程将异步事件流视为可观察的集合，这是一种“推”模型。微软为了提高云时代的编程体验而设计了响应式框架，其目的是为了简化复杂事件处理之间混合操作。从中我们了解到一些异步编程的模式与LINQ使用技巧，并可以将这种编程模型普及到JavaScript等其他平台上去。&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/chengshaofei.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/chengshaofei.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;h2&gt;大话程序员可用的算法&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;程劭非，盛大创新院，研究员。网名winter，无忧脚本版主。Web前端技术的积极倡导者。学生时代曾经热衷于参加ACM/ICPC。目前工作在Bambook电子书项目，主要负责文字排版和浏览器引擎WebKit相关。之前曾负责在Windows CE系统上的IE开发。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;俗话说“数据结构+算法=程序”，算法是什么？算法书里满篇是看不懂的形式化推导，网上一些&amp;quot;高人&amp;quot;写的关于算法文章高深莫测，大公司面 试最让人讨厌的就是考算法题，“我做了这么多年，跟本在实际开发中就没用过算法！”，算法真的是距离我们如此遥远的东西吗？且听这回演讲， 算法究竟如何影响我们的开发。&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/gaoxiang.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/gaoxiang.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;h2&gt;面向对象与生活&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;高翔，5173.com&amp;#160; 项目经理。关注前沿技术和技术人员的非技术生活。对面向对象、模式和建模技术有浓厚兴趣，并对游戏设计和图形学方面也比较感兴趣。最近在学习F#，Lua以及关注一些关于职业生涯规划方面的话题。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;面向对象这个话题虽然很热，但与哲学一样，很难给其一个很准确的定义。也因为如此，每个人对它都有自己的理解。本次演讲将从一个实际的例子出发，逐步引入面向对象的三个特征，结合对象的生命周期，以及基于事件的对象扩展方式等方面，探讨其与设计模式，与生活之间的联系。&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/panaimin.jpg" target="_blank"&gt;&lt;img class="floatRight" src="http://img.zhaojie.me/blog/snda-dotnet-conf/panaimin.jpg" height="150" /&gt;&lt;/a&gt; 

&lt;h2&gt;Windows内核技术介绍&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;潘爱民，盛大创新院专家，微软学者，集团COO专家顾问。长期从事软件和系统技术的研究和开发工作，撰写了大量软件技术文章，并著译了多部经典计算机图书。在MSR/清华等从事多年科研工作，在北大和清华多年执教经验。数学学士学位和计算机科学博士，主要研究领域包括软件设计、信息安全、操作系统和Internet技术。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;简介：&lt;/strong&gt;Windows操作系统经过二十年的发展，已臻成熟。Microsoft在推动Windows内核方面做了大量工作，譬如于2006年夏季向教育界开放了当时最为先进的内核源代码（Windows Research Kernel）。主讲者在这次讲座中，结合这些可利用的资源，分享对Windows内核研究的体会，尤其将重点讨论Windows中的I/O模型和环境子系统。&lt;/p&gt;

&lt;h1&gt;地点&lt;/h1&gt;

&lt;p&gt;本次交流会举办地为&lt;strong style="color: red"&gt;上海市浦东新区碧波路888号畅星大厦&lt;/strong&gt;（地铁二号线张江高科站下，步行10分钟可达）3楼会议厅，地图如下：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/changxing-map.png"&gt;&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/changxing-map.png" width="400" /&gt;&lt;/a&gt; 

&lt;p&gt;鸟瞰图：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/changxing-hybrid.jpg"&gt;&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/changxing-hybrid.jpg" width="400" /&gt;&lt;/a&gt; 

&lt;p&gt;畅星大厦外观：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/changxing-building.jpg"&gt;&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/changxing-building.jpg" width="400" /&gt;&lt;/a&gt; 

&lt;p&gt;会场实景照片：&lt;/p&gt;
&lt;a href="http://img.zhaojie.me/blog/snda-dotnet-conf/changxing-room.jpg"&gt;&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/changxing-room.jpg" width="400" /&gt;&lt;/a&gt; 

&lt;p&gt;会场容量可以容纳超过200人，希望到时候不会显得太过空旷。:)&lt;/p&gt;

&lt;h1&gt;报名信息&lt;/h1&gt;
&lt;img src="http://img.zhaojie.me/blog/snda-dotnet-conf/sign-up-now.jpg" /&gt; 

&lt;p&gt;本次交流会&lt;a href="http://www.diaochapai.com/survey504720"&gt;现已开始报名，请填写报名表&lt;/a&gt;，&lt;strike&gt;报名截止日期为2010年9月5日&lt;/strike&gt;人数已满，多谢大家支持。&lt;/p&gt;

&lt;h1&gt;相关文章&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;盛大创新院赞助第二届.NET技术交流会开始报名了！ &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-is-coming.html"&gt;盛大创新院赞助第二届.NET技术交流会即将召开&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-all-slides.html"&gt;盛大创新院赞助第二届.NET技术交流会 - 各场演讲幻灯片&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://blog.zhaojie.me/2010/09/2nd-snda-dotnet-conference-videos.html"&gt;盛大创新院赞助第二届.NET技术交流会 - 演讲录像及下载&lt;/a&gt; &lt;/li&gt;

  &lt;li&gt;盛大创新院赞助首届.NET技术交流会：&lt;a href="http://blog.zhaojie.me/2010/05/first-snda-dotnet-conference-sign-up.html"&gt;报名&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-is-coming.html"&gt;预告&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-all-slides.html"&gt;幻灯片&lt;/a&gt;、&lt;a href="http://blog.zhaojie.me/2010/06/first-snda-dotnet-conference-videos.html"&gt;演讲录象及下载&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <comments>http://blog.zhaojie.me/2010/08/2nd-snda-dotnet-conference-sign-up.html#comments</comments>
      <pubDate>Mon, 16 Aug 2010 03:02:30 GMT</pubDate>
      <lastBuildDate>Mon, 16 Aug 2010 03:02:30 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/news/">新闻信息</category>
      <title>活动：来自Opera挪威的子斌与上海前端工程师交流HTML5 &amp; CSS3</title>
      <link>http://blog.zhaojie.me/2010/08/html5-css3-talk-in-snda-innovation.html</link>
      <guid>http://blog.zhaojie.me/2010/08/html5-css3-talk-in-snda-innovation.html</guid>
      <description>&lt;p&gt;最近有些忙，疏于写博客，在此先带来一个&lt;a href="http://blog.zhaojie.me/2010/01/1651772.html"&gt;盛大创新院&lt;/a&gt;周末的活动通知，希望感兴趣的朋友可以来参加。地点在创新院，到时候会有专人负责接待。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;内容：&lt;/strong&gt;子斌将会带来一个关于HTML5和CSS3的演讲，介绍HTML5和CSS3的新特性以及一些实用技巧，并且会跟大家就一些关心的问题进行讨论。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;讲师：&lt;/strong&gt;子斌，Web Evangelist（Web传教士），在Opera挪威总部工作，其博客为：&lt;a href="http://zibin.tehais.com/"&gt;http://zibin.tehais.com/&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;时间：&lt;/strong&gt;2010年8月7日（周六）13：00-16：00&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;地点：&lt;/strong&gt;华佗路68号8号楼（盛大创新院）4楼万象报告厅，请提前5分钟入场签到，现场有无线与移动设备电源，咖啡在隔壁，请注意卫生与安全饮水。&lt;/p&gt;

&lt;p&gt;在此邀请各位对于HTML5 &amp;amp; CSS3感兴趣的朋友一起来参加交流探讨。请在以下网址报名：&lt;a href="http://www.51qiangzuo.com/events/235-htm5talk"&gt;http://www.51qiangzuo.com/events/235-htm5talk&lt;/a&gt;&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2010/08/html5-css3-talk-in-snda-innovation.html#comments</comments>
      <pubDate>Tue, 03 Aug 2010 08:33:19 GMT</pubDate>
      <lastBuildDate>Tue, 03 Aug 2010 08:33:19 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>使用Fiddler辅助观看InfoQ的视频</title>
      <link>http://blog.zhaojie.me/2009/12/fiddler-infoq-video.html</link>
      <guid>http://blog.zhaojie.me/2009/12/fiddler-infoq-video.html</guid>
      <description>&lt;p&gt;&lt;a href="http://www.infoq.com"&gt;InfoQ&lt;/a&gt;是一个好地方，而我认为其中最有价值的资源，便是其中的演讲视频。InfoQ在这方面有个特点：在演讲视频下方提供了清晰的幻灯片，而在播放的同时，还会根据进度进行切换。这观看体验自然比单纯的演讲录像要高出许多。可惜的是，时不时有朋友会向我反馈说，InfoQ实在是太慢，几乎无法流畅地观看视频。由于一时半会儿InfoQ也不会在中国放CDN，因此视频加载速度这问题……几乎无法解决。还好，如果我们退而求其次，至少可以使用Fiddler等工具来“缓解”这个问题。&lt;/p&gt;  &lt;h1&gt;Fiddler简介&lt;/h1&gt;  &lt;p&gt;&lt;a href="http://www.fiddler2.com/fiddler2/"&gt;Fiddler&lt;/a&gt;是Eric Lawrence完全使用.NET开发的一个免费HTTP调试工具，后来微软连人和工具一起收购之后，也把它在当作一个产品在做。Fiddler在工作时本身并不进行网络嗅探的工作，它只是充当一个“代理服务器”的功能，而任何软件只要使用这个代理，Fiddler便可以截获它发出的所有请求，进行跟踪，查看，甚至我们一会儿会看到的更多高级工作。关于Fiddler的更多信息，您可以关注&lt;a href="http://v.youku.com/v_show/id_XMTM0MzE0ODk2.html"&gt;PDC 09中Eric Lawrence做的演讲&lt;/a&gt;，内容还是挺充实的。&lt;/p&gt;  &lt;p&gt;因为有了Fiddler，我几乎已经完全不用Firebug的Net功能了，因为它的功能有限，且只能基于Firefox运行。由于Fiddler的工作原理，它可以在任何一个使用代理服务器的工具中使用。当然，Fiddler的这个工作原理，也导致它无法获得某些信息。例如Firebug可以获得某个请求在浏览器中的等待时间，或是DNS的解析时间——而Fiddler只能跟踪请求发起后的情况。&lt;/p&gt;  &lt;p&gt;Fiddler还支持扩展，编写Fiddler扩展很容易。Fiddler还提供了如FiddlerCore这样的核心类库，可以让我们的程序提供如Fiddler那样的嗅探功能。不过这些话题超出了这次的内容，我们这次只使用Fiddler自带的功能。&lt;/p&gt;  &lt;h1&gt;下载视频&lt;/h1&gt;  &lt;p&gt;那么，我们又该如何使用Fiddler来辅助观看InfoQ的视频呢？因为视频在线观看速度很慢，我们很自然想到，把它下载到本地再观看就没有这个问题了。一般此类在线视频都是请求一个flv文件，而这个文件的路径可以轻松地使用Fiddler获取到。&lt;/p&gt;  &lt;p&gt;例如，您可以打开Fiddler，在浏览器中进行代理服务器设置（可能自动已经设好了），然后打开InfoQ中&lt;a href="http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter"&gt;Statically Dynamic Typing&lt;/a&gt;这个视频，代页面加载完毕后点击视频的播放按钮。于是，Fiddler便可以截获如下请求：&lt;/p&gt; &lt;img src="http://img.zhaojie.me/blog/fiddler-infoq-video/fiddler-infoq-video-1.png" /&gt;   &lt;p&gt;请看上图中截获的最后那个请求，是一个flv文件。您可以对其点击鼠标右键，再选择Copy - Just Url，便可以复制这个文件的完整地址。然后，您便可以使用下载工具或任何方式把这个文件下载到本地，存放在硬盘上。至此第一步就完成了。&lt;/p&gt;  &lt;h1&gt;设置过滤条件&lt;/h1&gt;  &lt;p&gt;对于某些站点的视频播放来说，您已经可以直接查看下载后的文件了。不过对于InfoQ的视频来说，您如果直接在本地播放，便无法享受“幻灯片”的同步功能。很自然的，我们希望视频播放页面可以使用我们本地的文件。不过这是下一步要做的事情。而现在我们先来做另一件事情，那便是设置Fiddler的过滤条件。&lt;/p&gt;  &lt;p&gt;由于Fiddler的原理是截获通过自身（即代理）的所有请求，因此有时候也会得到很多不必要的信息。例如，我们在Internet Options中设置了代理服务器后，所有IE或Chrome的页面，甚至MSN亦或是您自己写程序时使用WebClient或WebRequest发出的请求，都会通过这个代理。此时您会发现Fiddler中的请求密密麻麻，且会不断增长，非常影响调试工作。&lt;/p&gt;  &lt;p&gt;此时，您可以设置一个过滤条件，告诉Fiddler，只有满足特定条件的请求才捕获进来。设置过滤条件并不难，Fiddler提供了一个Process Filter小图标（如上图），您可以把它拖拽至某个程序窗口上，这样只有那个进程发出的请求才会被捕获。不过这么做有时很不方便，因为如现在的浏览器，如IE 8或Chrome都是多Tab的，每个Tab都属于不同的进程，拖放时很可能会不知所云。因此，我们在这里手动设置过滤条件：&lt;/p&gt; &lt;img src="http://img.zhaojie.me/blog/fiddler-infoq-video/fiddler-infoq-video-2.png" /&gt;   &lt;p&gt;首先，还是上图的结果，不过我们把横向滚动条拖至右侧，可以发现在Process栏目中写着“&lt;font color="#ff0000"&gt;chorme:6764&lt;/font&gt;”字样，这表示该进程的名称及pid。于是，我们可以在Fiddler的Filter栏目中把它设为唯一的过滤条件：&lt;/p&gt;  &lt;img src="http://img.zhaojie.me/blog/fiddler-infoq-video/fiddler-infoq-video-3.png" /&gt;  &lt;p&gt;再试试，看看Fiddler是不是只截获那个Tab所发出的请求？&lt;/p&gt;  &lt;h1&gt;设置断点&lt;/h1&gt;  &lt;p&gt;万事俱备，只欠东风。我们要做的最后一步，便是让Fiddler把原本对远端flv文件的请求转为本地的请求。这便可以利用Fiddler的断点功能。&lt;/p&gt;  &lt;p&gt;由于Fiddler会在所有捕获的请求上进行中断，因此我们必须减少捕获的请求。例如，把过滤条件设为视频播放页面便是一个不错的选择。假如您已经设好了过滤条件，那么可以打开播放页面（但不要进行播放），再清除Fiddler中已经捕获到的信息。然后，设置断点：&lt;/p&gt;  &lt;img src="http://img.zhaojie.me/blog/fiddler-infoq-video/fiddler-infoq-video-4.png" /&gt;  &lt;p&gt;选择Before Requests，这样Fiddler会在得到请求信息之后暂停，等待我们后续处理（图片可点击放大）：&lt;/p&gt; &lt;a href="http://img.zhaojie.me/blog/fiddler-infoq-video/fiddler-infoq-video-5.png"&gt;&lt;img src="http://img.zhaojie.me/blog/fiddler-infoq-video/fiddler-infoq-video-5.png" width="640px" /&gt;&lt;/a&gt;   &lt;p&gt;然后，我们只要选择Choose Response - Find a file：&lt;/p&gt; &lt;img src="http://img.zhaojie.me/blog/fiddler-infoq-video/fiddler-infoq-video-6.png" /&gt;  &lt;p&gt;然后选择我们我们下载到本地的文件，在点滴“Run to Completion”就可以了。不过，开始播放之后记得禁用Fiddler的断点，否则页面在切换幻灯片的时候也会被Fiddler暂停——这就不太好了。&lt;/p&gt;  &lt;h1&gt;总结&lt;/h1&gt;  &lt;p&gt;看看，现在播放起来是不是很流畅？Fiddler其实还有许多有用的功能，几乎可以说是“Web相关”开发人员的必备工具了。Fiddler的发展也很迅猛，本身也有非常强大的可扩展性。如果您的项目需要有类似Fiddler的嗅探功能，不妨可以朝这方面进行一番探索。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/12/fiddler-infoq-video.html#comments</comments>
      <pubDate>Tue, 29 Dec 2009 04:04:00 GMT</pubDate>
      <lastBuildDate>Tue, 29 Dec 2009 04:04:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>浅谈代码着色（下）：服务器端着色</title>
      <link>http://blog.zhaojie.me/2009/12/syntax-highlighting-server-side.html</link>
      <guid>http://blog.zhaojie.me/2009/12/syntax-highlighting-server-side.html</guid>
      <description>&lt;p&gt;&lt;a href="http://blog.zhaojie.me/2009/12/syntax-highlighting-client-side.html#1724538"&gt;上篇文章&lt;/a&gt;谈了客户端着色，而现在自然就来讨论服务器端着色了。先下个定义：我在这里谈的“服务器端着色”，是指直接从服务器端输出着色效果的做法（与“客户端着色时”输出纯代码文本相对）。至于这个着色效果是如何获得的，例如是由另一个用户直接提供的，还是用户提供纯代码文本，而用服务器端逻辑“着色”，在这里就统称为“服务器端”着色了。不过接下去的讨论，我们还是会作一些区分。&lt;/p&gt;  &lt;p&gt;客户端着色的最大问题，在于很可能会出现着色错误，例如这段C#代码：&lt;/p&gt;  &lt;pre class="brush:csharp"&gt; 
public string Foo
{
    get
    {
        //*
        int set = 0;
        int value = 0;
        string yield = &amp;quot;/*&amp;quot; + set + value + &amp;quot;*/&amp;quot;;
        //*/
    }
}&lt;/pre&gt;

&lt;p&gt;或者这段VB.NET：&lt;/p&gt;

&lt;pre class="brush:vbnet"&gt; 
Dim [Dim] As My.Dim
Dim [REM] = &amp;quot;REM asdf&amp;quot;&amp;quot; 'Dim a As Dim&amp;quot;&amp;quot;&amp;quot; 'Dim a As String
Dim x = &amp;lt;Dim Rem=&amp;quot;Dim '&amp;quot;&amp;gt;'HaHa &amp;quot;&amp;lt;/Dim&amp;gt;&lt;/pre&gt;

&lt;p&gt;很明显这两段代码的着色都有问题（如C#的set关键字和VB.NET的XML Literal）——它们是&lt;a href="http://www.cnblogs.com/Ninputer/"&gt;脑袋&lt;/a&gt;在上篇文章的评论中&lt;a href="http://blog.zhaojie.me/2009/12/syntax-highlighting-client-side.html#1724534"&gt;给出的两个例子&lt;/a&gt;，他解释道：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;基于简单文本查找的代码着色很可能会在注释，字符串之间混杂嵌套的地方出错。比如连续两个/* */，可能会匹配到较远的一个。还有//和/*等同时出现的情况。现在的很多语言都有两种以上的注释格式。同样，字符串的&amp;quot;&amp;quot;和''还有转义，都不是简单替换算法能解决的。&lt;/p&gt;

  &lt;p&gt;基于词法的着色无法解决限定标识符的问题。比如DataColumn.ReadOnly中，ReadOnly是VB的关键字，但这里是被DataColumn限定的标识符，不应该变成蓝色。一般限定名称的解析不是在词法分析阶段，所以能够解决这个问题的代码着色器极少。&lt;/p&gt;

  &lt;p&gt;另外想要达到将类型名称着色（像VS那样）必须做完整的语法分析。估计也不是现在任何着色器能做到的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;同样，您可以尝试着将代码放入Editplus或Notepad++等工具中查看，错误依旧。对于这个问题，您可以简单的理解为：由于进行“客户端着色”时缺少足够的缺少信息，因此在很多情况下着色会出现偏差。而由于VS本身使用完整的语法分析，因此它的着色效果自然无可挑剔：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Program
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;public string &lt;/span&gt;Foo
    {
        &lt;span style="color: blue"&gt;get
        &lt;/span&gt;{
            &lt;span style="color: green"&gt;//*
            &lt;/span&gt;&lt;span style="color: blue"&gt;int &lt;/span&gt;set = &lt;span style="color: brown"&gt;0&lt;/span&gt;;
            &lt;span style="color: blue"&gt;int &lt;/span&gt;value = &lt;span style="color: brown"&gt;0&lt;/span&gt;;
            &lt;span style="color: blue"&gt;string &lt;/span&gt;yield = &lt;span style="color: #a31515"&gt;&amp;quot;/*&amp;quot; &lt;/span&gt;+ set + value + &lt;span style="color: #a31515"&gt;&amp;quot;*/&amp;quot;&lt;/span&gt;;
            &lt;span style="color: green"&gt;//*/
        &lt;/span&gt;}
    }
}&lt;/pre&gt;

&lt;p&gt;由于直接使用了VS的着色规则，因此这里的set关键字也正确了，Program作为类名也有漂亮的颜色，这样整段代码看上去非常赏心悦目，它也是我在博客上用的着色方式。我使用Windows Live Writer写博客，配合&lt;a href="http://gallery.live.com/liveItemDetail.aspx?li=d8835a5e-28da-4242-82eb-e1a006b083b9"&gt;Paste from Visual Studio&lt;/a&gt;插件，可以直接从Visual Studio里复制一段代码插入到文章内容中，例如对于上面的代码它便会生成这样的HTML：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;pre &lt;/span&gt;&lt;span style="color: red"&gt;class&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: blue&amp;quot;&amp;gt;&lt;/span&gt;class &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: #2b91af&amp;quot;&amp;gt;&lt;/span&gt;Program
&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;{
    &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: blue&amp;quot;&amp;gt;&lt;/span&gt;public string &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Foo
    {
        &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: blue&amp;quot;&amp;gt;&lt;/span&gt;get
        &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;{
            &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: green&amp;quot;&amp;gt;&lt;/span&gt;//*
            &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: blue&amp;quot;&amp;gt;&lt;/span&gt;int &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;set = &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: brown&amp;quot;&amp;gt;&lt;/span&gt;0&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;;
            &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: blue&amp;quot;&amp;gt;&lt;/span&gt;int &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;value = &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: brown&amp;quot;&amp;gt;&lt;/span&gt;0&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;;
            &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: blue&amp;quot;&amp;gt;&lt;/span&gt;string &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;yield = &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: #a31515&amp;quot;&amp;gt;&lt;/span&gt;&lt;span style="color: red"&gt;&amp;amp;quot;&lt;/span&gt;/*&lt;span style="color: red"&gt;&amp;amp;quot; &lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;+ set + value + &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: #a31515&amp;quot;&amp;gt;&lt;/span&gt;&lt;span style="color: red"&gt;&amp;amp;quot;&lt;/span&gt;*/&lt;span style="color: red"&gt;&amp;amp;quot;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;;
            &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: green&amp;quot;&amp;gt;&lt;/span&gt;//*/
        &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;}
    }
}&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;pre&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;很长，还好我们平时不会去阅读这类HTML源文件表现的具体内容。我们可以发现，在这段HTML中，所有的颜色都直接出现在代码中。这么做的优点在于即便是别人通过RSS订阅了您的文章，再它的阅读器里也可以看到美观而工整的着色样式。当然，它也有一些缺点。而它最大的问题便在于样式和内容直接耦合在了一起。也就是说，这么做的显示效果其实严重依赖它所存在的环境。假设，我忽然有一点想要把博客的风格切换成很酷的黑色，这样代码块的阅读体验就十分堪忧了。同样，如果读者的RSS阅读器使用了某种花哨的风格……&lt;/p&gt;

&lt;p&gt;要解决这个问题并不容易。一个可以尝试的做法是在HTML中使用class来标记元素，而并非直接将颜色代码写在HTML当中。例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;class&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;keyword&amp;quot;&amp;gt;&lt;/span&gt;class &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;class&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;type&amp;quot;&amp;gt;&lt;/span&gt;Program&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;由于我们只是标记了HTML元素的“种类”，因此代码的样式便可以自由定义了。即便是我们换了一个新的环境，那提供另一套CSS样式即可。可惜的是，这种做法在RSS读者那边又失效了。当然，如果您也可以在服务器端的逻辑里为代码快进行重新着色……对于完美主义者，我们还是应该表示尊敬的。&lt;/p&gt;

&lt;p&gt;可惜的是，我在博客里还是直接内嵌颜色代码，而不是使用class+CSS的方式。第一个原因是由于我可能不会对博客样式有“质的调整”，但更重要的原因是……插件没有直接支持，可能是VS直接提供了着色结果，而不是“结构化数据”。当然，我们其实也可以重新写一个插件，从特定颜色代码“识别回”类型（其实普通的查找替换应该也足够了）。既然有Paste from Visual Studio插件作为示例，做到这点其实应该也是挺容易的。&lt;/p&gt;

&lt;p&gt;好吧，如果真的某一天我需要把博客切换为黑底，又该怎么办呢？没办法了，写一个程序，利用博客园提供的Weblogs API，批量替换一下吧。&lt;/p&gt;

&lt;p&gt;回到文章开头，我谈到“服务器端着色”的另一种情况，是使用服务器端的逻辑将代码块进行高亮，这种做法大都出现在处理wiki标记或BBCode时进行。很可惜，这也是在一种缺少上下文信息的环境下进行着色，与“客户端着色”一样，难以出现完美的着色效果。最后可能值得一提的是，我在某段不算太长的时间内还是用过一种“不堪入目”的着色方式：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;pre &lt;/span&gt;&lt;span style="color: red"&gt;class&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: blue&amp;quot;&amp;gt;&lt;/span&gt;public string &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Foo&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;br &lt;/span&gt;&lt;span style="color: blue"&gt;/&amp;gt;&lt;/span&gt;{&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;br &lt;/span&gt;&lt;span style="color: blue"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: red"&gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: blue&amp;quot;&amp;gt;&lt;/span&gt;get&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;br &lt;/span&gt;&lt;span style="color: blue"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: red"&gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;{&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;br &lt;/span&gt;&lt;span style="color: blue"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: red"&gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: blue&amp;quot;&amp;gt;&lt;/span&gt;return &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;style&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;&lt;/span&gt;&lt;span style="color: red"&gt;color&lt;/span&gt;&lt;span style="color: blue"&gt;: #a31515&amp;quot;&amp;gt;&lt;/span&gt;&lt;span style="color: red"&gt;&amp;amp;quot;&lt;/span&gt;bar&lt;span style="color: red"&gt;&amp;amp;quot;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;br &lt;/span&gt;&lt;span style="color: blue"&gt;/&amp;gt;&lt;/span&gt;&lt;span style="color: red"&gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&lt;/span&gt;}&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;br &lt;/span&gt;&lt;span style="color: blue"&gt;/&amp;gt;&lt;/span&gt;}&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;pre&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;感觉如何？其实它的最终结果只是如此简单：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public string &lt;/span&gt;Foo
{
    &lt;span style="color: blue"&gt;get
    &lt;/span&gt;{
        &lt;span style="color: blue"&gt;return &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;;
    }
}&lt;/pre&gt;

&lt;p&gt;由于博客园的后台的某个编辑器，总是“自做主张”地将&amp;lt;pre /&amp;gt;内的代码进行“优化”，把多个空白字符替换成一个，于是换行没了，缩进也没了。于是，我只能手动将换行替换为&amp;lt;br /&amp;gt;，将多个空格替换为&amp;amp;nbsp;。不过您还真别说，这个丑陋的做法还有另一个效果，那就是对于某些同样自做聪明的阅读器或浏览器（大都是移动平台上的程序），代码片断也能显示正常——而之前的所有方式，在那些设备上都只会显示为一行。&lt;/p&gt;

&lt;p&gt;真是可歌可泣。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/12/syntax-highlighting-server-side.html#comments</comments>
      <pubDate>Tue, 15 Dec 2009 07:38:00 GMT</pubDate>
      <lastBuildDate>Tue, 15 Dec 2009 07:38:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>浅谈代码着色（上）：客户端着色</title>
      <link>http://blog.zhaojie.me/2009/12/syntax-highlighting-client-side.html</link>
      <guid>http://blog.zhaojie.me/2009/12/syntax-highlighting-client-side.html</guid>
      <description>&lt;p&gt;作为程序员，写文章时总免不了插入代码，而对代码进行着色几乎已经成为一个基础，一个事实标准。代码着色的确可以大大加强代码的可读性，因此即便是再不待见IDE的朋友，代码着色永远是必不可少的。不过在网页中进行代码着色的方式有很多，现在我们就来对比一下。记得之前也有朋友写过，但我总觉得不够完整，于是还是自己写一下吧。&lt;/p&gt;  &lt;p&gt;这样过瘾。&lt;/p&gt;  &lt;p&gt;现在先来谈“客户端着色”。这种着色方式的基本做法，在于在HTML中显示（几乎）完全普通的文本，而在页面加载过程中在浏览器里进行着色（通常是使用JavaScript）。由于HTML是服务器端输出的，但它不负责着色，因此我把这个做法叫作是“客户端着色”。例如，著名的问答网站&lt;a href="http://stackoverflow.com"&gt;Stack Overflow&lt;/a&gt;便使用的是客户端着色——可惜，我不知道它使用了何种着色方式。&lt;/p&gt;  &lt;p&gt;如果我们要使用客户端着色，那么可以使用&lt;a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter"&gt;Syntax Highlighter&lt;/a&gt;这个JavaScript组件。这个组件现在非常流行。博客园便提供了内置的Syntax Hilighter支持。例如，它最基本的使用方式如下：&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;pre &lt;/span&gt;&lt;span style="color: red"&gt;class&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;brush:csharp&amp;quot;&amp;gt;&lt;/span&gt;
static void Main(string[] args)
{ 
    // comment
    var i = 1;
    var s
}
&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;pre&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;便可生成如下的效果：&lt;/p&gt;

&lt;pre class="brush:csharp"&gt;static void Main(string[] args)
{ 
    // comment
    var i = 1;
    var s
}&lt;/pre&gt;

&lt;p&gt;然而，Syntax Highlighter的强大之处，在于它的&lt;a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter:Configuration"&gt;可配置性&lt;/a&gt;。例如您可以如此来折叠代码，并高亮其第1和第3行：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: maroon"&gt;pre &lt;/span&gt;&lt;span style="color: red"&gt;class&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;brush:csharp;collapse:true;highlight:[1,3]&amp;quot;&amp;gt;&lt;/span&gt;
static void Main(string[] args)
{ 
    // comment
    var i = 1;
    var s
}
&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: maroon"&gt;pre&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;效果如下（不过博客园的样式似乎不太好看）：&lt;/p&gt;

&lt;pre class="brush:csharp;collapse:true;highlight:[1,3]"&gt;static void Main(string[] args)
{ 
    // comment
    var i = 1;
    var s
}&lt;/pre&gt;

&lt;p&gt;其他更多配置还所有“轻量模式（没有右上角的浮动）”，“起始行号”等等——至少我看了之后就直接震精了。&lt;/p&gt;

&lt;p&gt;客户端着色的最大优势，便是在于良好的分离。话说我们为什么要提倡XHTML + CSS的网页编写方式呢？其关键也就是“分离”：“数据”和“样式”的分离。客户端着色的方式，由服务器提供最纯粹的数据，没有任何修饰的数据，而在客户端通过JavaScript来进行“改写”——就好比用CSS来在客户端把平淡的XHTML变得丰富多彩一样。&lt;/p&gt;

&lt;p&gt;既然分离，灵活性就高了。例如Syntax Highlighter也提供了一些“代码着色”以外的部分。因为可以使用JavaScript来操作页面，于是我们可以在页面上放置任意灵活的DOM结构。此外，如Syntax Highlighter是将HTML着色成带有不同class（如keyword或comment）的&amp;lt;code&amp;gt;标记。于是，我们可以通过不同的CSS定义来表示不同的着色方式。这样即便需要有所改变（如博客版式变化），那么好，我们重新提供一套着色样式即可。&lt;/p&gt;

&lt;p&gt;不过客户端着色也有缺点。首先，您可能已经观察到了，如果使用Syntax Highlighter进行着色，在内容显示的时候是非常平淡的，这一切直到页面加载完毕后才改变，这是因为Syntax Highlighter在windows的load事件中才能进行着色。如果页面加载比较慢（如我的博客），可能就会等待较长时间才能看到着色效果。还有一个缺点可能不太被人关注：如果有人通过RSS等方式订阅了您的文章，那么它便无法获得着色效果了。&lt;/p&gt;

&lt;p&gt;如果您只是一个普通用户，那么对于这两个缺点只能选择默默承受。不过，如果您是这个系统的开发人员，那么理论上还有的救，毕竟事在人为么。例如对于第一个缺点，我们可以在每个代码块下方插入一段用于“立即着色”的脚本，这样便可以避免着色的延迟。而第二个缺点，也可以在RSS内容输出之时，为文章里的代码块在服务器端进行着色。&lt;/p&gt;

&lt;p&gt;于是这又带来了新的考量：如果系统使用wiki标记或BBCode等方式来保存文章内容，那么便要修改其转化逻辑。如果系统直接保存的是HTML代码，那么我们必须制定一个规则，让服务器端逻辑可以从文章的内容中识别出代码块，这样才能插入“立即着色”的代码，或是将其在服务器端进行着色。那么，您是每次输出时进行着色，还是在系统中直接保存修改好的内容？还有……&lt;/p&gt;

&lt;p&gt;可见，“精益求精”也是有代价的。投入产出比是否值得，只有您自己可以作出判断了。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/12/syntax-highlighting-client-side.html#comments</comments>
      <pubDate>Mon, 14 Dec 2009 11:53:00 GMT</pubDate>
      <lastBuildDate>Mon, 14 Dec 2009 11:53:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>在项目中使用Google Closure Compiler</title>
      <link>http://blog.zhaojie.me/2009/12/ikvm-google-closure-compiler.html</link>
      <guid>http://blog.zhaojie.me/2009/12/ikvm-google-closure-compiler.html</guid>
      <description>&lt;p&gt;现在的Web项目总是离不开大量JavaScript，而JS文件的体积也越来越大，也越来越影响页面的感知性能（Perceived Performance）。因此，我们会对JS文件进行压缩，一方面是使用Gzip，而另一方面则是去除JS文件里的注释、空白，并且压缩局部变量长度等等。对于一些成熟的类库来说，它们本身都会提供“完整注释”以及“强烈压缩”两个版本。但是，有时候我们需要&lt;a href="http://blog.zhaojie.me/2009/12/jquery-validate-remote-bug.html"&gt;自己修复类库里的bug&lt;/a&gt;，这只能在注释版中修改，对于压缩版自然就无能为力了。此外，自定义的脚本文件一般也值得一压。因此我在项目中时常会备一个脚本压缩工具。&lt;/p&gt;  &lt;p&gt;压缩脚本的工具有很多，例如老牌的&lt;a href="http://www.crockford.com/javascript/jsmin.html"&gt;JSMin&lt;/a&gt;，或是&lt;a href="http://developer.yahoo.com/yui/compressor/"&gt;YUI Compressor&lt;/a&gt;（下称YC），它们都可以用来压缩脚本文件（后者还可以处理CSS）。不过在新项目中，我使用了新的工具：&lt;a href="http://code.google.com/closure/compiler/"&gt;Google Closure Compiler&lt;/a&gt;（下称GC）。GC有多种用法，例如&lt;a href="http://closure-compiler.appspot.com/home"&gt;网页版&lt;/a&gt;，&lt;a href="http://code.google.com/closure/compiler/docs/gettingstarted_api.html"&gt;网络API版&lt;/a&gt;，还有&lt;a href="http://code.google.com/closure/compiler/docs/gettingstarted_app.html"&gt;独立应用程序版&lt;/a&gt;。GC与YC不同的是，YC是一个压缩器（Compressor），而GC更是一个编译器（Compiler），也就是说GC的压缩并不仅仅是去除注释和空白，还可以在保证代码正确性的情况下进一步地改写成更省空间的做法，一个字节算一个字节，例如：&lt;/p&gt;  &lt;pre class="code"&gt;a = new Object    =&amp;gt; a = {}
a = new Array     =&amp;gt; a = []
if (a) b()        =&amp;gt; a &amp;amp;&amp;amp; b()
return 2 * 3;     =&amp;gt; return 6;&lt;/pre&gt;

&lt;p&gt;GC还提供了一些更危险的压缩方式，虽然有神奇效果，但个人不建议使用。关于YC和GC更详细的对比及注意事项，可以参考&lt;a href="http://ued.taobao.com/"&gt;淘宝UED&lt;/a&gt;部门&lt;a href="http://lifesinger.org/blog/"&gt;lifesinger&lt;/a&gt;所制作的无比精彩的幻灯片：&lt;/p&gt;

&lt;div style="text-align: left; width: 425px" id="__ss_2462617"&gt;&lt;a style="margin: 12px 0px 3px; display: block; font: 14px helvetica,arial,sans-serif; text-decoration: underline" title="Closure Compiler vs YUICompressor" href="http://www.slideshare.net/lifesinger/closure-compiler-vs-yuicompressor"&gt;Closure Compiler vs YUICompressor&lt;/a&gt;&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=closure-compiler-vs-yui-compressor-091109210742-phpapp02&amp;amp;stripped_title=closure-compiler-vs-yuicompressor" /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always" /&gt;&lt;embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=closure-compiler-vs-yui-compressor-091109210742-phpapp02&amp;amp;stripped_title=closure-compiler-vs-yuicompressor" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;

  &lt;div style="font-family: tahoma,arial; height: 26px; font-size: 11px; padding-top: 2px"&gt;View more &lt;a style="text-decoration: underline" href="http://www.slideshare.net/"&gt;documents&lt;/a&gt; from &lt;a style="text-decoration: underline" href="http://www.slideshare.net/lifesinger"&gt;lifesinger&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;GC使用Java编写（YC也一样，不过它有&lt;a href="http://www.codeplex.com/YUICompressor"&gt;.NET移植版&lt;/a&gt;），它的独立应用程序版是一个jar包。如果您不想装一个Java Runtime的话，则可以使用它的网络API版。但是，我在项目中却使用了另一种方式：即不需要安装JRE，也不需要依赖于网络。这个方式便是借助&lt;a href="http://www.ikvm.net/"&gt;IKVM.NET&lt;/a&gt;将GC转化为.NET使用——还记得&lt;a href="http://blog.zhaojie.me/2009/10/ikvm-simple-lucene-benchmark.html"&gt;上次的Lucene 2.9吗&lt;/a&gt;？&lt;/p&gt;

&lt;p&gt;我的项目组织结构大致是：&lt;/p&gt;

&lt;pre class="code"&gt;\src\Web.UI\Scripts\                   &amp;lt;- 存放JS脚本的目录
\tools\IKVM.NET\                       &amp;lt;- 存放IKVM.NET相关文件
\tools\closure-compiler\compiler.jar   &amp;lt;- GC的jar包
\tools\closure-compiler\build.bat      &amp;lt;- 将jar转化为exe的脚本
\tools\closure-compiler\compress.ps    &amp;lt;- 压缩JS的PowerShell命令&lt;/pre&gt;

&lt;p&gt;以上便是所有放入SVN中的文件，每个开发人员在使用GC之前，首先需要调用build.bat进行“重新编译”：&lt;/p&gt;

&lt;pre class="code"&gt;..\ikvm.net\ikvmc.exe compiler.jar -out:compiler.exe -target:exe
xcopy ..\ikvm.net\*.dll . /y /q&lt;/pre&gt;

&lt;p&gt;这两行脚本会将compiler.jar包编译为compiler.exe文件，并将IKVM.NET中需要的文件复制到closure-compiler目录下。于是，借助PowerShell的管道，便可以压缩Scripts目录下所有的JS文件：&lt;/p&gt;

&lt;pre class="code"&gt;dir -path ..\..\src\Web.UI\Scripts -filter *.js | % { .\compiler.exe --js $_.FullName --js_output_file ($_.FullName -replace &amp;quot;.js&amp;quot;, &amp;quot;.min.js&amp;quot;) }&lt;/pre&gt;

&lt;p&gt;这样，xxx.js便会被压缩为xxx.min.js。于是再配合项目中的扩展方法：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public static string &lt;/span&gt;Script(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;HtmlHelper &lt;/span&gt;helper, &lt;span style="color: blue"&gt;string &lt;/span&gt;path)
{
    &lt;span style="color: blue"&gt;return &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;&amp;lt;script language=\&amp;quot;javascript\&amp;quot; type=\&amp;quot;text/javascript\&amp;quot; src=\&amp;quot;&amp;quot; &lt;/span&gt;+ path +
        (helper.ViewContext.HttpContext.IsDebuggingEnabled ? &lt;span style="color: #a31515"&gt;&amp;quot;.min.js&amp;quot; &lt;/span&gt;: &lt;span style="color: #a31515"&gt;&amp;quot;.js&amp;quot;&lt;/span&gt;) +
        &lt;span style="color: #a31515"&gt;&amp;quot;\&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;quot;&lt;/span&gt;;
}&lt;/pre&gt;

&lt;p&gt;万事俱备。&lt;/p&gt;

&lt;p&gt;有些朋友时不时会羡慕其他平台上项目丰富，而在.NET平台上做一些事情感觉捉襟见肘的。我以前经常说，又何必把平台划分的那么细，大家既然都在为技术社区作贡献，那么思想或是做法都是可以借鉴的，所以也已经有了那么多移植过来的项目。而现在，可以“借鉴”的已经不只是“思想”，而是真正实际的项目！我相信在不久的将来，随着IronPython和IronRuby等项目愈发成熟，可以在.NET上运行的东西会越来越多（事实上，如IronPython其实已经很成熟了）。&lt;/p&gt;

&lt;p&gt;嗯，到时候，Python的就是.NET的，Ruby的也是.NET的，而Java的——它还是.NET的。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/12/ikvm-google-closure-compiler.html#comments</comments>
      <pubDate>Wed, 09 Dec 2009 01:13:00 GMT</pubDate>
      <lastBuildDate>Wed, 09 Dec 2009 01:13:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/asp-net/">ASP.NET</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <title>真有必要去除HTML中的空白字符吗？</title>
      <link>http://blog.zhaojie.me/2009/12/is-it-really-necessary-to-strip-white-space-in-html.html</link>
      <guid>http://blog.zhaojie.me/2009/12/is-it-really-necessary-to-strip-white-space-in-html.html</guid>
      <description>&lt;p&gt;刚才有朋友在MSN上问我说，他的页面中有许多空白字符，打开源文件一看发现这代码稀疏得很。他觉得很浪费，他说有什么办法可以去除它们。我问他“你的页面使用GZip压缩了吗？”他说用了，于是我回答说“那么就不用去除空白字符了，连续空白字符压缩得很好，去掉后效果不大的”。这时我又不禁想到早上那篇《&lt;a href="http://www.cnblogs.com/cmt/archive/2009/12/07/1618301.html"&gt;博客园首页优化心得&lt;/a&gt;》中也有一条是“去除HTML中的空格、空行”，于是我便打算尝试一下，去除空白字符到底有多少效果。&lt;/p&gt;  &lt;p&gt;我的实验目标是我博客的前40篇文章的详细页，未压缩前的体积从98K到277K不等，我想也应该算是博客园中比较典型的页面大小吧。我使用这样的测试代码：&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;static void &lt;/span&gt;Main()
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;files = &lt;span style="color: #2b91af"&gt;Directory&lt;/span&gt;.GetFiles(&lt;span style="color: #a31515"&gt;&amp;#64;&amp;quot;d:\pages&amp;quot;&lt;/span&gt;);
    &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;f &lt;span style="color: blue"&gt;in &lt;/span&gt;files) Compare(&lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.ReadAllText(f));

    &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;&amp;quot;Press enter to exit...&amp;quot;&lt;/span&gt;);
    &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.ReadLine();
}

&lt;span style="color: blue"&gt;private static readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Regex &lt;/span&gt;REGEX_LINE_BREAKS = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Regex&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;#64;&amp;quot;\n\s*&amp;quot;&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;RegexOptions&lt;/span&gt;.Compiled);
&lt;span style="color: blue"&gt;private static readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Regex &lt;/span&gt;REGEX_LINE_SPACE = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Regex&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;#64;&amp;quot;\n\s*\r&amp;quot;&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;RegexOptions&lt;/span&gt;.Compiled);
&lt;span style="color: blue"&gt;private static readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Regex &lt;/span&gt;REGEX_SPACE = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Regex&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;#64;&amp;quot;( )+&amp;quot;&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;RegexOptions&lt;/span&gt;.Compiled);

&lt;span style="color: blue"&gt;private static void &lt;/span&gt;Compare(&lt;span style="color: blue"&gt;string &lt;/span&gt;html)
{
    &lt;span style="color: blue"&gt;long &lt;/span&gt;original, compressedOriginal;
    GetLength(html, &lt;span style="color: blue"&gt;out &lt;/span&gt;original, &lt;span style="color: blue"&gt;out &lt;/span&gt;compressedOriginal);

    &lt;span style="color: blue"&gt;long &lt;/span&gt;stripped, compressedStripped;
    html = REGEX_LINE_BREAKS.Replace(html, &lt;span style="color: #a31515"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);
    html = REGEX_LINE_SPACE.Replace(html, &lt;span style="color: #a31515"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);
    html = REGEX_SPACE.Replace(html, &lt;span style="color: #a31515"&gt;&amp;quot; &amp;quot;&lt;/span&gt;);
    GetLength(html, &lt;span style="color: blue"&gt;out &lt;/span&gt;stripped, &lt;span style="color: blue"&gt;out &lt;/span&gt;compressedStripped);

    &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;&amp;quot;{0}\t{1}\t{2}\t{3}\t{4}\t{5}&amp;quot;&lt;/span&gt;,
        original, compressedOriginal,
        stripped, compressedStripped,
        original - stripped, compressedOriginal - compressedStripped);
}

&lt;span style="color: blue"&gt;private static void &lt;/span&gt;GetLength(&lt;span style="color: blue"&gt;string &lt;/span&gt;html, &lt;span style="color: blue"&gt;out long &lt;/span&gt;original, &lt;span style="color: blue"&gt;out long &lt;/span&gt;compressed)
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;bytes = &lt;span style="color: #2b91af"&gt;Encoding&lt;/span&gt;.UTF8.GetBytes(html);
    &lt;span style="color: blue"&gt;var &lt;/span&gt;stream = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MemoryStream&lt;/span&gt;();
    &lt;span style="color: blue"&gt;var &lt;/span&gt;gzipStream = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;GZipStream&lt;/span&gt;(stream, &lt;span style="color: #2b91af"&gt;CompressionMode&lt;/span&gt;.Compress);
    gzipStream.Write(bytes, 0, bytes.Length);
    gzipStream.Flush();

    original = bytes.LongLength;
    compressed = stream.Length;
}&lt;/pre&gt;

&lt;p&gt;在上面这段代码里我使用的是博客园去空白字符的方法，结果如下：&lt;/p&gt;

&lt;table style="text-align: center" cellpadding="5" border="1" cellspacing="0"&gt;&lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;原页面&lt;/th&gt;

      &lt;th&gt;原页面（压缩）&lt;/th&gt;

      &lt;th&gt;去空白后&lt;/th&gt;

      &lt;th&gt;去空白后（压缩）&lt;/th&gt;

      &lt;th&gt;去空白前后&lt;/th&gt;

      &lt;th&gt;去空白前后（压缩）&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;130760&lt;/td&gt;

      &lt;td&gt;36018&lt;/td&gt;

      &lt;td&gt;117354&lt;/td&gt;

      &lt;td&gt;34702&lt;/td&gt;

      &lt;td&gt;13406&lt;/td&gt;

      &lt;td&gt;1316&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;255935&lt;/td&gt;

      &lt;td&gt;63406&lt;/td&gt;

      &lt;td&gt;240433&lt;/td&gt;

      &lt;td&gt;61870&lt;/td&gt;

      &lt;td&gt;15502&lt;/td&gt;

      &lt;td&gt;1536&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;278871&lt;/td&gt;

      &lt;td&gt;86794&lt;/td&gt;

      &lt;td&gt;263704&lt;/td&gt;

      &lt;td&gt;85298&lt;/td&gt;

      &lt;td&gt;15167&lt;/td&gt;

      &lt;td&gt;1496&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;221248&lt;/td&gt;

      &lt;td&gt;53148&lt;/td&gt;

      &lt;td&gt;205440&lt;/td&gt;

      &lt;td&gt;51548&lt;/td&gt;

      &lt;td&gt;15808&lt;/td&gt;

      &lt;td&gt;1600&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;151612&lt;/td&gt;

      &lt;td&gt;40260&lt;/td&gt;

      &lt;td&gt;137939&lt;/td&gt;

      &lt;td&gt;38940&lt;/td&gt;

      &lt;td&gt;13673&lt;/td&gt;

      &lt;td&gt;1320&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;135019&lt;/td&gt;

      &lt;td&gt;36000&lt;/td&gt;

      &lt;td&gt;121593&lt;/td&gt;

      &lt;td&gt;34750&lt;/td&gt;

      &lt;td&gt;13426&lt;/td&gt;

      &lt;td&gt;1250&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;128239&lt;/td&gt;

      &lt;td&gt;36230&lt;/td&gt;

      &lt;td&gt;114658&lt;/td&gt;

      &lt;td&gt;34878&lt;/td&gt;

      &lt;td&gt;13581&lt;/td&gt;

      &lt;td&gt;1352&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;161530&lt;/td&gt;

      &lt;td&gt;42776&lt;/td&gt;

      &lt;td&gt;147189&lt;/td&gt;

      &lt;td&gt;41392&lt;/td&gt;

      &lt;td&gt;14341&lt;/td&gt;

      &lt;td&gt;1384&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;99884&lt;/td&gt;

      &lt;td&gt;28372&lt;/td&gt;

      &lt;td&gt;87047&lt;/td&gt;

      &lt;td&gt;27084&lt;/td&gt;

      &lt;td&gt;12837&lt;/td&gt;

      &lt;td&gt;1288&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;173534&lt;/td&gt;

      &lt;td&gt;43724&lt;/td&gt;

      &lt;td&gt;158446&lt;/td&gt;

      &lt;td&gt;42272&lt;/td&gt;

      &lt;td&gt;15088&lt;/td&gt;

      &lt;td&gt;1452&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;191519&lt;/td&gt;

      &lt;td&gt;50398&lt;/td&gt;

      &lt;td&gt;176958&lt;/td&gt;

      &lt;td&gt;48888&lt;/td&gt;

      &lt;td&gt;14561&lt;/td&gt;

      &lt;td&gt;1510&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;176996&lt;/td&gt;

      &lt;td&gt;40274&lt;/td&gt;

      &lt;td&gt;162706&lt;/td&gt;

      &lt;td&gt;38978&lt;/td&gt;

      &lt;td&gt;14290&lt;/td&gt;

      &lt;td&gt;1296&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;206348&lt;/td&gt;

      &lt;td&gt;47362&lt;/td&gt;

      &lt;td&gt;191400&lt;/td&gt;

      &lt;td&gt;45964&lt;/td&gt;

      &lt;td&gt;14948&lt;/td&gt;

      &lt;td&gt;1398&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;137014&lt;/td&gt;

      &lt;td&gt;38608&lt;/td&gt;

      &lt;td&gt;122855&lt;/td&gt;

      &lt;td&gt;37076&lt;/td&gt;

      &lt;td&gt;14159&lt;/td&gt;

      &lt;td&gt;1532&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;144715&lt;/td&gt;

      &lt;td&gt;37260&lt;/td&gt;

      &lt;td&gt;131097&lt;/td&gt;

      &lt;td&gt;35748&lt;/td&gt;

      &lt;td&gt;13618&lt;/td&gt;

      &lt;td&gt;1512&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;146531&lt;/td&gt;

      &lt;td&gt;36704&lt;/td&gt;

      &lt;td&gt;132619&lt;/td&gt;

      &lt;td&gt;35302&lt;/td&gt;

      &lt;td&gt;13912&lt;/td&gt;

      &lt;td&gt;1402&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;199915&lt;/td&gt;

      &lt;td&gt;49224&lt;/td&gt;

      &lt;td&gt;182227&lt;/td&gt;

      &lt;td&gt;47452&lt;/td&gt;

      &lt;td&gt;17688&lt;/td&gt;

      &lt;td&gt;1772&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;106929&lt;/td&gt;

      &lt;td&gt;29850&lt;/td&gt;

      &lt;td&gt;93690&lt;/td&gt;

      &lt;td&gt;28518&lt;/td&gt;

      &lt;td&gt;13239&lt;/td&gt;

      &lt;td&gt;1332&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;136264&lt;/td&gt;

      &lt;td&gt;36664&lt;/td&gt;

      &lt;td&gt;121548&lt;/td&gt;

      &lt;td&gt;34990&lt;/td&gt;

      &lt;td&gt;14716&lt;/td&gt;

      &lt;td&gt;1674&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;148750&lt;/td&gt;

      &lt;td&gt;37990&lt;/td&gt;

      &lt;td&gt;134567&lt;/td&gt;

      &lt;td&gt;36578&lt;/td&gt;

      &lt;td&gt;14183&lt;/td&gt;

      &lt;td&gt;1412&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;282886&lt;/td&gt;

      &lt;td&gt;71924&lt;/td&gt;

      &lt;td&gt;266336&lt;/td&gt;

      &lt;td&gt;70306&lt;/td&gt;

      &lt;td&gt;16550&lt;/td&gt;

      &lt;td&gt;1618&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;176099&lt;/td&gt;

      &lt;td&gt;41468&lt;/td&gt;

      &lt;td&gt;161322&lt;/td&gt;

      &lt;td&gt;40126&lt;/td&gt;

      &lt;td&gt;14777&lt;/td&gt;

      &lt;td&gt;1342&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;108394&lt;/td&gt;

      &lt;td&gt;30456&lt;/td&gt;

      &lt;td&gt;95428&lt;/td&gt;

      &lt;td&gt;29216&lt;/td&gt;

      &lt;td&gt;12966&lt;/td&gt;

      &lt;td&gt;1240&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;152578&lt;/td&gt;

      &lt;td&gt;40186&lt;/td&gt;

      &lt;td&gt;138543&lt;/td&gt;

      &lt;td&gt;38866&lt;/td&gt;

      &lt;td&gt;14035&lt;/td&gt;

      &lt;td&gt;1320&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;230243&lt;/td&gt;

      &lt;td&gt;59970&lt;/td&gt;

      &lt;td&gt;215389&lt;/td&gt;

      &lt;td&gt;58554&lt;/td&gt;

      &lt;td&gt;14854&lt;/td&gt;

      &lt;td&gt;1416&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;251183&lt;/td&gt;

      &lt;td&gt;57156&lt;/td&gt;

      &lt;td&gt;234862&lt;/td&gt;

      &lt;td&gt;55694&lt;/td&gt;

      &lt;td&gt;16321&lt;/td&gt;

      &lt;td&gt;1462&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;196957&lt;/td&gt;

      &lt;td&gt;48176&lt;/td&gt;

      &lt;td&gt;181608&lt;/td&gt;

      &lt;td&gt;46776&lt;/td&gt;

      &lt;td&gt;15349&lt;/td&gt;

      &lt;td&gt;1400&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;172267&lt;/td&gt;

      &lt;td&gt;41340&lt;/td&gt;

      &lt;td&gt;158105&lt;/td&gt;

      &lt;td&gt;40056&lt;/td&gt;

      &lt;td&gt;14162&lt;/td&gt;

      &lt;td&gt;1284&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;265877&lt;/td&gt;

      &lt;td&gt;63650&lt;/td&gt;

      &lt;td&gt;248974&lt;/td&gt;

      &lt;td&gt;62142&lt;/td&gt;

      &lt;td&gt;16903&lt;/td&gt;

      &lt;td&gt;1508&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;147403&lt;/td&gt;

      &lt;td&gt;38894&lt;/td&gt;

      &lt;td&gt;133751&lt;/td&gt;

      &lt;td&gt;37492&lt;/td&gt;

      &lt;td&gt;13652&lt;/td&gt;

      &lt;td&gt;1402&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;149091&lt;/td&gt;

      &lt;td&gt;36460&lt;/td&gt;

      &lt;td&gt;134998&lt;/td&gt;

      &lt;td&gt;35190&lt;/td&gt;

      &lt;td&gt;14093&lt;/td&gt;

      &lt;td&gt;1270&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;167741&lt;/td&gt;

      &lt;td&gt;43200&lt;/td&gt;

      &lt;td&gt;153614&lt;/td&gt;

      &lt;td&gt;41856&lt;/td&gt;

      &lt;td&gt;14127&lt;/td&gt;

      &lt;td&gt;1344&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;171564&lt;/td&gt;

      &lt;td&gt;40898&lt;/td&gt;

      &lt;td&gt;157333&lt;/td&gt;

      &lt;td&gt;39648&lt;/td&gt;

      &lt;td&gt;14231&lt;/td&gt;

      &lt;td&gt;1250&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;125812&lt;/td&gt;

      &lt;td&gt;34570&lt;/td&gt;

      &lt;td&gt;111047&lt;/td&gt;

      &lt;td&gt;33200&lt;/td&gt;

      &lt;td&gt;14765&lt;/td&gt;

      &lt;td&gt;1370&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;190649&lt;/td&gt;

      &lt;td&gt;46524&lt;/td&gt;

      &lt;td&gt;175197&lt;/td&gt;

      &lt;td&gt;45040&lt;/td&gt;

      &lt;td&gt;15452&lt;/td&gt;

      &lt;td&gt;1484&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;153807&lt;/td&gt;

      &lt;td&gt;39462&lt;/td&gt;

      &lt;td&gt;139401&lt;/td&gt;

      &lt;td&gt;38054&lt;/td&gt;

      &lt;td&gt;14406&lt;/td&gt;

      &lt;td&gt;1408&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;120788&lt;/td&gt;

      &lt;td&gt;32228&lt;/td&gt;

      &lt;td&gt;107534&lt;/td&gt;

      &lt;td&gt;30930&lt;/td&gt;

      &lt;td&gt;13254&lt;/td&gt;

      &lt;td&gt;1298&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;163327&lt;/td&gt;

      &lt;td&gt;41110&lt;/td&gt;

      &lt;td&gt;148763&lt;/td&gt;

      &lt;td&gt;39710&lt;/td&gt;

      &lt;td&gt;14564&lt;/td&gt;

      &lt;td&gt;1400&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;103101&lt;/td&gt;

      &lt;td&gt;29476&lt;/td&gt;

      &lt;td&gt;90284&lt;/td&gt;

      &lt;td&gt;28222&lt;/td&gt;

      &lt;td&gt;12817&lt;/td&gt;

      &lt;td&gt;1254&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td&gt;141384&lt;/td&gt;

      &lt;td&gt;39784&lt;/td&gt;

      &lt;td&gt;126641&lt;/td&gt;

      &lt;td&gt;38350&lt;/td&gt;

      &lt;td&gt;14743&lt;/td&gt;

      &lt;td&gt;1434&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;值得关注的是最后两列，从中我们可以发现，虽然去除空白前后可以让页面体积减小十几K，但是经过压缩后其实只相差1-2K了——大约1-2个数据包。这些节省是否值得我们去这么做？再者，博客园的做法是为每个页面的内容使用正则表达式进行替换，那么它所带来开销又是否值得呢？这就要博客园自己进行profiling了……&lt;/p&gt;

&lt;p&gt;最后一提，其实去空白字符并非如此简单一件事情。最简单的例子是：您是否遇到某些HTML编辑器或RSS阅读器，它们会让文章中原本工整的代码变成一行？这就是因为它们武断地去除了所有的空白字符，但是忘了有个HTML标记叫做&amp;lt;pre /&amp;gt;……&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/12/is-it-really-necessary-to-strip-white-space-in-html.html#comments</comments>
      <pubDate>Mon, 07 Dec 2009 09:59:00 GMT</pubDate>
      <lastBuildDate>Mon, 07 Dec 2009 09:59:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/asp-net/">ASP.NET</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <title>jQuery Validation插件remote验证方式的Bug</title>
      <link>http://blog.zhaojie.me/2009/12/jquery-validate-remote-bug.html</link>
      <guid>http://blog.zhaojie.me/2009/12/jquery-validate-remote-bug.html</guid>
      <description>&lt;p&gt;jQuery插件很多，其中一个重要的插件便是&lt;a href="http://plugins.jquery.com/project/validate"&gt;jQuery Validation&lt;/a&gt;，它的作用是对表单进行验证，还上了jQuery官网。不过奇怪的是，最近用下来感觉有些古怪，因为好像有些死板，已有功能的应变能力还不强，甚至还有个奇怪的Bug。任何项目有Bug其实也正常，但这个Bug其实是一个文档上已经记载了，却没有实现的功能，这就有些说不过去了。这个问题便出在remote验证方式上，还好修改起来非常容易，在此记录一下，也方便以后的参考。&lt;/p&gt;  &lt;p&gt;在表单验证时，有时候会需要发一个AJAX请求去服务器上进行判断，例如在用户注册时检查用户名是否存在。jQuery Validation插件提供了一种remote方式来实现这一点。例如我可以这样验证表单：&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;form &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;regForm&amp;quot;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;input &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;name&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;userName&amp;quot; /&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;form&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;

&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;script &lt;/span&gt;&lt;span style="color: red"&gt;language&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;javascript&amp;quot;&amp;gt;
    &lt;/span&gt;$(&lt;span style="color: #a31515"&gt;'#regForm'&lt;/span&gt;).validate({
        &lt;span style="color: #a31515"&gt;'rules'&lt;/span&gt;: {
            &lt;span style="color: #a31515"&gt;'userName'&lt;/span&gt;: {
                &lt;span style="color: #a31515"&gt;'required'&lt;/span&gt;: &lt;span style="color: blue"&gt;true&lt;/span&gt;,
                &lt;span style="color: #a31515"&gt;'remote'&lt;/span&gt;: &lt;span style="color: #a31515"&gt;'/account/verify'
            &lt;/span&gt;}});
&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;script&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;这样，jQuery Validation便会请求“/account/verify?userName=jeffz”这样的URL来获取true/false。可惜的是，我们在使用ASP.NET MVC时，往往会将input的name写为特定的形式，目的是利用DefaultModelBinder的强大绑定功能。例如：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;form &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;regForm&amp;quot;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;input &lt;/span&gt;&lt;span style="color: red"&gt;type&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;text&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;userName&amp;quot; &lt;/span&gt;&lt;span style="color: red"&gt;name&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;user.Name&amp;quot; /&amp;gt;
&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;form&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;与此同时，我们用来进行验证的Action方法，它的参数名可能也有所不同：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ActionResult &lt;/span&gt;Verify(&lt;span style="color: blue"&gt;string &lt;/span&gt;name) { ... }&lt;/pre&gt;

&lt;p&gt;根据&lt;a href="http://docs.jquery.com/Plugins/Validation/Methods/remote#options"&gt;文档描述&lt;/a&gt;，此时我们应该这样写：&lt;/p&gt;

&lt;pre class="code"&gt;$(&lt;span style="color: #a31515"&gt;'#regForm'&lt;/span&gt;).validate({
    &lt;span style="color: #a31515"&gt;'rules'&lt;/span&gt;: {
        &lt;span style="color: #a31515"&gt;'user.Name'&lt;/span&gt;: {
            &lt;span style="color: #a31515"&gt;'remote'&lt;/span&gt;: {
                url: &lt;span style="color: #a31515"&gt;'/account/verify'&lt;/span&gt;,
                data: {
                    name: &lt;span style="color: blue"&gt;function&lt;/span&gt;() { &lt;span style="color: blue"&gt;return &lt;/span&gt;$(&lt;span style="color: #a31515"&gt;&amp;quot;#userName&amp;quot;&lt;/span&gt;).val(); }
                }}}}});&lt;/pre&gt;

&lt;p&gt;可是，从实际效果来看，jQuery还是在请求“/account/verify?user.Name=jeffz”，百思不得其解。确认在三之后只得求助于jquery.validation.js源码，看后差点晕过去：&lt;/p&gt;

&lt;pre class="code"&gt;remote: &lt;span style="color: blue"&gt;function&lt;/span&gt;(value, element, param) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;( &lt;span style="color: blue"&gt;this&lt;/span&gt;.optional(element) )
        &lt;span style="color: blue"&gt;return &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;dependency-mismatch&amp;quot;&lt;/span&gt;;
    
    ...
    
    param = &lt;span style="color: blue"&gt;typeof &lt;/span&gt;param == &lt;span style="color: #a31515"&gt;&amp;quot;string&amp;quot; &lt;/span&gt;&amp;amp;&amp;amp; {url:param} || param; 
    
    &lt;span style="color: blue"&gt;if &lt;/span&gt;( previous.old !== value ) {
        previous.old = value;
        &lt;span style="color: blue"&gt;var &lt;/span&gt;validator = &lt;span style="color: blue"&gt;this&lt;/span&gt;;
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.startRequest(element);
        &lt;span style="color: blue"&gt;var &lt;/span&gt;data = {}; 
        data[element.name] = value; &lt;span style="color: green"&gt;// data还是以element.name为准？
        &lt;/span&gt;$.ajax($.extend(&lt;span style="color: blue"&gt;true&lt;/span&gt;, {
            url: param,&lt;span style="color: green"&gt;
            &lt;/span&gt;mode: &lt;span style="color: #a31515"&gt;&amp;quot;abort&amp;quot;&lt;/span&gt;,
            port: &lt;span style="color: #a31515"&gt;&amp;quot;validate&amp;quot; &lt;/span&gt;+ element.name,
            dataType: &lt;span style="color: #a31515"&gt;&amp;quot;json&amp;quot;&lt;/span&gt;,
            data: data,
            success: &lt;span style="color: blue"&gt;function&lt;/span&gt;(response) {
                ...&lt;/pre&gt;

&lt;p&gt;我很奇怪，不知道为什么会这样做，这样根本没有起到指定参数名的作用。那么，改吧：&lt;/p&gt;

&lt;pre class="code"&gt;remote: &lt;span style="color: blue"&gt;function&lt;/span&gt;(value, element, param) {
    &lt;span style="color: blue"&gt;if &lt;/span&gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;.optional(element))
        &lt;span style="color: blue"&gt;return &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot;dependency-mismatch&amp;quot;&lt;/span&gt;;

    ...

    param = &lt;span style="color: blue"&gt;typeof &lt;/span&gt; param == &lt;span style="color: #a31515"&gt;&amp;quot;string&amp;quot;&lt;/span&gt; &amp;amp;&amp;amp; {url:param} || param;

    &lt;span style="color: blue"&gt;if &lt;/span&gt;(previous.old !== value) {
        previous.old = value;
        &lt;span style="color: blue"&gt;var &lt;/span&gt;validator = &lt;span style="color: blue"&gt;this&lt;/span&gt;;
        &lt;span style="color: blue"&gt;this&lt;/span&gt;.startRequest(element);
        &lt;span style="color: blue"&gt;var &lt;/span&gt;data = {};
        data[element.name] = value;
        $.ajax($.extend(&lt;span style="color: blue"&gt;true&lt;/span&gt;, {
            &lt;span style="color: green"&gt;// url: param,
            &lt;/span&gt;url: param.url,
            mode: &lt;span style="color: #a31515"&gt;&amp;quot;abort&amp;quot;&lt;/span&gt;,
            port: &lt;span style="color: #a31515"&gt;&amp;quot;validate&amp;quot; &lt;/span&gt;+ element.name,
            dataType: &lt;span style="color: #a31515"&gt;&amp;quot;json&amp;quot;&lt;/span&gt;,
            &lt;span style="color: green"&gt;// data: data,
            &lt;/span&gt;data: param.data || data,
            success: &lt;span style="color: blue"&gt;function&lt;/span&gt;(response) {
                ...&lt;/pre&gt;

&lt;p&gt;修改两处即可，问题就此解决。只可惜，jquery.validate.min.js类似的文件只能自己进行压缩了。&lt;/p&gt;

&lt;p&gt;居然会出现这样的问题，实在令人费解。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/12/jquery-validate-remote-bug.html#comments</comments>
      <pubDate>Fri, 04 Dec 2009 06:29:00 GMT</pubDate>
      <lastBuildDate>Fri, 04 Dec 2009 06:29:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <title>如何对一个博客系统进行CSS管理？</title>
      <link>http://blog.zhaojie.me/2009/11/style-management-for-blog-system.html</link>
      <guid>http://blog.zhaojie.me/2009/11/style-management-for-blog-system.html</guid>
      <description>&lt;p&gt;忽然发现，我的博客皮肤又改变了，这次是About栏： &lt;/p&gt; &lt;img src="http://img.zhaojie.me/blog/168980/o_bad-about.jpg" /&gt;   &lt;p&gt;如果您和我博客右侧的内容相比，会发现行间距首行文字略有下沉，且段落之间的间距变小了，导致“希望”一段跑到了照片的右侧，进而照片与下方文字的间距也加大了。&lt;/p&gt;  &lt;p&gt;我在博客版式上投入了很大的精力，动用了大量的JavaScript和自定义CSS，希望可以让浏览效果更加“和谐”。可是，博客园在样式管理上似乎经常会出现一些问题，时不时让一些基础的样式修改破坏了版式。对于大部分朋友来说，可能不很在乎一个版式的缩进、行间距是否对齐等等。但是对我来说，一些细节上的破坏就会让我觉得有些不舒服。那么这次到底又发生了什么呢？幸好我在TechEd的示例中保存了一周前的CSS文件，于是再用Fiddler抓取新的CSS文件后使用WinDiff进行对比，终于发现罪魁祸首是common2.css文件。&lt;/p&gt;  &lt;p&gt;在一周前的common2.css文件中，有个定义是：&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: #a31515"&gt;p 
&lt;/span&gt;{
    &lt;span style="color: red"&gt;margin&lt;/span&gt;: &lt;span style="color: blue"&gt;0 auto 10px auto&lt;/span&gt;;
    &lt;span style="color: red"&gt;text-indent&lt;/span&gt;:&lt;span style="color: blue"&gt;0px&lt;/span&gt;;
}&lt;/pre&gt;

&lt;p&gt;而在现在的common2.css文件中，这个定义变成了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #a31515"&gt;p 
&lt;/span&gt;{
    &lt;span style="color: red"&gt;margin&lt;/span&gt;: &lt;span style="color: blue"&gt;5px auto 5px auto&lt;/span&gt;;
    &lt;span style="color: red"&gt;text-indent&lt;/span&gt;:&lt;span style="color: blue"&gt;0px&lt;/span&gt;;
}&lt;/pre&gt;

&lt;p&gt;既然知道了原因，那么我只要加一行自定义样式便把效果修补回来了：&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: #a31515"&gt;div#about_body p &lt;/span&gt;{ &lt;span style="color: red"&gt;margin&lt;/span&gt;: &lt;span style="color: blue"&gt;0 auto 10px auto&lt;/span&gt;; }&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;那么，为什么这样的事情总是一而再，再而三地发生呢？这无法不让我多想一些。似乎这次的问题是由两方面原因造成的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;博客园修改了过于宽泛的选择器：&lt;/strong&gt;p是个非常基础的CSS选择器，涉及到整个页面的p元素，对他进行修改就好比改变了类库中的基本类型。对于这样的修改一定要小心，因为影响过于巨大，试想如果.NET修改了Object类的行为那么会造成什么后果呢？&lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;皮肤的样式直接依赖了最基础的样式：&lt;/strong&gt;如果说上一点是主要因素，那么这一点就是次要因素。由于about区域的文字直接依赖于基本上“毫无关联”的基础样式，导致了一些难以预料的事情发生。因此，最终的解决方案也是使用了限制更优先的选择器来覆盖基础样式。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;多皮肤的博客样式管理是一件麻烦的事情，它涉及了继承，覆盖等多种因素。我对于CSS的使用不是非常了解，也没有干过如此有技术要求的项目。园子里的CSS专家不少，不妨趁此机会来讨论一下该如何为这样的“多主题”系统设计CSS的管理方式呢？&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/11/style-management-for-blog-system.html#comments</comments>
      <pubDate>Tue, 10 Nov 2009 11:56:00 GMT</pubDate>
      <lastBuildDate>Tue, 10 Nov 2009 11:56:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/asp-net/">ASP.NET</category>
      <title>关于网页*静态化*及SEO问题的一些补充</title>
      <link>http://blog.zhaojie.me/2009/07/more-on-page-statilization-and-seo.html</link>
      <guid>http://blog.zhaojie.me/2009/07/more-on-page-statilization-and-seo.html</guid>
      <description>&lt;p&gt;&lt;a href="http://blog.zhaojie.me/2009/07/talk-about-page-statilization.html"&gt;前一篇讨论“静态页”的文章&lt;/a&gt;反响不错，不少朋友发表了自己的看法，也给老赵更多的想法。虽然也在前一篇文章后面回复了不少内容，但是就以往经验来看，总结为一篇新的文章会让我想表达的内容更为明确，对于“静态化”这一非常容易被人误解的概念来说也是非常重要的。&lt;/p&gt; &lt;a href="http://img.zhaojie.me/blog/168980/o_confused-spider.jpg" title="seo" target="_blank"&gt;&lt;img class="floatRight" alt="seo" src="http://img.zhaojie.me/blog/168980/o_confused-spider.jpg" width="200" /&gt;&lt;/a&gt;&lt;p&gt;我们还是先来讨论一下，什么叫做“静态页”。有朋友说，放在硬盘上的htm或html文件便是一种静态页，Web服务器不需要做额外的处理，直接读取文件内容并输出就可以了，而这样的静态文件对于SEO是有帮助的。至于理由，是搜索引擎会对html结尾的文件给更好的权值（这好像还是结论，不是理由），而这是“常识”，“了解一点SEO的人都知道这个”，“人们普遍在使用的做法”，因此“它一定是正确的”。不过其实&lt;a href="http://googlechinablog.com/2008/10/blog-post_20.html"&gt;Google并不这么认为&lt;/a&gt;，百度倒没有给出专业说法。&lt;/p&gt; &lt;p&gt;当然，我们已经重复强调，但还是需要不断明确的一点是，即使搜索引擎对于“静态页”有更好的倾向性，那也是因为其“URL样式”，而不是“在硬盘上放置了一个html文件”。请求方（也就是爬虫）只是向服务器端发送一个URL，并获取服务器端给出的内容。它不会关心，也无法了解服务器端究竟是如何得到页面内容的，对于客户端来说，世界上没有“静态”或“动态”页面之分。有些朋友可能还是会说“不会啊，html就是静态页面，像aspx之类的就是动态页面，前者不需要在Web服务器上运算，后者需要”。&lt;/p&gt; &lt;p&gt;真是这样的吗？并非如此，因为html文件也是需要Web服务器来运算的。例如，您请求一个html文件，Web服务器至少做了几件事情：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;如果请求包含缓存信息，那么处理缓存状态。  &lt;li&gt;根据URL定位到磁盘上的文件。  &lt;li&gt;进行用户认证和授权（如，是否匿名？）。  &lt;li&gt;判断是否有权限读取。  &lt;li&gt;读取文件。  &lt;li&gt;根据文件类型设置MIME的值。  &lt;li&gt;根据文件最后修改日期设置Last-Modified值。  &lt;li&gt;根据文件内容及其他状态设置其E-Tag值。  &lt;li&gt;如果文件内部有include标记，那么读取另一个文件填充进来。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;看看，处理一个文件需要多少“动态运算”啊，这些可都是在Web服务器（如IIS）加载一个html所做的事情。如果您想要观察这些过程，可以阅读一些Web服务器的源代码，或者去观察一下ASP.NET中System.Web.StaticFileHandler类所做的事情，它也体现了Web服务器处理html时的关键之处。事实上，如果您在IIS中将html配置给ASP.NET ISAPI的话，或者使用VS自带的Web服务器，最后便是由StaticFileHandler来输出硬盘上的文件的。&lt;/p&gt; &lt;p&gt;所以，虽然我们看起来Web服务器只是简单地读取了硬盘上的文件，但其实它还是不如我们想象的那么简单。不过对于客户端来说，这一切都是不可知的。例如Squid，Nginx这样部署在前端的缓存或反向代理服务器，它们都不会关心后端Web服务器是Windows，Linux还是Unix，也不会关心是IIS，Apache，Lightted甚至是我们自己写的高效或低劣的Web服务器。对于浏览器，爬虫，或前端负载均衡器来说，它们只知道TCP/IP协议，它们只知道HTTP协议等东西，其他一概不知。&lt;/p&gt; &lt;p&gt;不过，也有朋友坚持认为“生成静态页”来“进行页面缓存”对SEO有帮助。理由是，“进行页面缓存”能够提高网站性能，爬虫更倾向于访问速度更快的页面。从这个角度看来，这种说法的确有一定道理。只是我还是不喜欢这样的看法，因为这种说法没有把握事物关键。在这里，SEO的关键在于优化网站性能，而生成静态页只是一种手段之一。这并不是适用性最广的，也并非是最容易实现的。如果您直接把“生成静态页”与“SEO”联系起来，很有可能会对他人造成误解。&lt;/p&gt; &lt;p&gt;当然，如果您的思路没有问题，“静态页”三个字的指代也足够明确，“静态页有利于SEO”这个命题毫无疑问是正确的。不过我们现在并没有讨论一个命题的逻辑是否正确，我们也不必纠缠于一个表达形式是否严谨，我们的目的是要说明道理。也正因为如此，老赵才会一遍一遍地写这么多内容。也就是说，这几篇文章的关键在于“说清道理”，我们把握它既可。&lt;/p&gt; &lt;p&gt;最后，老赵再谈一下对SEO这个工作的看法。&lt;/p&gt; &lt;a href="http://img.zhaojie.me/blog/168980/o_googateer.gif" title="seo" target="_blank"&gt;&lt;img class="floatRight" alt="seo" src="http://img.zhaojie.me/blog/168980/o_googateer.gif" width="200" /&gt;&lt;/a&gt;&lt;p&gt;从老赵与各SEO人员的接触感觉来看，他们总是有各种理由来说明“问题所在”，只是如果在改进问题之后还是没有效果的话，他们又可以找出各种理由来告诉你为什么没有效果——但是要知道SEO是一个实践性工作，它的唯一判断依据便是“效果”，而不是“理论”。SEO的理论很容易掌握，但是如果无法真切提高一个网站在搜索引擎上的表现，这一切还是白搭。老赵认为，一个好的SEO是需要了解网页制作，或者说网站开发的基本技术的，至少要有常识，否则基本上就是在扯蛋。老赵曾经接触过一个“专业”的SEO公司，那里的“SEO咨询师”给我留下了深刻的印象——负面印象。其“非专业性”从以下几个事件中便可见一斑：&lt;/p&gt; &lt;ol&gt; &lt;li&gt;还是“静态页”的问题。由于把URL变为.html结尾之后并没有得到明显的效果，他询问我们的实现方式。在得知我们使用了URL重写，而不是在硬盘上放置html文件时他“惊呼”这种欺骗搜索引擎的行为是会起到反效果的。他强烈要求我们在硬盘上放置html文件。这个要求自然遭到了我们的拒绝，原因之一是我们是非常动态的网站，很难实现这个需求，但是更重要的是，懂得一点技术的人就知道，Web服务器的处理方式对于搜索引擎爬虫时完全不可见的，我们是否真正放置html文件与搜索引擎没有任何关系。  &lt;li&gt;内容的位置问题。在SEO界有种说法是，搜索引擎会更倾向于把页面靠前的内容看的更重，而把页面靠后的内容权值放低。因此那位专业SEO咨询师指着我们的某张页面说，这部分内容太靠“下方”，很容易被搜索引擎忽略。请注意，他说的是“内容在页面显示的时候出现在下方”。您觉得这种说法有道理吗？如今页面布局往往使用XHTML+CSS的方式，而搜索引擎只会关注HTML的内容，而“位置”很大程度上是由CSS，甚至是由JS来控制的。出现在HTML内容前段的内容，在页面呈现时也可以出现在下方，这也和搜索引擎没有任何关系。可惜这一点也解释了半天。  &lt;li&gt;最后一条可以说是最可笑的。因为SEO效果不好，那位SEO咨询师觉得只能“来真的”了，于是向我们索要网站的IIS日志。分析日志对于SEO有些帮助，因为可以看出爬虫的抓取顺序，频率，甚至结果等等，因此查看日志的做法本没有问题。可惜问题在于，对方从MSN上给出一个邮箱，让我们把过去几个星期的日志发给他。当看到这个要求的时候，老赵几乎要破口大骂。从这点可以看出，这位SEO咨询师缺少必要的常识，他根本不知道一个中小型的网站，每天便要生成几百兆到几个G的日志。如此没有常识，为什么会有那么多“成功案例”？&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;老赵的博客（也就是您正在看的这个）在搜索引擎上的表现也非常糟糕，即使是老赵经常写作的话题，在Google上也很难找到几篇文章，排名也不太靠前。如果不使用site:cnblogs.com进行限制的话，几乎没有一篇文章是找到我的blog，都是各种地方的转载。为此我也比较苦恼，咨询了一些专业搞SEO的朋友，做出一些修改之后还是没有太大改善。不过我相信那只是我没有遇上优秀的SEO人员而已，我的博客的潜力还远没有挖掘到底。&lt;/p&gt; &lt;p&gt;如果您是一个专业的SEO人员，或者是专业的SEO公司，不妨给我一些建议——如果可以的话，我也不介意在这方面进行一点投资。不过，如果是一些“肮脏”的优化方式就不必了，例如去论坛上贴链接，发垃圾邮件。我也知道这些做法很有效果，但是我不想这样做。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/07/more-on-page-statilization-and-seo.html#comments</comments>
      <pubDate>Sun, 05 Jul 2009 16:08:00 GMT</pubDate>
      <lastBuildDate>Sun, 05 Jul 2009 16:08:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/practice/">实践优化</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/asp-net/">ASP.NET</category>
      <title>谈*静态页*（或网页*静态化*）的时候，请区分一些概念</title>
      <link>http://blog.zhaojie.me/2009/07/talk-about-page-statilization.html</link>
      <guid>http://blog.zhaojie.me/2009/07/talk-about-page-statilization.html</guid>
      <description>&lt;p&gt;“静态页”，在Web应用程序开发中是很常见的概念。只是我发现目前还是有相当部分的朋友，在这方面的存在一定的误区。因此现在独立写一篇文章，也想把一些问题讲讲清楚，以后在讨论的时候也好有个准。&lt;/p&gt; &lt;p&gt;不久前有朋友写了一篇题为《&lt;a href="http://www.cnblogs.com/zhengxiqiang/archive/2009/07/04/1516720.html"&gt;提供生成静态页核心代码&lt;/a&gt;》的文章，介绍了一种“向硬盘写入页面文件”的方式。这篇文章的内容在此并不多作讨论，这里引用一下作者给出的摘要：&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;网页生成静态Html文件有许多好处，比如生成html网页有利于被搜索引擎收录，不仅被收录的快还收录的全。前台脱离了数据访问，减轻对数据库访问的压力，加快网页打开速度。&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;这种说法存在一个严重的问题，因为它混淆了两个概念：“静态页”有利于网站性能，和“静态页”有利于SEO。有朋友可能会说：“这两点说的都没有错啊，不信你去搜索引擎上查一下，都有很多资料”。是的，这两种说法都能在搜索引擎上找出“依据”来，只可惜在这种两种情况下的“静态页”所指的内容，或者说是“做法”完全不同，可以说没有任何关系。换句话说，这里造成“混淆”的原因是“指代不明”。为了方便阐述，在本文接下来的部分中将尽可能避免“静态页”，“静态化”等词语，而是使用以下两种区分明显的说法进行阐述：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;规范页面URL  &lt;li&gt;缓存页面内容&lt;/li&gt;&lt;/ul&gt; &lt;h1&gt;规范页面URL&lt;/h1&gt; &lt;p&gt;如今在开发的Web应用程序时，往往需要从客户端获取一些信息，然后根据这些信息生成页面。例如，我们需要从客户端获取一个“页码”，然后在页面上呈现出这一页的内容。从客户端传递信息的方式有多种，其中最常见的便是通过Query String进行传递。例如，我们可以通过Article.aspx?id=3这样的方式来请求id为3的文章。不过如果纯粹使用Query String来传递信息的话，一个URL可能会带有许多项Query String。例如ArticleList.aspx?page=3&amp;amp;keywords=helloworld&amp;amp;category=6&amp;amp;....。&lt;/p&gt; &lt;p&gt;有种说法是，这样的URL由于明显是动态的，因此搜索引擎对它的处理会有所负面倾斜，例如将其权值放低。因此，很多程序都会把为URL规范为特别的形式，例如Article/3，甚至是Article_3.html。使用htm或html作为URL的结尾，是为了“欺骗”搜索引擎，让搜索引擎以为这是一个直接从存储设备上直接读取的资源，它不会改变，因此“它的权值会相对提高”。实际上老赵并不同意这个说法，而且似乎也没有实际案例可以证明这一点——当然我也无法证否，因此无法判断这个说法的正确性。不过这篇文章并不是在追究这个问题，在这里我们暂且认为它有道理吧。&lt;/p&gt; &lt;p&gt;要实现这点，我们所要实现的是进行&lt;a href="http://blog.zhaojie.me/2008/01/url-rewrite-1.html"&gt;URL重写&lt;/a&gt;。URL重写的目的，是在服务器端把客户端请求的URL（如Article_3.html）当作另一个请求进行处理（如Article.aspx?id=3）。请注意，这个工作是在服务器端完成的：&lt;/p&gt; &lt;table border="0" cellspacing="0" cellpadding="5"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td style="padding-right: 20px; font-weight: bold; border-right: black 1px solid"&gt;客户端&lt;/td&gt; &lt;td style="border-left: black 1px solid; padding-left: 20px; font-weight: bold"&gt;服务器端&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td style="padding-right: 20px; border-right: black 1px solid"&gt;Article_3.html&lt;/td&gt; &lt;td style="border-left: black 1px solid; padding-left: 20px"&gt;Article_3.html =&amp;gt; Article.aspx?id=3 =&amp;gt; 处理 =&amp;gt; 输出&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;对于搜索引擎的爬虫来说，它根本意识不到这个URL是在直接读取资源，还是经过了动态的请求。我们是Web应用程序的编写者，对于一个请求我们可以使用我们任意的方式进行处理，想欺骗搜索引擎还不是易如反掌？不过这种做法对于网站性能来说是否有帮助？没有，肯定没有。&lt;/p&gt; &lt;p&gt;这种改变URL，想要获取更好SEO效果的做法，有些人也会把它叫做“伪静态化”。老赵不知道这种说法合不合适，我是从来不会使用这样的说法的。&lt;/p&gt; &lt;h1&gt;缓存页面内容&lt;/h1&gt; &lt;p&gt;动态生成一个页面的开销往往很大，例如需要多次查询数据库或者外部服务。为了减少服务器端的开销，为了加快网站的运行效率，有时候在服务器端会将一个页面的整体内容保存为一个文件，这样每次在服务器端获取客户端请求的时候，只要读取相应的文件即可，而不需要重新查询数据库或外部服务并重新生成页面内容：&lt;/p&gt; &lt;table border="0" cellspacing="0" cellpadding="5"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td style="padding-right: 20px; font-weight: bold; border-right: black 1px solid"&gt;客户端&lt;/td&gt; &lt;td style="border-left: black 1px solid; padding-left: 20px; font-weight: bold"&gt;服务器端&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td style="padding-right: 20px; border-right: black 1px solid"&gt;Article.aspx?id=3&lt;/td&gt; &lt;td style="border-left: black 1px solid; padding-left: 20px"&gt;Article.aspx?id=3 =&amp;gt; 读取文件 =&amp;gt; 输出&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;同样的，这些事情完全是在服务器端进行的处理，搜索引擎的爬虫对此一无所知。即使搜索引擎认为Article.aspx?id=3这样的请求是由服务器端即时生成的（当然搜索引擎真不会考虑这些），我们编写的服务器端逻辑同样可以直接读取磁盘上的文件，并且直接输出。这种做法自然是为了效率，不过……&lt;/p&gt; &lt;p&gt;这种做法和SEO有没有关系？没有任何关系，因为爬虫根本不知道我们做了这些。&lt;/p&gt; &lt;p&gt;这种做法是否需要在硬盘上生成一个html文件？没有必要，我可以生成txt文件，可以生成jeffz文件，甚至我可以不生成文件，而是将页面内容直接存放在内存中，甚至是高性能的Key/Value Store里。&lt;/p&gt; &lt;p&gt;这种做法是否需要把URL修改为html结尾？没有必要，URL改不改都无所谓，改成什么也都无所谓。&lt;/p&gt; &lt;h1&gt;总结&lt;/h1&gt; &lt;p&gt;有时候事情其实就是那么简单，但是还是会让人混淆。一句话听上去很正确，但是一旦“指代不明”，正确的话也变成错误的了。例如本文一开始引用的文章，它是为了“缓存页面内容”而使用的做法，这个做法和SEO没有任何关系，因此说“生成html网页有利于被搜索引擎收录，不仅被收录的快还收录的全”是将其目的与“规范页面URL”混淆了起来。错误产生在这里。在那片文章后面的评论中，有朋友回复说目前的搜索引擎已经不关心URL是否是html还是别的什么形式了。这种说法可能也是正确的，不过并没有谈在点子上。因为无论搜索引擎如何处理HTML，文章的内容都和搜索引擎没有一丝一缕关系。&lt;/p&gt; &lt;p&gt;因此，如果您以后要谈“静态页”或网页“静态化”的时候，请区分您究竟是在谈“规范页面URL”还是“缓存页面内容”。&lt;/p&gt; &lt;p&gt;如果您说“静态页有助于SEO”，明白人知道您是再指“规范页面URL”，而某些朋友可能就会认为您是指在服务器端缓存页面内容。&lt;/p&gt; &lt;p&gt;如果您说“静态页有助于提高网站性能”，明白人知道您是指“缓存页面内容”，而某些朋友可能就会认为您是指使用“URL重写”来规范URL样式。&lt;/p&gt;&lt;p&gt;如果您说“静态页，既有助于SEO，又有助于提高网站性能”，那么（我希望）明白人就会带您来看现在这篇文章，而某些朋友可能就会……哎哎。&lt;/p&gt;
&lt;h1&gt;补充说明&lt;/h1&gt;
&lt;p&gt;有朋友提到静态资源适合被CDN分发，其实不然。CDN难道不能分发动态请求生成的内容了吗？对于CDN来说，动态和静态是没有区别的。不说CDN，就说Squid吧，Squid知道后面连接的请求是静态还是动态的吗？是Windows系统还是Linux吗？其实这就是“分层”，抽象出来以后完全不知道后端的递交方式。而且换个角度想，世界上有“静态请求”这个东西吗？不都是需要经过Web服务器处理的吗？只不过，一个是复杂运算，一个是直接读取硬盘文件。对访问者来说，是看不出任何区别的。CDN分发的也只是“请求内容”而不会关心“内容的生成方式”。&lt;/p&gt;
&lt;p&gt;此外，有朋友给出了一份应该说“比较权威”的说明，各位不妨参考一下：&lt;a href="http://googlechinablog.com/2008/10/blog-post_20.html" alt="动态网址与静态网址"&gt;动态网址与静态网址&lt;/a&gt;&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/07/talk-about-page-statilization.html#comments</comments>
      <pubDate>Sat, 04 Jul 2009 17:37:00 GMT</pubDate>
      <lastBuildDate>Sat, 04 Jul 2009 17:37:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/news/">新闻信息</category>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <title>正在构建一个新的版式</title>
      <link>http://blog.zhaojie.me/2009/05/new-template-minyx2-lite.html</link>
      <guid>http://blog.zhaojie.me/2009/05/new-template-minyx2-lite.html</guid>
      <description>&lt;p&gt;老赵对目前博客园中的模板都不太满意：样式单调，元素不够丰富，不容易Hack，不够“标准”，不够充分利用屏幕横向距离等等。因此和dudu商量后，准备为博客园新增一个模板。新模板以&lt;a href="http://themepreview.amazingwordpressthemes.com/"&gt;Minyx 2.0 Lite&lt;/a&gt;为蓝本进行一些调整，如把固定宽度修改为右边固定，左边自适应的满屏宽度，还有把字体设为更适合中文的样式等等。经过几个小时努力，结果如下图：&lt;/p&gt; &lt;h3&gt;&lt;/h3&gt; &lt;p&gt;&lt;a href="http://img.zhaojie.me/blog/168980/o_detai.png"&gt;&lt;img alt="新版式" src="http://img.zhaojie.me/blog/168980/r_detai.png"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;这个版式可以保证在1024宽度下没有横向滚动条，最佳浏览宽度为1280至1650，基本上适合目前主流显示器分辨率。此外定义了各种标准元素，如各级标题，图片嵌入，代码样式，列表，引用等等。左侧为文章主体，可以让读者打开后直接阅读。右侧分一大栏和两小栏，充分利用屏幕宽度展示更多内容。&lt;/p&gt; &lt;p&gt;看过老赵blog的朋友一定发现老赵对于博客的修改还是较为投入的，例如调整模块顺序，集成“相关文章”和Twitter等内容，不过使用起来还是觉得不够顺手。因此这个版式更重要的特点便是易于Hack，可以有充足及优雅的方式让用户调整自己的博客。具体方式还在思考，最终老赵也会以“教程”的方式来介绍这个版式的使用方法。&lt;/p&gt; &lt;p&gt;最后便是要移植到博客园的版式上了，希望会顺利——老赵对Minyx 2.0 Lite进行了较大幅度的修改，精简了许多HTML与样式，集成起来应该没有问题。&lt;/p&gt;</description>
      <comments>http://blog.zhaojie.me/2009/05/new-template-minyx2-lite.html#comments</comments>
      <pubDate>Wed, 20 May 2009 12:55:00 GMT</pubDate>
      <lastBuildDate>Wed, 20 May 2009 12:55:00 GMT</lastBuildDate>
    </item>
    <item>
      <author>jeffz@live.com (老赵)</author>
      <category domain="http://blog.zhaojie.me/front-end/">前端表现</category>
      <category domain="http://blog.zhaojie.me/asp-net/">ASP.NET</category>
      <category domain="http://blog.zhaojie.me/dotnet/">.Net框架</category>
      <title>觉得有必要来澄清几组重要概念</title>
      <link>http://blog.zhaojie.me/2008/06/ajax-linq-lambda-expression.html</link>
      <guid>http://blog.zhaojie.me/2008/06/ajax-linq-lambda-expression.html</guid>
      <description>&lt;p&gt;承蒙大家厚爱，不少朋友都会把遇到的问题向老赵进行讯问。不过在阅读很多朋友问题的过程中，以及平时和别人讨论中，亦或是园子里的文章中经常发现一些误用概念的情况。如果在概念上没有形成共识，那么在工作和交流上就会造成许多问题。因此，虽然老赵已经在其他一些地方（例如WebCast或一些文章的评论）做过解释，但是现在仍然觉得有必要特地来澄清一些概念，解释一下这些概念之间的区别和联系。希望在明确这些概念之后，大家能够把注意力集中在对于具体问题的分析解决上，而不要让沟通成为瓶颈。&lt;/p&gt; &lt;p&gt;在这篇文章中，我希望澄清三组概念，它们是：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;AJAX / AJAX框架 / AJAX.NET (Professional) / ASP.NET AJAX  &lt;li&gt;LINQ / LINQ to SQL / LINQ to XXX  &lt;li&gt;Lambda Expression / Expression Tree / 匿名方法&lt;/li&gt;&lt;/ul&gt;&lt;h1&gt;AJAX / AJAX框架 / AJAX.NET (Professional) / ASP.NET AJAX&lt;/h1&gt; &lt;p&gt;这是我见过的混淆最多的一组概念。不过当去年AJAX技术如火如荼，AJAX框架层出不穷，而微软又推出ASP.NET AJAX框架之后，这四个概念之间的混淆也愈发严重起来了。我经常能看到这样的问题：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;我在用AJAX技术时不知道怎么在UpdatePanel里……  &lt;li&gt;我用AJAX.NET框架调用Web Service时……&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;这是非常典型的概念混淆，上面两句话的AJAX与AJAX.NET都应该使用ASP.NET AJAX替换。关于这几个概念的区别我《深入浅出ASP.NET AJAX》系列WebCast多次进行解释，不过收效似乎并不明显。于是我打算在这里再进行一下说明：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;AJAX&lt;/strong&gt;是Asynchronous JavaScript and XML缩写。这个概念代表的是一种技术，当您在说“我在项目中使用了AJAX技术时”，只是代表了您使用客户端XMLHttpRequest对象与服务器端进行异步通信。不过因为随着AJAX技术的运用往往会带来丰富的客户端效果，因此对AJAX技术的广义理解也可以认为这是一种操作客户端DOM而带来丰富效果的技术（这个“广义”的描述其实并不准确，大家明白老赵的意思就可以了）。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;AJAX框架&lt;/strong&gt;是一套辅助开发人员使用AJAX技术的代码包（库，etc.）。一个AJAX框架的目的一般是对XMLHttpRequest对象的使用进行封装，并提供一些操作DOM元素或者实现特殊效果的“捷径”。成熟的AJAX框架有很多，它们可以被分为“以客户端为中心”和“面向特定服务器技术”两大类。前者的优秀代表有（排名不分先后）：&lt;a href="http://www.prototypejs.org/"&gt;Prototype&lt;/a&gt;、&lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;、&lt;a href="http://mootools.net/"&gt;Mootools&lt;/a&gt;、&lt;a href="http://developer.yahoo.com/yui/"&gt;YUI&lt;/a&gt;、&lt;a href="http://dojotoolkit.org/"&gt;Dojo&lt;/a&gt;等等（最后两个其实更接近一套客户端界面库，也就是“广义”的AJAX框架）；而后者的典型代表既是AJAX.NET和ASP.NET AJAX了——当然，也有优秀的AJAX框架面向其他服务器技术，在此不提。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;AJAX.NET (Professional)&lt;/strong&gt;是ASP.NET平台上著名的AJAX框架，诞生于2005年2月（虽然正式命名为AJAX.NET则是在两个月之后），作者为&lt;a href="http://weblogs.asp.net/mschwarz/archive/2008/01/04/future-of-ajax-net-professional.aspx"&gt;Michael Schwarz&lt;/a&gt;。同年10月，Michael为AJAX.NET提供了更丰富的功能（主要是安全性方面），并将其改名为AJAX.NET Professional。AJAX.NET提供的最主要的（也是唯一）的功能就是异步调用服务器端方法，可谓非常纯粹的“基于数据”的AJAX使用方式。这个框架是一个个人作品，有支持.NET 1.1和2.0的版本，它并不属于微软官方，目前已经停止更新。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;ASP.NET AJAX&lt;/strong&gt;的Code Name为“Atlas”，在CTP向Beta版转移时曾经发生过翻天覆地的变化。ASP.NET AJAX中包含了UpdatePanel等控件，可以非常透明地为现有的ASP.NET WebForms应用程序添加AJAX效果。此外还提供了客户端异步调用Web Services的方法，使开发人员也能够使用面向数据的方式使用AJAX技术。值得一提的是ASP.NET AJAX的“附属品”相当丰富。例如ASP.NET AJAX名为“Microsoft AJAX Library”的客户端部分是一个纯客户端AJAX框架，提供了面向对象类型系统、浏览器兼容层、异步通信层等多种基础组件；ASP.NET AJAX的开源扩展包“AJAX Control Toolkit”包含了数十个可以直接使用的AJAX服务器端控件，这样开发人员能够轻松地添加丰富的客户端效果。同时，官方还为ASP.NET AJAX提供了“非正式”地扩展包，其中的History等优秀控件也将加入未来版本的ASP.NET AJAX框架中。ASP.NET AJAX是官方出品的AJAX框架，目前已经被集成到ASP.NET 3.5中去了，因此其版本号也从ASP.NET AJAX 1.0一下子“跃升为”ASP.NET AJAX 3.5。如果您看到了这些版本号也请不要疑惑，其实ASP.NET AJAX 3.5相对于ASP.NET AJAX 1.0来说只是修补了一些细小bug，几乎没有任何变化。&lt;/p&gt; &lt;h1&gt;LINQ / LINQ to SQL / LINQ to XXX&lt;/h1&gt; &lt;p&gt;LINQ是新生事物，不过从不少文章和讨论上看来，这方面的概念也已经有点混沌不清了。因此我们经常可以看到这样的话：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;LINQ只能将数据表与实体属性一一对应……  &lt;li&gt;LINQ开发指南：在LINQ中进行数据库字段映射……&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;以上两句话其实说的都是LINQ to SQL而不是指LINQ。可能由于LINQ to SQL的上镜率最广（连MSDN上About LINQ的第一个示例就是查询数据库的），因此许多人都将LINQ to SQL与LINQ混用，这会给初学者造成误解，认为LINQ就是LINQ to SQL，LINQ to SQL就是LINQ——事实当然不是这样的。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;LINQ&lt;/strong&gt;是Language-Integrated Query的缩写，是C# 3.0和VB 9.0中新加入的语言特性，可以在编程时使用内置的查询语言进行基于集合的操作。这么做可以大大简化开发过程，提高开发效率。例如：&lt;/p&gt; &lt;pre class="dirty_html code"&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;User&lt;/span&gt;&amp;gt; userList = GetUserList();&lt;/span&gt;&lt;br&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt;&lt;span&gt; userWithOddId = &lt;span style="color: blue"&gt;from&lt;/span&gt; u &lt;span style="color: blue"&gt;in&lt;/span&gt; userList&lt;/span&gt;&lt;br&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue"&gt;where&lt;/span&gt; u.UserID % 2 == 1&lt;/span&gt;&lt;br&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue"&gt;select&lt;/span&gt; u;&lt;/span&gt;&lt;br&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style="color: blue"&gt;foreach&lt;/span&gt;&lt;span&gt; (&lt;span style="color: #2b91af"&gt;User&lt;/span&gt; u &lt;span style="color: blue"&gt;in&lt;/span&gt; userWithOddId)&lt;/span&gt;&lt;br&gt;&lt;span&gt;{&lt;/span&gt;&lt;br&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(u.UserName);&lt;/span&gt;&lt;br&gt;&lt;span&gt;}&lt;/span&gt;&lt;/pre&gt; &lt;p&gt;如果没有LINQ，要筛选出ID为奇数的User对象则需要创建一个List，然后遍历整个列表，将符合特定条件的User对象放入新列表。而有了LINQ，这部分的筛选就变得非常容易，甚至只需要一句话就能完成。如果觉得这个例子不够说明LINQ对生产力有重大贡献的话，请关注我接下来的一篇文章（暂定名为《我们为什么要拥抱LINQ》）。LINQ特指形如上面这段代码中from...where...select这样的用法，其返回值是IQueryable&amp;lt;T&amp;gt;或IEnumerable&amp;lt;T&amp;gt;。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;LINQ to SQL&lt;/strong&gt;是.NET 3.5内置的一个轻量级O/R Mapping解决方案，可以将数据表映射为实体对象，方便开发人员对数据库的操作。可见，LINQ to SQL实只是LINQ的一个实现，提供了一个可以查询SQL Server数据库的LINQ Provider。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;LINQ Provider&lt;/strong&gt;是LINQ查询的执行器，标准LINQ语法支持许多的操作符，但是某个具体的LINQ实现可能只支持其中的一部分。在.NET 3.5默认提供了三种LINQ Provider，分别是LINQ to Object（即上面的例子），LINQ to SQL以及LINQ to XML。&lt;/p&gt; &lt;p&gt;LINQ to XXX表示使用LINQ针对XXX这种数据进行查询的解决方案。我们可以自定义LINQ Provider，使用我们自定义的查询规则来处理特定数据集。目前互联网上已经可以找到数十种LINQ Provider（如LINQ to Flickr，LINQ to NHibernate等），而已经处于beta 3阶段的ADO.NET Entity Framework，最终也会提供一个LINQ Provider，叫做“LINQ to Entities”。&lt;/p&gt;&lt;h1&gt;Lambda Expression / Expression Tree / 匿名方法&lt;/h1&gt; &lt;p&gt;&lt;strong&gt;Lambda Expression&lt;/strong&gt;从定义上讲是指带有“=&amp;gt;”符号的表达式，例如：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;x =&amp;gt; x + 1  &lt;li&gt;(x, y) =&amp;gt; x &amp;gt; y  &lt;li&gt;() =&amp;gt; 5  &lt;li&gt;(x, y) =&amp;gt; { return x &amp;gt; y; }&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Lambda Expreesion本身并不会在概念上引起混淆，不过由于在C# 3.0中Lambda Expression有两种截然不同的使用方式，有些朋友就会产生疑问，究竟Lambda Expression是做什么用的？&lt;/p&gt; &lt;p&gt;Lambda Expression的一个重要作用就是提供一种使用&lt;strong&gt;匿名方法&lt;/strong&gt;的新语法，在《&lt;a title="您善于使用匿名函数吗？" href="http://blog.zhaojie.me/2008/04/can-you-use-anonymous-method-properly.html"&gt;您善于使用匿名函数吗？&lt;/a&gt;》一文中您可以看到这种使用方式。利用Lambda Expression表示匿名函数的一个重要的缺点就是无法使用带out或ref关键字的参数，不过它比使用delegate关键字的表示法略为简单一点，因为无需提供参数类型，例如：&lt;/p&gt; &lt;pre class="dirty_html code"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&lt;span&gt; &lt;span style="color: blue"&gt;static&lt;/span&gt; &lt;span style="color: blue"&gt;bool&lt;/span&gt; CallMethod(&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;bool&lt;/span&gt;&amp;gt; method)&lt;/span&gt;&lt;br&gt;&lt;span&gt;{&lt;/span&gt;&lt;br&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: blue"&gt;return&lt;/span&gt; method(0);&lt;/span&gt;&lt;br&gt;&lt;span&gt;}&lt;/span&gt;&lt;br&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style="color: blue"&gt;static&lt;/span&gt;&lt;span&gt; &lt;span style="color: blue"&gt;void&lt;/span&gt; Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)&lt;/span&gt;&lt;br&gt;&lt;span&gt;{&lt;/span&gt;&lt;br&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;CallMethod(&lt;span style="color: blue"&gt;delegate&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt; a) { &lt;span style="color: blue"&gt;return&lt;/span&gt; &lt;span style="color: blue"&gt;false&lt;/span&gt;; });&lt;/span&gt;&lt;br&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;CallMethod(a =&amp;gt; { &lt;span style="color: blue"&gt;return&lt;/span&gt; &lt;span style="color: blue"&gt;false&lt;/span&gt;; });&lt;/span&gt;&lt;br&gt;&lt;span&gt;}&lt;/span&gt;&lt;/pre&gt; &lt;p&gt;可见，使用Lambda Expression表示的匿名方法无需指定参数类型，因为这一切都已经交给编译器来判断了。这一点在参数类型长而复杂的情况下（例如并行库中的方法）非常重要。因此现在要不是会涉及到out/ref参数，我都会使用Lambda Expression来表示匿名方法。&lt;/p&gt; &lt;p&gt;Lambda Expression的另一个作用自然就是构造一个LambdaExpression对象。任意一个Expression对象都表示了一个&lt;strong&gt;Expression Tree&lt;/strong&gt;的根节点，而开发人员可以通过解析这个Expression Tree来实现特定的功能。我们编写的方法可以接受一个Lambda Expression作为参数，但是我们还必须对这个参数的形式进行限制，这个参数才能有意义。这时候我们就会使用Expression&amp;lt;TDelegate&amp;gt;类型作为方法的参数类型，这样在使用这个方法时就必须使用满足TDelegate的签名及返回值的Lambda Expression才能编译通过。例如：&lt;/p&gt; &lt;pre class="dirty_html code"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&lt;span&gt; &lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;void&lt;/span&gt; CallMethod(&lt;span style="color: #2b91af"&gt;Expression&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Func&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;bool&lt;/span&gt;&amp;gt;&amp;gt; prediect) &lt;/span&gt;&lt;span&gt;{ ... &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;br&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style="color: blue"&gt;static&lt;/span&gt;&lt;span&gt; &lt;span style="color: blue"&gt;void&lt;/span&gt; Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)&lt;/span&gt;&lt;br&gt;&lt;span&gt;{&lt;/span&gt;&lt;br&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;CallMethod(a =&amp;gt; a &amp;gt; 0);&lt;/span&gt;&lt;br&gt;&lt;span&gt;}&lt;/span&gt;&lt;/pre&gt; &lt;p&gt;解析一个Expression Tree并不是一件简单的事情，一定程度上这相当于在进行编译工作，只是最终生成的结果不是机器码或IL，而是一个执行结果，并且语法解析的过程已经由C#编译器帮我们完成了。我在《&lt;a title="扩展LINQ to SQL：使用Lambda Expression批量删除数据" href="http://blog.zhaojie.me/2008/03/linq-to-sql-batch-delete-extension.html"&gt;扩展LINQ to SQL：使用Lambda Expression批量删除数据&lt;/a&gt;》一文中曾经提到过这一点，并且给出了一个实例，感兴趣的朋友们可以参考一下。&lt;/p&gt; &lt;p&gt;需要注意的是，如果在构造一个Expression对象时，Lambda Expression的Body部分不能是Statement；而在表示一个匿名对象时Lambda Expression的Body既可以是Expression也可以是Statement。无论是使用Expression还是Statement作为Body，只要表示的含义相同，编译器都会生成一样的匿名函数。例如以下两种写法其实是等价的：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;(x, y) =&amp;gt; x &amp;gt; y  &lt;li&gt;(x, y) =&amp;gt; { return (x &amp;gt; y); }&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;最后，我留给大家一个问题：以下两个做法的结果是相同的，而代码也非常接近。不过它们其实有着非常大的区别，您能指出吗？&lt;/p&gt; &lt;pre class="dirty_html code"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt;&amp;nbsp;&lt;span&gt;intList = &lt;span style="color: blue"&gt;new&lt;/span&gt; &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;() { 1, 2, 3, 4, 5 };&lt;/span&gt;&lt;br&gt;&lt;span style="color: blue"&gt;foreach&lt;/span&gt;&lt;span&gt; (&lt;span style="color: blue"&gt;int&lt;/span&gt; i &lt;span style="color: blue"&gt;in&lt;/span&gt; intList.Where(i =&amp;gt; i % 2 == 1))&lt;/span&gt;&lt;br&gt;&lt;span&gt;{&lt;/span&gt;&lt;br&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(i);&lt;/span&gt;&lt;br&gt;&lt;span&gt;}&lt;/span&gt;&lt;/pre&gt; &lt;pre class="dirty_html code"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt;&amp;nbsp;&lt;span&gt;intList = &lt;span style="color: blue"&gt;new&lt;/span&gt; &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;() { 1, 2, 3, 4, 5 }&lt;span style="color: red"&gt;.AsQueryable();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span style="color: blue"&gt;foreach&lt;/span&gt;&lt;span&gt; (&lt;span style="color: blue"&gt;int&lt;/span&gt; i &lt;span style="color: blue"&gt;in&lt;/span&gt; intList.Where(i =&amp;gt; i % 2 == 1))&lt;/span&gt;&lt;br&gt;&lt;span&gt;{&lt;/span&gt;&lt;br&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(i);&lt;/span&gt;&lt;br&gt;&lt;span&gt;}&lt;/span&gt;&lt;/pre&gt;</description>
      <comments>http://blog.zhaojie.me/2008/06/ajax-linq-lambda-expression.html#comments</comments>
      <pubDate>Tue, 03 Jun 2008 17:51:00 GMT</pubDate>
      <lastBuildDate>Tue, 03 Jun 2008 17:51:00 GMT</lastBuildDate>
    </item>
  </channel>
</rss>
