<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;CEYDRn88fSp7ImA9WxJVFk0.&quot;"><id>tag:blogger.com,1999:blog-7005036</id><updated>2009-07-03T14:22:57.175+08:00</updated><title>Cat in Chinese</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://chinese.catchen.biz/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>362</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><geo:lat>23.1</geo:lat><geo:long>113.2</geo:long><logo>http://static.flickr.com/95/buddyicons/45201686@N00.jpg?1161510808</logo><link rel="self" href="http://feeds.feedburner.com/CatChen/Chinese" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry gd:etag="W/&quot;Ck8NSX89cSp7ImA9WxJVFU8.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-8016471998223107850</id><published>2009-07-02T15:28:00.002+08:00</published><updated>2009-07-02T15:48:18.169+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-02T15:48:18.169+08:00</app:edited><title>让 JavaScript 轻松支持函数重载 (Part 2 - 实现)</title><content type="html">在&lt;a href="http://chinese.catchen.biz/2009/07/javascript-part-1.html"&gt;上一篇文章&lt;/a&gt;里，我们设计了一套能在JavaScript中描述函数重载的方法，这套方法依赖于一个叫做Overload的静态类，现在我们就来看看如何实现这个静态类。&lt;h4&gt;识别文本签名&lt;/h4&gt;我们先来回顾一下上一篇文章中提到的Overload用例：&lt;br /&gt;&lt;br /&gt;&lt;code&gt; var extend = Overload&lt;br /&gt;&amp;nbsp; .add("*, ...",&lt;br /&gt;&amp;nbsp; &amp;nbsp; function(target) { })&lt;br /&gt;&amp;nbsp; .add("Boolean, *, ...",&lt;br /&gt;&amp;nbsp; &amp;nbsp; function(deep, target) { });&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;我们允许用户输入一个字符串，表示某一个重载的签名。在用户调用函数时，我们需要拿着用户输入的参数实例去跟签名上的每一个参数类型作比较，因此我们需要先把这个字符串转换为类型数组。也就是说，字符串"Boolean, Number, Array"应该转换为数组[Boolean, Number, Array]。&lt;br /&gt;&lt;br /&gt;在进行转换之前，我们先要考虑处理两个特殊类型，就是代表任意类型的"*"，和代表任意数量的"..."。我们可以为它们定义两个专有的类型，以便在Overload内对它们做出特殊的兼容性处理：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Overload.Any = function() {};&lt;br /&gt;Overload.More = function() {};&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在有了这两个类型之后，字符串"Boolean, *, ..."就会被正确转换为数组[Boolean, Overload.Any, Overload.More]。由于Overload.Any和Overload.More都是函数，自然也都可以看做类型。&lt;br /&gt;&lt;br /&gt;在这两个类型得到正确处理后，我们就可以开始编写识别文本签名的转换函数了：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;if (signature.replace(/(^\s+|\s+$)/ig, "") == "") {&lt;br /&gt;&amp;nbsp; signature = [];&lt;br /&gt;} else {&lt;br /&gt;&amp;nbsp; signature = signature.split(",");&lt;br /&gt;&amp;nbsp; for (var i = 0; i &amp;lt; signature.length; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var typeExpression =&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; signature[i].replace(/(^\s+|\s+$)/ig, "");&lt;br /&gt;&amp;nbsp; &amp;nbsp; var type = null;&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (typeExpression == "*") {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; type = Overload.Any;&lt;br /&gt;&amp;nbsp; &amp;nbsp; } else if (typeExpression == "...") {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; type = Overload.More;&lt;br /&gt;&amp;nbsp; &amp;nbsp; } else {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; type = eval("(" + typeExpression + ")");&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; signature[i] = type;&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;我想这段代码相当容易理解，因此就不再解释了。我第一次写这段代码时忘记写上面的第一个if了，导致空白签名字符串""无法被正确识别为空白签名数组[]，幸好我的unit test代码第一时间发现了这个缺陷。看来编写unit test代码还是十分重要的。&lt;h4&gt;匹配函数签名&lt;/h4&gt;在我们得到函数签名的类型数组后，我们就可以用它和输入参数的实例数组做匹配了，以此找出正确的重载。在讨论具体如何匹配函数签名以前，我们先来看看&lt;a href="http://msdn.microsoft.com/en-us/library/aa691336%28VS.71%29.aspx"&gt;C#&lt;/a&gt;或&lt;a href="http://msdn.microsoft.com/en-us/library/tb18a48w.aspx"&gt;VB.NET&lt;/a&gt;这样的语言是如何处理函数重载匹配的。一般语言进行函数重载匹配的流程都是这样子的：&lt;ol&gt;&lt;li&gt;&lt;strong&gt;参数个数&lt;/strong&gt; - 参数个数不对的重载会被排除掉&lt;/li&gt;&lt;li&gt;&lt;strong&gt;参数类型&lt;/strong&gt; - 参数类型无法隐式转换为签名类型的会被排除掉&lt;/li&gt;&lt;li&gt;&lt;strong&gt;匹配个数&lt;/strong&gt; - 排除完毕后，剩下匹配的签名个数不同处理方法也不同&lt;ul&gt;&lt;li&gt;&lt;strong&gt;0个匹配&lt;/strong&gt; - 没有命中的匹配&lt;/li&gt;&lt;li&gt;&lt;strong&gt;1个匹配&lt;/strong&gt; - 这个就是命中的匹配&lt;/li&gt;&lt;li&gt;&lt;strong&gt;2个或以上的匹配&lt;/strong&gt; - 如果能在这些匹配中找出一个最佳匹配，那就命中最佳匹配；否则不命中任何匹配&lt;/li&gt;&lt;/ul&gt;&lt;/ol&gt;在这一节里面，我们先处理流程中的前两个步骤，把参数个数或参数类型不一致的签名去掉：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var matchSignature = function(argumentsArray, signature) {&lt;br /&gt;&amp;nbsp; if (argumentsArray.length &amp;lt; signature.length) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return false;&lt;br /&gt;&amp;nbsp; } else if (argumentsArray.length &gt; signature.length&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;&amp; !signature.more) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return false;&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; for (var i = 0; i &amp;lt; signature.length; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (!(signature[i] == Overload.Any&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; || argumentsArray[i] instanceof signature[i]&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; || argumentsArray[i].constructor&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; == signature[i])) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return false;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; return true;&lt;br /&gt;};&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;为了作长度对比，我们需要在这个函数外对表示任何参数个数的"..."作一下特殊处理：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;if (signature[signature.length - 1] == Overload.More) {&lt;br /&gt;&amp;nbsp; signature.length = signature.length - 1;&lt;br /&gt;&amp;nbsp; signature.more = true;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;这一段代码将会整合到第一节的转换函数末端，以便matchSignature函数能够轻易判断出参数与签名是否匹配。在最理想的情况下，我们对输入参数类型匹配到0个或1个重载，这样我们很容易就判断出命中哪个重载了。但如果有2个或以上的重载匹配，那么我们就要从中挑选一个最优的了，这正是下一节要讨论的内容。&lt;h4&gt;处理多重匹配&lt;/h4&gt;关于C#是如何从多重匹配中选出较为匹配的重载，可以看C# Language Specification中的&lt;a href="http://msdn.microsoft.com/en-us/library/aa691338%28VS.71%29.aspx"&gt;有关章节&lt;/a&gt;。我觉得通过三个简单的例子就能说明问题：&lt;br /&gt;&lt;br /&gt;&lt;code&gt; long Sum(int x, int y) { return x + y; }&lt;br /&gt; long Sum(long x, long y) { return x + y; }&lt;br /&gt;Sum(0, 1);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;由于0和1这两个参数会被编译器理解为int类型，对于第1个重载它们都不用进行类型转换，都与第2个重载它们都要进行类型转换，因此第1个重载较优。&lt;br /&gt;&lt;br /&gt;&lt;code&gt; long Sum(int x, long y) { return x + y; }&lt;br /&gt; long Sum(long x, int y) { return x + y; }&lt;br /&gt;Sum(0, 1);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在第1个参数上，第1个重载较优；在第2个参数上，第2个重载较优。在这种情况下，任何一个重载都不优于另一个，找不到较优重载编译器就报错。&lt;br /&gt;&lt;br /&gt;&lt;code&gt; long Sum(int x, int y) { return x + y; }&lt;br /&gt; long Sum(int x, long y) { return x + y; }&lt;br /&gt; long Sum(long x, int y) { return x + y; }&lt;br /&gt;Sum(0, 1);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在第1个参数上，第1个重载优于第3个重载，于第2个重载无异；在第2个参数上，第1个重载优于第2个重载，于第3个重载无异。尽管第2个重载于第3个重载分不出个优劣来，但我们可以确定第1个重载比它们都要好，因此编译器选择了第1个重载。&lt;br /&gt;&lt;br /&gt;假设我们有一个overloadComparator的比较函数，可以比较任意两个签名之间的优劣，我们需要对签名仅仅两两比较，以找出最优重载吗？事实上是不需要的，我们可以利用Array的sort方法，让它调用overloadComparator进行排序，排序后再验证前两名的关系就可以了——如果并列，则不命中任何一个；如果有先后之分，则命中第一个。&lt;br /&gt;&lt;br /&gt;具体的overloadComparator代码就不在这里给出了，它依赖于另一个名为inheritanceComparator的比较函数来对比两个签名的参数类型哪一个更贴实际传入的参数类型，里面用到了一种比较巧妙的方法来判断两个类型是否为继承关系，以及是谁继承自谁。&lt;h4&gt;小结&lt;/h4&gt;现在我们有了一个JavaScript的函数重载库，完整代码请看这里：&lt;a href="http://www.cnblogs.com/cathsfz/articles/1515479.html"&gt;函数重载库Overload&lt;/a&gt;。希望这个库能有效帮助大家提升JavaScript代码的可读性，降低大型Ajax项目的维护成本。如果大家希望将来继续读到类似的JavaScript开发模式相关的文章，不妨考虑订阅我的博客：&lt;ul&gt;&lt;li&gt;&lt;a href="http://chinese.catchen.biz/"&gt;Cat in Chinese&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dotnet.catchen.biz/"&gt;Cat in dotNET&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/dotNET"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-8016471998223107850?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/t6P6V2wN5HRkoUReukq2XTm1Nv8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/t6P6V2wN5HRkoUReukq2XTm1Nv8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/t6P6V2wN5HRkoUReukq2XTm1Nv8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/t6P6V2wN5HRkoUReukq2XTm1Nv8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=BEQ8BYearII:rcx4F6KDajM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=BEQ8BYearII:rcx4F6KDajM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=BEQ8BYearII:rcx4F6KDajM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=BEQ8BYearII:rcx4F6KDajM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=BEQ8BYearII:rcx4F6KDajM:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=BEQ8BYearII:rcx4F6KDajM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/BEQ8BYearII" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/8016471998223107850/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=8016471998223107850" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8016471998223107850?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8016471998223107850?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/BEQ8BYearII/javascript-part-2.html" title="让 JavaScript 轻松支持函数重载 (Part 2 - 实现)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/07/javascript-part-2.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQMRn05fyp7ImA9WxJVFEQ.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-7608353110740264513</id><published>2009-07-02T09:16:00.001+08:00</published><updated>2009-07-02T09:16:27.327+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-02T09:16:27.327+08:00</app:edited><title>让 JavaScript 轻松支持函数重载 (Part 1 - 设计)</title><content type="html">&lt;h4&gt;JavaScript支持重载吗？&lt;/h4&gt;JavaScript支持函数重载吗？可以说不支持，也可以说支持。说不支持，是因为JavaScript不能好像其它原生支持函数重载的语言一样，直接写多个同名函数，让编译器来判断某个调用对应的是哪一个重载。说支持，是因为JavaScript函数对参数列表不作任何限制，可以在函数内部模拟对函数重载的支持。&lt;br /&gt;&lt;br /&gt;实际上，在很多著名的开源库当中，我们都可以看到函数内部模拟重载支持的设计。例如说jQuery的&lt;a href="http://docs.jquery.com/Utilities/jQuery.extend"&gt;jQuery.extend&lt;/a&gt;方法，就是通过参数类型判断出可选参数是否存在，如果不存在的话就对参数进行移位以确保后面的逻辑正确运行。我相信很多人在写JavaScript时也写过类似的代码，以求为功能丰富的函数提供一个（或多个）简单的调用入口。&lt;br /&gt;&lt;br /&gt;不过做种做法一个根本的问题，那就是违反了&lt;a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt;原则。每个支持重载的函数内部都多出来一段代码，用于根据参数个数和参数类型处理重载，这些代码暗含着重复的逻辑，写出来却又每一段都不一样。此外，这些代码要维护起来也不容易，因为阅读代码时你并不能一眼看出函数支持的几种重载方式是什么，要对重载做出维护自然也困难。&lt;h4&gt;描述重载入口的DSL&lt;/h4&gt;我希望能够在JavaScript中以一种简单的方式来描述重载入口。最好就如同在其它语言中一样，使用函数签名来区分重载入口，因为我认为函数签名就是这方面最好的&lt;a href="http://en.wikipedia.org/wiki/Domain_specific_language"&gt;DSL&lt;/a&gt;。我假想中最符合JavaScript语法的重载入口描述DSL应该是这样子的：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var sum = new Overload();&lt;br /&gt;sum.add("Number, Number",&lt;br /&gt;&amp;nbsp; function(x, y) { return x + y; });&lt;br /&gt;sum.add("Number, Number, Number",&lt;br /&gt;&amp;nbsp; function(x, y, z) { return x + y + z; });&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在描述好重载入口与对应函数体后，对sum函数的调用应该是这样子的：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;sum(1, 2);&lt;br /&gt;sum(1, 2, 3);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;上述代码在我看来非常清晰，也非常容易维护——你可以一眼看得出重载入口的签名，并且要修改或者增加重载入口都是很容易的事情。但是我们遇到了一个问题，那就是JavaScript里面的函数是不能new出来的，通过new Overload()获得的对象一定不能被调用，为此我们只能把Overload做成一个静态类，静态方法返回的是Function实例：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var sum = Overload&lt;br /&gt;&amp;nbsp; .add("Number, Number",&lt;br /&gt;&amp;nbsp; &amp;nbsp; function(x, y) { return x + y; })&lt;br /&gt;&amp;nbsp; .add("Number, Number, Number",&lt;br /&gt;&amp;nbsp; &amp;nbsp; function(x, y, z) { return x + y + z; });&lt;/code&gt;&lt;h4&gt;必要的重载入口支持&lt;/h4&gt;想象一下，有哪些常见的JavaScript函数入口是用上述DSL无法描述的？我所知道的有两种：&lt;h5&gt;任意类型参数&lt;/h5&gt;假想我们要写一个each函数，对于Array就迭代它的下标，对于其它类型就迭代它的所有成员，这两个函数入口的参数列表如何声明？如果用C#，我们会如此描述两个函数入口：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;void Each(IEnumerable iterator) { }&lt;br /&gt;void Each(object iterator) { }&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;然而在JavaScript当中，Object不是一切类型的基类，(100) instanceof Object的结果为false，所以我们不能用Object来指代任意类型，必须引入一个新的符号来指代任意类型。考虑到这个符号不应该与任何可能存在的类名冲突，所以我选择了用"*"来表示任意类型。上述C#代码对应的JavaScript应该是这样子的：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var each = Overload&lt;br /&gt;&amp;nbsp; .add("Array",&lt;br /&gt;&amp;nbsp; &amp;nbsp; function(array) { })&lt;br /&gt;&amp;nbsp; .add("*",&lt;br /&gt;&amp;nbsp; &amp;nbsp; function(object) { });&lt;/code&gt;&lt;h5&gt;任意数量参数&lt;/h5&gt;在JavaScript的函数里面，要求支持任意数量参数是很常见的需求，相信使用率比C#里面的params关键字要多得多。在我们之前制定的规则当中，这也无法描述的，因此我们要引入一个不和类名冲突的符号来表示C#中的params。我选择了用"..."表示params，意思是这里出现任意多个参数都是可以接受的。让我们看看jQuery.extend的重载应该如何描述：&lt;br /&gt;&lt;br /&gt;&lt;code&gt; var extend = Overload&lt;br /&gt;&amp;nbsp; .add("*, ...",&lt;br /&gt;&amp;nbsp; &amp;nbsp; function(target) { })&lt;br /&gt;&amp;nbsp; .add("Boolean, *, ...",&lt;br /&gt;&amp;nbsp; &amp;nbsp; function(deep, target) { });&lt;/code&gt;&lt;h4&gt;小结&lt;/h4&gt;在这篇文章当中，我们尝试设计出一种适用于JavaScript且易读易维护的函数重载写法。在下一篇文章当中，我们将会尝试编写Overload类，以实现这一设计。如果你不希望错过的话，欢迎订阅：&lt;ul&gt;&lt;li&gt;&lt;a href="http://chinese.catchen.biz/"&gt;Cat in Chinese&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dotnet.catchen.biz/"&gt;Cat in dotNET&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/dotNET"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-7608353110740264513?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/EROEwfCsHRwVh6v2SPZVT9T3uoA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EROEwfCsHRwVh6v2SPZVT9T3uoA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/EROEwfCsHRwVh6v2SPZVT9T3uoA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/EROEwfCsHRwVh6v2SPZVT9T3uoA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=l_8SobnbiaY:pbMZx0T-25c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=l_8SobnbiaY:pbMZx0T-25c:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=l_8SobnbiaY:pbMZx0T-25c:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=l_8SobnbiaY:pbMZx0T-25c:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=l_8SobnbiaY:pbMZx0T-25c:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=l_8SobnbiaY:pbMZx0T-25c:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/l_8SobnbiaY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/7608353110740264513/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=7608353110740264513" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/7608353110740264513?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/7608353110740264513?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/l_8SobnbiaY/javascript-part-1.html" title="让 JavaScript 轻松支持函数重载 (Part 1 - 设计)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/07/javascript-part-1.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEIMQH8-eyp7ImA9WxJVFEg.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-1224147802385655091</id><published>2009-07-01T21:17:00.002+08:00</published><updated>2009-07-01T21:56:21.153+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-01T21:56:21.153+08:00</app:edited><title>写个 JavaScript 异步调用框架 (Part 6 - 实例 &amp; 模式)</title><content type="html">我们用了5篇文章来讨论如何编写一个JavaScript异步调用框架（&lt;a href="http://chinese.catchen.biz/2009/05/javascript-part-1.html"&gt;问题 &amp; 场景&lt;/a&gt;、&lt;a href="http://chinese.catchen.biz/2009/05/javascript-part-2.html"&gt;用例设计&lt;/a&gt;、&lt;a href="http://chinese.catchen.biz/2009/05/javascript-part-3.html"&gt;代码实现&lt;/a&gt;、&lt;a href="http://chinese.catchen.biz/2009/05/javascript-part-4.html"&gt;链式调用&lt;/a&gt;、&lt;a href="http://chinese.catchen.biz/2009/06/javascript-part-5.html"&gt;链式实现&lt;/a&gt;），现在是时候让我们看一下在各种常见开发情景中如何使用它了。&lt;h4&gt;封装Ajax&lt;/h4&gt;设计Async.Operation的最初目的就是解决Ajax调用需要传递callback参数的问题，为此我们先把Ajax请求封装为Async.Operation。我在这里使用的是jQuery，当然无论你用什么基础库，在使用Async.Operation时都可以做这种简单的封装。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var Ajax = {};&lt;br /&gt;&lt;br /&gt;Ajax.get = function(url, data) {&lt;br /&gt;&amp;nbsp; var operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; $.get(url, data, function(result) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; operation.yield(result);&lt;br /&gt;&amp;nbsp; }, "json");&lt;br /&gt;&amp;nbsp; return operation;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;Ajax.post = function(url, data) {&lt;br /&gt;&amp;nbsp; var operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; $.post(url, data, function(result) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; operation.yield(result);&lt;br /&gt;&amp;nbsp; }, "json");&lt;br /&gt;&amp;nbsp; return operation;&lt;br /&gt;};&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在我所调用的服务器端API中，只需要GET和POST，且数据都为JSON，所以我就直接把jQuery提供的其它Ajax选项屏蔽掉了，并设置数据类型为JSON。在你的项目当中，也可以用类似的方式将Ajax封装为若干仅仅返回Async.Operation的方法，将jQuery提供的选项都封装在Ajax这一层内，不再向上层暴露这些选项。&lt;h4&gt;调用Ajax&lt;/h4&gt;把Ajax封装好后，我们就可以开始专心写业务逻辑了。&lt;br /&gt;&lt;br /&gt;假设我们有一个Friend对象，它的get方法用于返回单个好友对象，而getAll方法用于返回所有好友对象。于此对应的是两个服务器端API，friend接口会返回单个好友JSON，而friendlist接口会返回所有好友名称组成的JSON。&lt;br /&gt;&lt;br /&gt;首先我们看看较为基础的get方法怎么写：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function get(name) {&lt;br /&gt;&amp;nbsp; return Ajax.get("/friend",&lt;br /&gt;&amp;nbsp; &amp;nbsp; "name=" + encodeURIComponent(name));&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;就这么简单？对的，假如服务器端API返回的JSON结构正好就是你要的好友对象结构的话。如果JSON结构和好友对象结构是异构的，或许你还要加点代码来把JSON映射为对象：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function get(name) {&lt;br /&gt;&amp;nbsp; var operation = new Async.Operation()&lt;br /&gt;&amp;nbsp; Ajax.get("/friend", "name=" + encodeURIComponent(name))&lt;br /&gt;&amp;nbsp; &amp;nbsp; .addCallback(function(json) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; operation.yield(createFriendFromJson(json));&lt;br /&gt;&amp;nbsp; &amp;nbsp; });&lt;br /&gt;&amp;nbsp; return operation;&lt;br /&gt;}&lt;/code&gt;&lt;h4&gt;Ajax队列&lt;/h4&gt;接下来我们要编写的是getAll方法。因为friendlist接口只返回好友名称列表，因此在取得这份列表后我们还要逐一调用get方法获取具体的好友对象。考虑到在同时进行多个friend接口调用可能触发服务器的防攻击策略，导致被关小黑屋一段时间，所以对friend接口的调用必须排队。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function getAll(){&lt;br /&gt;&amp;nbsp; var operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; var friends = [];&lt;br /&gt;&amp;nbsp; var chain = Async.chain();&lt;br /&gt;&amp;nbsp; Ajax.get("/friendlist", "")&lt;br /&gt;&amp;nbsp; &amp;nbsp; .addCallback(function(json) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; for (var i = 0; i &amp;lt; json.length; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; chain.next(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return get(json.shift())&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .addCallback(function(friend) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; friends.push(friend);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; chain&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .next(function() { operation.yield(friends); })&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .go();&lt;br /&gt;&amp;nbsp; &amp;nbsp; })&lt;br /&gt;&amp;nbsp; return operation;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在这里，我们假设friendlist接口返回的JSON就是一个Array，在获取到这个Array后构造一个等长的异步调用队列，其中每一个调用的逻辑都是一样的——取出Array中首个好友的名称，用get方法获取对应的好友对象，再将好友对象放入另一个Array中。在调用队列的末端，我们再追加了一个调用，用于返回保存好友对象的Array。&lt;br /&gt;&lt;br /&gt;在这个例子当中，我们没有利用调用队列会把上一个函数的结果传递给下一个函数的特性，不过也足够展示调用队列的用途了——让多个底层为Ajax请求的异步操作按照固定的顺序阻塞式执行。&lt;br /&gt;&lt;br /&gt;由于底层异步函数返回的就是Async.Operation，你可以直接把它传递给next方法，也可以用匿名函数包装后传递给next方法，而匿名函数内部只需要一个return。&lt;h4&gt;延时函数&lt;/h4&gt;在上面的例子中，使用队列是为了避免触发服务器的防攻击策略，但有时候这还是不够的。例如说，服务器要求两个请求之间至少间隔500毫秒，否则就认为是攻击，那么我们就要在队列里面插入这个间隔了。&lt;br /&gt;&lt;br /&gt;在原本next方法调用的匿名函数中手动加入setTimeout是一个办法，但为什么我们不写一个辅助函数来解决这类问题呢？让我们来写一个辅助方法并让它和Async.Operation无缝结合起来。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Async.wait = function(delay, context) {&lt;br /&gt;&amp;nbsp; var operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; setTimeout(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; operation.yield(context);&lt;br /&gt;&amp;nbsp; }, delay);&lt;br /&gt;&amp;nbsp; return operation;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;Async.Operation.prototype.wait = function(delay, context) {&lt;br /&gt;&amp;nbsp; this.next(function(context) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return Async.wait(delay, context);&lt;br /&gt;&amp;nbsp; });&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在有了这个辅助方法后，我们就可以在上述getAll方法中轻松实现在每个Ajax请求之间间隔500毫秒。在for循环内的加上对wait的调用就可以了。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;for (var i = 0; i &amp;lt; json.length; i++) {&lt;br /&gt;&amp;nbsp; chain&lt;br /&gt;&amp;nbsp; &amp;nbsp; .wait(500)&lt;br /&gt;&amp;nbsp; &amp;nbsp; .next(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; return get(json.shift())&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .addCallback(function(friend) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; friends.push(friend);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;br /&gt;&amp;nbsp; });&lt;br /&gt;}&lt;/code&gt;&lt;h4&gt;小结&lt;/h4&gt;通过一些简单的例子，我们了解到了Async.Operation常见的使用方式，以及在有需要的时候如何扩展它的功能。希望Async.Operation能够有效帮助大家提高Ajax应用的代码可读性。&lt;br /&gt;&lt;br /&gt;最后，如果大家希望将来继续读到类似的JavaScript开发模式相关的文章，不妨考虑订阅我的博客：&lt;ul&gt;&lt;li&gt;&lt;a href="http://chinese.catchen.biz/"&gt;Cat in Chinese&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dotnet.catchen.biz/"&gt;Cat in dotNET&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/dotNET"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-1224147802385655091?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/sXjL7yEJz6R_cVZUVVv56WCFJ2Y/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sXjL7yEJz6R_cVZUVVv56WCFJ2Y/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/sXjL7yEJz6R_cVZUVVv56WCFJ2Y/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/sXjL7yEJz6R_cVZUVVv56WCFJ2Y/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=4YwEgAzOKZQ:WwI78mFnsO0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=4YwEgAzOKZQ:WwI78mFnsO0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=4YwEgAzOKZQ:WwI78mFnsO0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=4YwEgAzOKZQ:WwI78mFnsO0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=4YwEgAzOKZQ:WwI78mFnsO0:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=4YwEgAzOKZQ:WwI78mFnsO0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/4YwEgAzOKZQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/1224147802385655091/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=1224147802385655091" title="4 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/1224147802385655091?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/1224147802385655091?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/4YwEgAzOKZQ/javascript-part-6.html" title="写个 JavaScript 异步调用框架 (Part 6 - 实例 &amp;amp; 模式)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/07/javascript-part-6.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUNQns-fip7ImA9WxJVE0o.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-898950405528034715</id><published>2009-06-30T23:33:00.002+08:00</published><updated>2009-06-30T23:54:53.556+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-30T23:54:53.556+08:00</app:edited><title>写个 JavaScript 异步调用框架 (Part 5 - 链式实现)</title><content type="html">在上一篇文章里面，我们为异步调用框架设计了一种链式调用方式，来增强异步调用队列的代码可读性，现在我们就来编写实现这部分功能的代码。&lt;h4&gt;调用入口&lt;/h4&gt;链式调用存在Async.go方法和Async.chain方法两个入口，这两个入口本质上是一致的，只是Async.chain方法在调用时先不提供初始参数，而Async.go方法在调用时提供了初始参数并启动异步调用链。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Async.chain = function() {&lt;br /&gt;&amp;nbsp; var chain = new Async.Operation({ chain: true });&lt;br /&gt;&amp;nbsp; return chain;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;Async.go = function(initialArgument) {&lt;br /&gt;&amp;nbsp; return Async.chain().go(initialArgument);&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在这里我们可以看到，链式调用本身也是一个Async.Operation，链式调用所需的go方法和next方法都是在Async.Operation上面做的扩展，并且这个扩展不会很难，这将在下一小节说明。&lt;h4&gt;扩展方法&lt;/h4&gt;我们都知道，通过addCallback方法添加的回调函数是会被逐一执行的，至少同步函数如此，因此我们可以用Async.Operation的这一特性来维护异步调用队列，前提是我们为它加上对异步调用进行队列的支持。&lt;br /&gt;&lt;br /&gt;对于异步调用进行队列的支持，我们稍后再来处理，首先我们利用现成的addCallback方法和yield方法扩展出go方法和next方法。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;this.go = function(initialArgument) {&lt;br /&gt;&amp;nbsp; return this.yield(initialArgument);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;this.next = function(nextFunction) {&lt;br /&gt;&amp;nbsp; return this.addCallback(nextFunction);&lt;br /&gt;};&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;实际上，go方法和next方法直接调用的正是yield方法和addCallback方法。go方法的语义与yield方法一样，传递一个参数给Async.Operation实例，并且启动调用队列。同时，next方法的语义和addCallback方法，添加一个调用到队列的末端。&lt;h4&gt;异步队列&lt;/h4&gt;如何才能让原本仅支持同步的队列变得也支持异步？这需要检测队列中的每一个调用的返回，如果返回类型为Async.Operation，我们知道是异步调用，从而使用特殊的方法等它执行完后再执行下去。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;callbackResult = callback(self.result);&lt;br /&gt;self.result = callbackResult;&lt;br /&gt;if (callbackResult &amp;&amp; callbackResult instanceof Async.Operation) {&lt;br /&gt;&amp;nbsp; innerChain = Async.chain();&lt;br /&gt;&amp;nbsp; while (callbackQueue.length &gt; 0) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; innerChain.next(callbackQueue.shift());&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; innerChain.next(function(result) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; self.result = result;&lt;br /&gt;&amp;nbsp; &amp;nbsp; self.state = "completed";&lt;br /&gt;&amp;nbsp; &amp;nbsp; self.completed = true;&lt;br /&gt;&amp;nbsp; &amp;nbsp; return result;&lt;br /&gt;&amp;nbsp; });&lt;br /&gt;&amp;nbsp; callbackResult.addCallback(function(result) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; self.result = result;&lt;br /&gt;&amp;nbsp; &amp;nbsp; innerChain.go(result);&lt;br /&gt;&amp;nbsp; });&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;如果调用返回了一个Async.Operation实例，我们就利用它自身的addCallback方法帮我们执行队列中余下的调用。准确来说，是我们构造了一个新的调用链，把队列余下的调用都转移到新的调用链上，然后让当前异步调用在回调中启动这个新的调用链。&lt;br /&gt;&lt;br /&gt;此外还有一些地方我们需要略作修改，以兼容新的异步调用队列的。例如result、state、completed的状态变更，在链式调用中是有所不同的。&lt;h4&gt;小结&lt;/h4&gt;我们在原有的Async.Operation上略作修改，使得它支持异步调用队列，完整的代码看这里：&lt;a href="http://www.cnblogs.com/cathsfz/articles/1514190.html"&gt;支持链式调用的异步调用框架Async.Operation&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;现在我们已经拥有了一个功能强大的Async.Operation，接下来我们就要看看如何将它投入到更多常见的使用模式中去，如果你不希望错过相关讨论的话，欢迎订阅我的博客：&lt;ul&gt;&lt;li&gt;&lt;a href="http://chinese.catchen.biz/"&gt;Cat in Chinese&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dotnet.catchen.biz/"&gt;Cat in dotNET&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/dotNET"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-898950405528034715?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/RCejfTEmMsIgLUX-Sl7oDPHDig0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RCejfTEmMsIgLUX-Sl7oDPHDig0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/RCejfTEmMsIgLUX-Sl7oDPHDig0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/RCejfTEmMsIgLUX-Sl7oDPHDig0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=1MrEFik6yUo:cWwAMNWIMeg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=1MrEFik6yUo:cWwAMNWIMeg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=1MrEFik6yUo:cWwAMNWIMeg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=1MrEFik6yUo:cWwAMNWIMeg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=1MrEFik6yUo:cWwAMNWIMeg:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=1MrEFik6yUo:cWwAMNWIMeg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/1MrEFik6yUo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/898950405528034715/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=898950405528034715" title="6 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/898950405528034715?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/898950405528034715?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/1MrEFik6yUo/javascript-part-5.html" title="写个 JavaScript 异步调用框架 (Part 5 - 链式实现)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/06/javascript-part-5.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEFRno8cCp7ImA9WxJWEE0.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-7849806061085151943</id><published>2009-06-15T02:30:00.001+08:00</published><updated>2009-06-15T02:36:57.478+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-15T02:36:57.478+08:00</app:edited><title>中国程序员有美国梦吗？</title><content type="html">&lt;a href="http://www.cnblogs.com/JeffreyZhao/"&gt;Jeff&lt;/a&gt;最近转载了一篇名为《贺计算机成“就业最困难专业”》的文章，然后抛出了一个&lt;a href="http://www.cnblogs.com/JeffreyZhao/archive/2009/06/12/1501834.html"&gt;问题&lt;/a&gt;来，问大家对此看法如何，接着自然又引起了新一轮博客园首页发文热潮。对此，我站在我的角度说说我的看法。&lt;h4&gt;大浪淘沙，金子难寻&lt;/h4&gt;1848年，美国爆发了&lt;a href="http://en.wikipedia.org/wiki/California_Gold_Rush"&gt;加州淘金热潮&lt;/a&gt;，大量人口涌到加州进行淘金，其直接后果就是让一个叫&lt;a href="http://en.wikipedia.org/wiki/San_Francisco"&gt;旧金山&lt;/a&gt;小村庄的转眼间变成了一座大城市。在淘金热潮之初，你拿个筛子在河床里筛泥沙也能找到金子，这是何其容易的事情。但我们假想一下，如果当时一个淘金者拿着他的筛子跑到中国来，他能够淘到金子吗？我觉得是不可以的。这不是说中国的黄金矿藏量就比不上美国的一个州，而是中国没有如此高浓度而且可以进行露天开采的金矿。&lt;br /&gt;&lt;br /&gt;1971年，旧金山&lt;a href="http://en.wikipedia.org/wiki/Bay_area"&gt;湾区&lt;/a&gt;南端由于“淘沙子”出了名，因而被大家叫做&lt;a href="http://en.wikipedia.org/wiki/Silicon_Valley"&gt;硅谷&lt;/a&gt;。1998年，Google在硅谷诞生了，然后开始疯狂地吸收硅谷乃至全球的技术人才。2006至2007年，Google在中国进行大规模招聘，这时候他们遇到了跟淘金者中国之行一样的难题……&lt;br /&gt;&lt;br /&gt;中国是第一个成功迫使Google进行笔试的国家。这句话说出来可能会让大家觉得十分搞笑，国内哪家公司招聘一线工程师不是先笔试再面试的。在人力资源供求相当的国家里，确实不需要笔试这轮筛选。有资质并且自信有资格来应聘的人，一般都能够得到充分的沟通，然后再确定这份工作是否适合。但这在人力资源明显供过于求的中国来说，就一点也不现实。&lt;br /&gt;&lt;br /&gt;Google中国也有高层承认，以ACM/ICPC竞赛成绩来挑人确实有所缺陷，很多适合这份工作的人会被直接淘汰掉，连面对面或电话沟通的机会也没有。但假如你去看一下Google中国的校园招聘场面，看一下在各省重点大学里连开多个大教室进行笔试的情景，你不得不承认，要跟所有这些学生进行沟通的成本实在是连Google中国也支付不起。&lt;br /&gt;&lt;br /&gt;总结一下，中国不是没有人才，也不是培养人才的方法有严重缺陷，至少这些都不是根本问题。根本问题是，在如此庞大的人口基数下，再好的企业都只能选用一套折中的人才选拔方案。你不能怪企业，说假如能生在美国你就能被Google美国所挖掘，而现在则被Google中国拒之门外。&lt;h4&gt;人人都会发光的时候你该怎么办？&lt;/h4&gt;有时候用“是金子早晚会发光”的说法来自我安慰一下，也不是什么坏事。但当你发现人人都会发光的时候，你就知道事情有多不好了。&lt;br /&gt;&lt;br /&gt;我们来做个数学建模，假设美国计算机人才中的top 100, top 1,000, top 10,000分别叫做A类、B类、C类。那么美国的人力资源供需关系大概是这样的：&lt;ul style="list-style-type: upper-alpha"&gt;&lt;li&gt;需求：100人；供应：100人。&lt;/li&gt;&lt;li&gt;需求：1,000人；供应：1,000人。&lt;/li&gt;&lt;li&gt;需求：10,000人；供应：10,000人。&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;在保持A类、B类、C类界线不变的情况下，放到中国来很可能就是这样子的：&lt;ul style="list-style-type: upper-alpha"&gt;&lt;li&gt;需求：500人；供应：1,000人。&lt;/li&gt;&lt;li&gt;需求：5,000人；供应：100,000人。&lt;/li&gt;&lt;li&gt;需求：50,000人；供应：10,000,000人。&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;就算中国的人才需求是美国的5倍，但依然供过于求。就算顶级人才的供求矛盾不明显，人才结构上的不合理也会给底层造成巨大的压力。&lt;br /&gt;&lt;br /&gt;在A类里面，只有500人做不了自己应做的工作，被迫降级去做B类的工作。对于人口如此众多的一个国家来说，让500人屈就一下不是什么大问题。在B类里面，会有95,000人做不了自己应做的工作。而到了C类里面，会有9,950,000人做不了自己应做的工作，这时候问题就很严重了。&lt;br /&gt;&lt;br /&gt;如果你搞不懂上面那堆数字说的是什么，那么我尝试换用浅白一些的语言来再说一次。如果你在美国，并且你达到了Microsoft美国招聘的要求，那么你成功加入Microsoft的概率就相当高了，因为你和Microsoft相互需要对方。&lt;br /&gt;&lt;br /&gt;但如果你在中国，并且你达到了Microsoft中国招聘的要求，那么你成功加入Microsoft的概率并不怎么高，因为跟你一样符合这一条件的人数要远多于Microsoft的需求。如果你想要确保一个较高的应聘成功率，那么你必须远高于Microsoft的招聘要求。&lt;h4&gt;没有压力的都跳槽了&lt;/h4&gt;假如我们把胜任某个职位的人简单分为两类：刚刚好胜任的，以及过度胜任的。那么，我们可以得到一个有趣的推论。&lt;br /&gt;&lt;br /&gt;刚刚好胜任的人，是一定很有压力的。如果上面的分析没错的话，你之所以能够获得这个职位，一定程度上依赖于运气。好几个拥有同等实力的人跟你抢这个职位，尽管他们没能抢到，但依旧对此虎视眈眈，时刻准备着抢你饭碗。企业没理由对此坐视不理，一定会让你一直处于失业的边缘，以此作为筹码逼你好好干活，这时候你没压力才怪。&lt;br /&gt;&lt;br /&gt;过度胜任的人，&lt;a href="http://en.wikipedia.org/wiki/Overqualification"&gt;忠诚度一定会打折扣&lt;/a&gt;。总有更好的职位在对你招手，尽管你知道那些职位你只能凭运气获得，尽管你知道得到了你也站到了失业的边缘上，但你就不心动吗？肯定还是会心动的。&lt;br /&gt;&lt;br /&gt;知足常乐者？估计在顶端会多一些。我和Jeff这样的，属于在国内少数算是享有&lt;a href="http://en.wikipedia.org/wiki/American_Dream"&gt;American Dream&lt;/a&gt;的人，也就是能够通过自己的奋斗来获得应有的财富与社会地位。Jeff倾向于认为这对广大程序员普遍适用，并且鼓励大家都去追求自己的梦想。然而我得到的结论却并非如此。对于这事情，你怎么看？&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-7849806061085151943?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/PNE4F1-Yi8RJZmz-S9N25IKXoy0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PNE4F1-Yi8RJZmz-S9N25IKXoy0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/PNE4F1-Yi8RJZmz-S9N25IKXoy0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PNE4F1-Yi8RJZmz-S9N25IKXoy0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Vd8y37ZJ_kU:ptG8MfYADh8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Vd8y37ZJ_kU:ptG8MfYADh8:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Vd8y37ZJ_kU:ptG8MfYADh8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=Vd8y37ZJ_kU:ptG8MfYADh8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Vd8y37ZJ_kU:ptG8MfYADh8:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Vd8y37ZJ_kU:ptG8MfYADh8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/Vd8y37ZJ_kU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/7849806061085151943/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=7849806061085151943" title="2 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/7849806061085151943?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/7849806061085151943?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/Vd8y37ZJ_kU/blog-post.html" title="中国程序员有美国梦吗？" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/06/blog-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQMR3g7fyp7ImA9WxJVE0s.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-5782044070666185412</id><published>2009-05-08T15:29:00.004+08:00</published><updated>2009-06-30T19:13:06.607+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-30T19:13:06.607+08:00</app:edited><title>写个 JavaScript 异步调用框架 (Part 4 - 链式调用)</title><content type="html">我们已经实现了一个简单的异步调用框架，然而还有一些美中不足，那就是顺序执行的异步函数需要用嵌套的方式来声明。&lt;br /&gt;&lt;br /&gt;现实开发中，要按顺序执行一系列的同步异步操作又是很常见的。还是用&lt;a href="http://web.im.baidu.com/" target="_blank"&gt;百度Hi网页版&lt;/a&gt;中的例子，我们先要异步获取联系人列表，然后再异步获取每一个联系人的具体信息，而且后者是分页获取的，每次请求发送10个联系人的名称然后取回对应的具体信息。这就是多个需要顺序执行的异步请求。&lt;br /&gt;&lt;br /&gt;为此，我们需要设计一种新的操作方式来优化代码可读性，让顺序异步操作代码看起来和传统的顺序同步操作代码一样优雅。&lt;h4&gt;传统做法&lt;/h4&gt;大多数程序员都能够很好的理解顺序执行的代码，例如这样子的：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var firstResult = firstOperation(initialArgument);&lt;br /&gt;var secondResult = secondOperation(firstResult);&lt;br /&gt;var finalResult = thirdOperation(secondResult);&lt;br /&gt;alert(finalResult);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;其中先执行的函数为后执行的函数提供所需的数据。然而使用我们的异步调用框架后，同样的逻辑必须变成这样子：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;firstAsyncOperation(initialArgument)&lt;br /&gt;&amp;nbsp; .addCallback(function(firstResult) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; secondAsyncOperation(firstResult)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; .addCallback(function(secondResult) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; thirdAsyncOperation(secondResult)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .addCallback(function(finalResult) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; alert(finalResult);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;br /&gt;&amp;nbsp; &amp;nbsp; });&lt;br /&gt;});&lt;/code&gt;&lt;h4&gt;链式写法&lt;/h4&gt;我认为上面的代码实在是太不美观了，并且希望能够改造为jQuery风格的链式写法。为此，我们先构造一个用例：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Async.go(initialArgument)&lt;br /&gt;&amp;nbsp; .next(firstAsyncOperation)&lt;br /&gt;&amp;nbsp; .next(secondAsyncOperation)&lt;br /&gt;&amp;nbsp; .next(thirdAsyncOperation)&lt;br /&gt;&amp;nbsp; .next(function(finalResult) { alert(finalResult); })&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在这个用例当中，我们在go传入初始化数据，然后每一个next后面传入一个数据处理函数，这些处理函数按顺序对数据进行处理。&lt;h4&gt;同步并存&lt;/h4&gt;上面的用例调用到的全部都是异步函数，不过我们最好能够兼容同步函数，让使用者无需关心函数的具体实现，也能使用这项功能。为此我们再写一个这样的用例：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Async.go(0)&lt;br /&gt;&amp;nbsp; .next(function(i) { alert(i); return i + 1; })&lt;br /&gt;&amp;nbsp; .next(function(i) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; alert(i);&lt;br /&gt;&amp;nbsp; &amp;nbsp; var operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; &amp;nbsp; setTimeout(function() { operation.yield(i + 1); }, 1000);&lt;br /&gt;&amp;nbsp; &amp;nbsp; return operation;&lt;br /&gt;&amp;nbsp; })&lt;br /&gt;&amp;nbsp; .next(function(i) { alert(i); return i + 1; })&lt;br /&gt;&amp;nbsp; .next(function(i) { alert(i); return i; });&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在上述用例中，我们期待能够看到0, 1, 2, 3的提示信息序列，并且1和2之间间隔为1000毫秒。&lt;h4&gt;异步本质&lt;/h4&gt;一个链式调用，本质上也是一个异步调用，所以它返回的也是一个Operation实例。这个实例自然也有result、state和completed这几个字段，并且当整个链式调用完成时，result等于最后一个调用返回的结果，而completed自然是等于true。&lt;br /&gt;&lt;br /&gt;我们可以扩展一下上一个用例，得到如下用例代码：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var chainOperation = Async.go(0)&lt;br /&gt;&amp;nbsp; .next(function(i) { alert(i); return i + 1; })&lt;br /&gt;&amp;nbsp; .next(function(i) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; alert(i);&lt;br /&gt;&amp;nbsp; &amp;nbsp; var operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; &amp;nbsp; setTimeout(function() { operation.yield(i + 1); }, 1000);&lt;br /&gt;&amp;nbsp; &amp;nbsp; return operation;&lt;br /&gt;&amp;nbsp; })&lt;br /&gt;&amp;nbsp; .next(function(i) { alert(i); return i + 1; })&lt;br /&gt;&amp;nbsp; .next(function(i) { alert(i); return i; });&lt;br /&gt;&lt;br /&gt;setTiemout(function() { alert(chainOperation.result; }, 2000);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;把链式调用的返回保存下来，在链式调用完成时，它的result应该与最后一个操作的返回一致。在上述用例中，也就是3。&lt;h4&gt;调用时机&lt;/h4&gt;尽管我们提供了一种链式调用方式，但是用户不一定会按照这种固定的方式来调用，所以我们仍然要考虑兼容用户的各种可能用法，例如说异步地用next往调用链添加操作：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var chainOperation = Async.go(0);&lt;br /&gt;chainOperation.next(function(i) { alert(i); return i + 1; });&lt;br /&gt;setTimeout(function() {&lt;br /&gt;&amp;nbsp; chainOperation.next(function(i) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; alert(i);&lt;br /&gt;&amp;nbsp; &amp;nbsp; var operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; &amp;nbsp; setTimeout(function() { operation.yield(i + 1); }, 2000);&lt;br /&gt;&amp;nbsp; &amp;nbsp; return operation;&lt;br /&gt;&amp;nbsp; })&lt;br /&gt;}, 1000);&lt;br /&gt;setTimeout(function() {&lt;br /&gt;&amp;nbsp; chainOperation.next(function(i) { alert(i); return i + 1; });&lt;br /&gt;}, 2000);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在这个用例当中，用户每隔1000毫秒添加一个操作，而其中第二个操作耗时2000毫秒。也就是说，添加第三个操作时第二个操作还没返回。作为一个健壮的框架，必须要能兼容这样的使用方式。&lt;br /&gt;&lt;br /&gt;此外我们还要考虑，用户可能想要先构造调用链，然后再执行调用链。这时候用户就会先使用next方法添加操作，再使用go方法执行。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var chainOperation = Async&lt;br /&gt;&amp;nbsp; .chain(function(i) { alert(i); return i + 1; })&lt;br /&gt;&amp;nbsp; .next(function(i) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; alert(i);&lt;br /&gt;&amp;nbsp; &amp;nbsp; var operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; &amp;nbsp; setTimeout(function() { operation.yield(i + 1); }, 2000);&lt;br /&gt;&amp;nbsp; &amp;nbsp; return operation;&lt;br /&gt;&amp;nbsp; })&lt;br /&gt;&amp;nbsp; .go(0)&lt;br /&gt;setTimeout(function() {&lt;br /&gt;&amp;nbsp; chainOperation.next(function(i) { alert(i); return i + 1; })&lt;br /&gt;}, 1000);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在上述用例中，用户通过chain和next添加了头同步操作和异步操作各一个，然后用go执行调用链，在调用链执行完毕之前又用next异步追加了一个操作。一个健壮的框架，在这样的用例当中应该能够如同用户所期望的那样提示0, 1, 2。&lt;h4&gt;小结&lt;/h4&gt;针对链式调用的需求，我们设计了如此多的用例，包括各种奇怪的异步调用方式。最终如何实现这样的功能呢？如果你想知道的话，欢迎订阅我的博客：&lt;ul&gt;&lt;li&gt;&lt;a href="http://chinese.catchen.biz/"&gt;Cat in Chinese&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dotnet.catchen.biz/"&gt;Cat in dotNET&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/dotNET"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-5782044070666185412?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/n7JLchVsvj3MeXRSWPln6cYy85Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/n7JLchVsvj3MeXRSWPln6cYy85Q/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/n7JLchVsvj3MeXRSWPln6cYy85Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/n7JLchVsvj3MeXRSWPln6cYy85Q/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=lYHMXSEMJJU:oqJtOf5hyA0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=lYHMXSEMJJU:oqJtOf5hyA0:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=lYHMXSEMJJU:oqJtOf5hyA0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=lYHMXSEMJJU:oqJtOf5hyA0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=lYHMXSEMJJU:oqJtOf5hyA0:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=lYHMXSEMJJU:oqJtOf5hyA0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/lYHMXSEMJJU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/5782044070666185412/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=5782044070666185412" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/5782044070666185412?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/5782044070666185412?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/lYHMXSEMJJU/javascript-part-4.html" title="写个 JavaScript 异步调用框架 (Part 4 - 链式调用)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/05/javascript-part-4.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEIMR3czeyp7ImA9WxJVE0g.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-4368906441611258753</id><published>2009-05-07T14:51:00.002+08:00</published><updated>2009-06-30T18:09:46.983+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-30T18:09:46.983+08:00</app:edited><title>写个 JavaScript 异步调用框架 (Part 3 - 代码实现)</title><content type="html">在上一篇文章里，我们说到了要实现一个Async.Operation类，通过addCallback方法传递回调函数，并且通过yield方法返回回调结果。现在我们就来实现这个类吧。&lt;h4&gt;类结构&lt;/h4&gt;首先我们来搭一个架子，把需要用到的似有变量都列出来。我们需要一个数组，来保存回调函数列表；需要一个标志位，来表示异步操作是否已完成；还可以学IAsyncResult，加一个state，允许异步操作的实现者对外暴露自定义的执行状态；最后加一个变量保存异步操作结果。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var Cat = {};&lt;br /&gt;&lt;br /&gt;Async = {&lt;br /&gt;&amp;nbsp; Operation: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var callbackQueue = [];&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.result = undefined;&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.state = "waiting";&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.completed = false;&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;}&lt;/code&gt;&lt;h4&gt;addCallback方法&lt;/h4&gt;接下来，我们要实现addCallback方法，它的工作职责很简单，就是把回调函数放到callbackQueue中。此外，如果此时completed为true，说明异步操作已经yield过了，则立即调用此回调。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;this.yield = function(callback) {&lt;br /&gt;&amp;nbsp; callbackQueue.push(callback);&lt;br /&gt;&amp;nbsp; if (this.completed) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.yield(this.result);&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; return this;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;我们假设yield方法会把callbackQueue中的回调函数逐个取出来然后调用，因此如果compeleted为true，则使用已有的result再调用一次yield就可以了，这样yield自然会调用这次添加到callbackQueue的回调函数。&lt;br /&gt;&lt;br /&gt;至于最后的return this;，只是为了方便jQuery风格的链式写法，可以通过点号分隔连续添加多个回调函数：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;asyncOperation(argument)&lt;br /&gt;&amp;nbsp; .addCallback(firstCallback)&lt;br /&gt;&amp;nbsp; .addCallback(secondCallback);&lt;/code&gt;&lt;h4&gt;yield方法&lt;/h4&gt;最后，我们要实现yield方法。它需要将callbackQueue中的回调函数逐个取出来，然后都调用一遍，并且保证这个操作是异步吧。&lt;br /&gt;&lt;br /&gt;&lt;code&gt;this.yield = function(result) {&lt;br /&gt;&amp;nbsp; var self = this;&lt;br /&gt;&amp;nbsp; setTimeout(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; self.result = result;&lt;br /&gt;&amp;nbsp; &amp;nbsp; self.state = "completed";&lt;br /&gt;&amp;nbsp; &amp;nbsp; self.completed = true;&lt;br /&gt;&amp;nbsp; &amp;nbsp; while (callbackQueue.length &gt; 0) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; var callback = callbackQueue.shift();&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; callback(self.result);&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; }, 1);&lt;br /&gt;&amp;nbsp; return this;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;通过使用setTimeout，我们确保了yield的实际操作是异步进行的。然后我们把用户传入yield的结果及相关状态更新到对象属性之上，最后遍历callbackQueue调用所有的回调函数。&lt;h4&gt;小结&lt;/h4&gt;这样我们就做好了一个简单的JavaScript异步调用框架，完整的代码可以看这里：&lt;a href="http://www.cnblogs.com/cathsfz/articles/1451428.html"&gt;异步调用框架Async.Operation&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;这个框架能够很好的解决调用栈中出现同步异步操作并存的情况，假设所有函数都返回Async.Operation，框架的使用者可以使用一种统一的模式来编写代码，处理函数返回，而无需关心这个函数实际上是同步返回了还是异步返回了。&lt;br /&gt;&lt;br /&gt;对于串行调用多个异步函数的情况，我们现在可以用嵌套addCallback的方式来书写，但随着嵌套层数的增多，代码会变得越来越不美观：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;firstAsyncOperation().addCallback(function() {&lt;br /&gt;&amp;nbsp; secondAsyncOperation().addCallback(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; thirdAsyncOperation().addCallback(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; finalSyncOperation();&lt;br /&gt;&amp;nbsp; &amp;nbsp; });&lt;br /&gt;&amp;nbsp; });&lt;br /&gt;});&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;我们能否把嵌套形式改为jQuery风格的链式写法呢？这是我们接下来要思考的问题，如果你不希望错过相关讨论的话，欢迎订阅我的博客：&lt;ul&gt;&lt;li&gt;&lt;a href="http://chinese.catchen.biz/"&gt;Cat in Chinese&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dotnet.catchen.biz/"&gt;Cat in dotNET&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/dotNET"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-4368906441611258753?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/a83j73wdt1JxFOtKfVuERaXq0jk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/a83j73wdt1JxFOtKfVuERaXq0jk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/a83j73wdt1JxFOtKfVuERaXq0jk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/a83j73wdt1JxFOtKfVuERaXq0jk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pXr-aOpgf5s:ID02aFtxHXc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pXr-aOpgf5s:ID02aFtxHXc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pXr-aOpgf5s:ID02aFtxHXc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=pXr-aOpgf5s:ID02aFtxHXc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pXr-aOpgf5s:ID02aFtxHXc:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pXr-aOpgf5s:ID02aFtxHXc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/pXr-aOpgf5s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/4368906441611258753/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=4368906441611258753" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/4368906441611258753?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/4368906441611258753?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/pXr-aOpgf5s/javascript-part-3.html" title="写个 JavaScript 异步调用框架 (Part 3 - 代码实现)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/05/javascript-part-3.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEIHSH48eCp7ImA9WxJVE0g.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-8224993432935771088</id><published>2009-05-07T01:03:00.002+08:00</published><updated>2009-06-30T18:08:59.070+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-30T18:08:59.070+08:00</app:edited><title>写个 JavaScript 异步调用框架 (Part 2 - 用例设计)</title><content type="html">在上一篇文章里说到，我们要设计一个异步调用框架，最好能够统一同步异步调用的接口，同时具体调用顺序与实现方式无关。那么我们现在就来设计这样一个框架的用例。&lt;h4&gt;传递回调&lt;/h4&gt;我们首先要考虑的一个问题是，如何传递回调入口。在最传统的XHR调用当中，回调函数会被作为最后一个参数传递给异步函数：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function asyncOperation(argument, callback)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在参数相当多的时候，我们可以把参数放到一个JSON里面，这样参数就如同具名参数一样，可以通过参数名选择性的传递参数，不传递的参数相当于使用默认值。这是从Prototype开始就流行起来的做法：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function asyncOperation(argument, options)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;然而这两种做法都有一个坏处，就是把同步函数改为异步函数（或同步异步混合函数）时，必须显式地修改函数签名，在最后增加一个（或多个）参数。&lt;br /&gt;&lt;br /&gt;由于在调用栈的底层引入异步函数对我们来说太常见了，为此可能要更改一大堆上层调用函数签名的成本实在是太高了，所以我们还是想一个不用修改函数签名的做法吧。&lt;br /&gt;&lt;br /&gt;在这里我参考了.NET Framework的IAsyncResult设计，把异步操作有关的一切信息集中到一个对象上来，从而避免了对函数签名的修改。在此，我们假设一个异步函数的调用原型是这样子的：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function asyncOperation(argument) {&lt;br/&gt;&amp;nbsp; operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; setTimeout(function() { operation.yield("hello world"); }, 1000);&lt;br /&gt;&amp;nbsp; return operation;&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在这段代码里，我们返回了一个Operation对象，用于将来传递回调函数。同时，我们通过setTimeout模拟了异步返回结果，而具体的返回方式就是yield方法。&lt;br /&gt;&lt;br /&gt;接着，我们还要设计传递回调函数的方法。由于我们不能好像C#那样重载+=运算符，所以只能用函数传递回调函数：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var operation = asyncOperation(argument);&lt;br /&gt;operation.addCallback(function(result) { alert(result); });&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;在C#里面做这样的设计是不安全的，因为在异步操作可能在添加回调之前就完成了。但在JavaScript里面这样写是安全的，因为JavaScript是单线程的，紧接着asyncOperation的同步addCallback必然先执行，asyncOperation中的异步yield必然后执行。&lt;h4&gt;调用顺序&lt;/h4&gt;可能有人要问，如果用户使用同步的方式来调用yield，这时候执行顺序不一样依赖于yield的实现吗？没错，不过yeild是在框架中一次性实现的，我们只要把它做成异步的就可以了，这样即使对它进行同步调用，也不影响执行顺序：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function psudoAsyncOperation(argument) {&lt;br /&gt;&amp;nbsp; operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; operation.yield("hello world");&lt;br /&gt;&amp;nbsp; return operation;&lt;br /&gt;}&lt;br /&gt;var operation = asyncOperation(argument);&lt;br /&gt;operation.addCallback(function(result) { alert(result); });&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;就算把代码写成这个样子，我们也能确保addCallback先于yield的实际逻辑执行。&lt;h4&gt;事后回调&lt;/h4&gt;有时候，框架的使用者可能真的写出了先yield后addCallback的代码。这时候，我认为必须保证addCallback中添加的回调函数会被立即触发。因为用户添加这个回调函数，意味着他期望当异步操作有结果时通知这个回调函数，而这与添加回调函数时异步操作是否完成无关。为此，我们再添加一个用例：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function psudoAsyncOperation(argument) {&lt;br /&gt;&amp;nbsp; operation = new Async.Operation();&lt;br /&gt;&amp;nbsp; operation.yield("hello world");&lt;br /&gt;&amp;nbsp; return operation;&lt;br /&gt;}&lt;br /&gt;var operation = asyncOperation(argument);&lt;br /&gt;setTimeout(function() {&lt;br/&gt;&amp;nbsp; operation.addCallback(function(result) { alert(result); });&lt;br/&gt;}, 1000);&lt;/code&gt;&lt;h4&gt;小结&lt;/h4&gt;到这里，我们就设计好了一个名为Async.Operation的异步操作对象，具体如何实现关键的yield方法和addCallback方法将在下一篇文章讲述如果。你不希望错过的话，欢迎订阅我的博客：&lt;ul&gt;&lt;li&gt;&lt;a href="http://chinese.catchen.biz/"&gt;Cat in Chinese&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dotnet.catchen.biz/"&gt;Cat in dotNET&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/dotNET"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-8224993432935771088?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/7_fd59zGr5a4MC7rCFtctP3y60s/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/7_fd59zGr5a4MC7rCFtctP3y60s/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/7_fd59zGr5a4MC7rCFtctP3y60s/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/7_fd59zGr5a4MC7rCFtctP3y60s/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=mRzM2pWFSwU:DfjkvhHTdG4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=mRzM2pWFSwU:DfjkvhHTdG4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=mRzM2pWFSwU:DfjkvhHTdG4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=mRzM2pWFSwU:DfjkvhHTdG4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=mRzM2pWFSwU:DfjkvhHTdG4:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=mRzM2pWFSwU:DfjkvhHTdG4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/mRzM2pWFSwU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/8224993432935771088/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=8224993432935771088" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8224993432935771088?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8224993432935771088?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/mRzM2pWFSwU/javascript-part-2.html" title="写个 JavaScript 异步调用框架 (Part 2 - 用例设计)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/05/javascript-part-2.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMGQ3k5eSp7ImA9WxJSFUk.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-8630630787516835659</id><published>2009-05-06T00:11:00.001+08:00</published><updated>2009-05-06T00:20:22.721+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-06T00:20:22.721+08:00</app:edited><title>写个 JavaScript 异步调用框架 (Part 1 - 问题 &amp; 场景)</title><content type="html">&lt;h4&gt;问题&lt;/h4&gt;在Ajax应用中，调用XMLHttpRequest是很常见的情况。特别是以客户端为中心的Ajax应用，各种需要从服务器端获取数据的操作都通过XHR异步调用完成。然而在单线程的JavaScript编程中，XHR异步调用的代码风格实在是与一般的JavaScript代码格格不入。&lt;h5&gt;额外参数&lt;/h5&gt;考虑一个除法函数，如果它是纯客户端的同步函数，那么签名会是这样的：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function divide(operand1, operand2)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;然而假设我们对客户端除法的精度不满意，于是把除法转移到服务器端来执行，那么它是个需要调用XHR的异步函数，签名也就可能会是以下几种之一：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function divide(operand1, operand2, callback)&lt;br /&gt;function divide(operand1, operand2, successCallback, failureCallback)&lt;br /&gt;function divide(operand1, operand2, options)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;我们必须在签名中引入新的参数来传递回调函数，不能选择让函数变成阻塞式的同步调用。&lt;h5&gt;可传递性&lt;/h5&gt;不仅仅直接操作XHR的函数需要引入新的参数，这种复杂性还会顺着调用栈向外传递。例如说，我们对加减乘除四则运算作了封装，只向外暴露一个运算接口：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function calculate(operand1, operand2, operator)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;这个calculate函数根据operator参数来调用内部的plus, subtract, multiply, divide函数。然而，因为divide函数变成了异步函数，所以整个calculate函数不得不也转变为异步函数：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;function calculate(operand1, operand2, operator, callback)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;同时，在调用栈之上凡是需要调用到calculate的函数，都必须变成异步的，除非它并不需要向上一级调用函数返回结果。&lt;h5&gt;同步并存&lt;/h5&gt;尽管calculate函数变成了一个异步函数，它所调用的plus, subtract, multiply函数还是同步函数，只有调用divide时是异步的，这时候calculate就是一个异步同步并存函数。&lt;br /&gt;&lt;br /&gt;这会带来什么问题？calculate的调用者看到函数签名自然会认为calculate是个异步函数，因为它需要传递回调函数，然而calculate的执行方式却是不确定的。考虑如下调用：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;calculate(operand1, operand2, operator, callback);&lt;br /&gt;next();&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;这里涉及到callback和next两个函数，它们哪个先执行哪个后执行是不确定的，或者说是依赖于calculate具体实现的。&lt;br /&gt;&lt;br /&gt;如果calculate的实现是，当不需要进行异步操作时，直接调用callback。那么，在执行加减乘法时callback会在next之前被调用；在执行除法时next会在callback之前调用。&lt;br /&gt;&lt;br /&gt;如果我们不喜欢这种不确定性，我们可以改变一下calculate的实现，把同步调用也都改为setTimeout形式的，这样在任何情况下next都一定会在callback之前被调用。&lt;br /&gt;&lt;br /&gt;然而后面一种做法依赖于成本较高的实现方式，开发者一个不小心（或者摆明偷懒）就会漏掉setTimeout，导致函数调用顺序变得不确定，所以我们会希望这是框架帮助实现的功能，在使用框架时无法把这绕过。&lt;h4&gt;场景&lt;/h4&gt;在这里，我举一个关于上述问题的具体应用场景。（为简化问题，描述已略作修改，与实际应用并不一致。）&lt;br /&gt;&lt;br /&gt;在&lt;a href="http://web.im.baidu.com/" target="_blank"&gt;百度Hi网页版&lt;/a&gt;里面，我们会在客户端保存一个用户对象列表，在打开和这个用户的聊天窗口时，我们需要从中读取这个用户的信息。这个操作就涉及很多可能同步又可能异步的分支：&lt;ul&gt;&lt;li&gt;用户对象未缓存&lt;ul&gt;&lt;li&gt;异步读取用户信息&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;用户对象已缓存&lt;ul&gt;&lt;li&gt;用户是好友（信息更新会由服务器端推送）&lt;ul&gt;&lt;li&gt;同步读取用户信息&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;用户不是好友（信息更新需要由客户端拉取）&lt;ul&gt;&lt;li&gt;可以接受缓存信息&lt;ul&gt;&lt;li&gt;同步读取用户信息&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;必须获取最新信息&lt;ul&gt;&lt;li&gt;异步读取用户信息&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;可以看到，分支的结果最终既有同步的，也有异步的。并且这些分支还不是在一个函数里完成，而是在几个函数里实现。也就是说，按照传统的模式，这些函数一部分是同步的，一部分是异步的，由于异步的传递性，最终调用栈顶层的函数都是异步的。&lt;br /&gt;&lt;br /&gt;为了解决这个问题，我们需要写一个异步调用框架，用一种统一的方式来进行调用，把同步和异步调用都合并为一种返回方式。&lt;br /&gt;&lt;br /&gt;具体的解决方案会在下一篇文章中给出，如果你不希望错过的话，欢迎订阅我的博客：&lt;ul&gt;&lt;li&gt;&lt;a href="http://chinese.catchen.biz/"&gt;Cat in Chinese&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dotnet.catchen.biz/"&gt;Cat in dotNET&lt;/a&gt; (&lt;a href="http://feedproxy.google.com/CatChen/dotNET"&gt;feed&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-8630630787516835659?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/nUJ82I3hUP5CrzfQrHU-Pvxobts/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/nUJ82I3hUP5CrzfQrHU-Pvxobts/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/nUJ82I3hUP5CrzfQrHU-Pvxobts/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/nUJ82I3hUP5CrzfQrHU-Pvxobts/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pw75-lXPdIU:T9AY3bwrV4s:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pw75-lXPdIU:T9AY3bwrV4s:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pw75-lXPdIU:T9AY3bwrV4s:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=pw75-lXPdIU:T9AY3bwrV4s:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pw75-lXPdIU:T9AY3bwrV4s:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pw75-lXPdIU:T9AY3bwrV4s:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/pw75-lXPdIU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/8630630787516835659/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=8630630787516835659" title="1 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8630630787516835659?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8630630787516835659?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/pw75-lXPdIU/javascript-part-1.html" title="写个 JavaScript 异步调用框架 (Part 1 - 问题 &amp;amp; 场景)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/05/javascript-part-1.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE8FQ348eCp7ImA9WxVaFUw.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-6525023023684770507</id><published>2009-04-12T14:13:00.001+08:00</published><updated>2009-04-12T14:13:32.070+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-12T14:13:32.070+08:00</app:edited><title>豆瓣的『请勿联系我们』页面</title><content type="html">如果说&lt;a href="http://www.amazon.com/Think-Common-Sense-Approach-Usability/dp/0789723107"&gt;&lt;em&gt;Don't Make Me Think&lt;/em&gt;&lt;/a&gt;的基本思想是让用户凭直觉都能找到他们需要的东西，那么豆瓣的&lt;a href="http://www.douban.com/about?topic=contactus"&gt;联系我们&lt;/a&gt;页面就做了一个绝佳的例子，或者说是绝佳的反例，这视乎你怎么看这个问题。&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/45201686@N00/3432974569" title="View '联系豆瓣 - Mozilla Firefox (Build 2009032608)' on Flickr.com"&gt;&lt;img src="http://static.flickr.com/3549/3432974569_ec0921882c.jpg" alt="联系豆瓣 - Mozilla Firefox (Build 2009032608)" border="0" width="" height="" /&gt;&lt;/a&gt;&lt;h4&gt;联系我们&lt;/h4&gt;还是用&lt;em&gt;Don't Make Me Think&lt;/em&gt;里面的比喻，在网站上寻找内容就如同在商店里寻找商品，最理想的情况自然是一抬头就看到导向牌说你想要的商品在哪一行的哪一个货架上。在这方面，豆瓣的联系我们页面是做得非常好的，通过清晰的分类说明了不同情况应该联系不同的邮件地址。就如同清晰的导向牌一样，你能够径直走到摆满你想要的商品的货架前。&lt;br /&gt;&lt;br /&gt;接着搞笑的事情发生了，你发现这个货架其实是个锁上了的玻璃橱窗，上面有一个牌子写着『如需取商品，请找售货员』。这时候你会想，这不是一家便利店吗？还是有什么我理解错了？这就是豆瓣的联系我们页面给我的感觉，因为那些邮件地址不仅仅不可以点击，还刻意设计为图片使得不能够复制。&lt;br /&gt;&lt;br /&gt;如果你跟我一样，习惯有什么想法直接点击网站的联系我们链接，你应该明白常见的联系我们页面是怎样的。网站往往为了保护邮件地址免受垃圾邮件骚扰而不会直接公开邮件地址，为此他们会设计一个专门的表单让你提交反馈信息并留下你的联系方式。随后如果你提交的信息确实有价值，网站会主动联系你，之后的通信就都可以在邮件里进行了。&lt;br /&gt;&lt;br /&gt;这是用户对联系我们页面的印象，就如同对便利店的印象一样——自由地在货架前选取商品然后再去埋单。然而豆瓣选择了在便利店里放置锁上了的玻璃橱窗，这就会导致用户迷失方向，他们会想『这是一家便利店吗？还是说我走进了一家「珠宝便利店」？那算了，我就不买了，或者到附近的便利店买吧。』&lt;h4&gt;帮助中心&lt;/h4&gt;这个联系我们页面上面，只有一个链接可点击（除去全局链接外），那就是&lt;a href="http://www.douban.com/help/"&gt;帮助中心&lt;/a&gt;链接了。用户找不到熟悉的反馈表单，自然会凭直觉沿着链接点击下去，而这个链接是唯一的选择。进去之后仍然是一个清晰分类的页面，不过就是没有用户需要的反馈表单。或许用户会浏览一下分类列表，然后发现他要反馈的问题不能算是严格属于某个特定分类。这时候就如同顾客根据导向牌走进了唯一一行他认为正确的货架，但是货架上的分类标识他想要的东西不属于这里的任何一个货架。&lt;br /&gt;&lt;br /&gt;这又是一个让用户感受挫折的地方，然而这还不是最后一个。如果用户坚信反馈表单存在，就如同顾客坚信这是每一家便利店都卖的东西一样，那么他们自然会继续找下去。在点击进入任何一个分类链接后，用户会看到又一个清晰的Q&amp;A列表，然后他必须再一次经历同样的挫折，最后才在一个不显眼的地方找到他想要的东西，一个写着『没搞定？给管理员捎个话』的链接。&lt;br /&gt;&lt;br /&gt;为什么说这个链接不显眼？因为特殊链接往往会放在页头、页脚或侧栏这类视觉上与内容分离开来的区域，而这个链接放在问题列表和解答列表之间的狭缝。这处的留白本应只用于表示两个列表互相对立的关系，在两个块区元素之间插入一个行内元素只会让人以为那也是分隔符的一部分而已。&lt;h4&gt;小结&lt;/h4&gt;这就是为什么我说豆瓣的『联系我们』页面应该叫做『请勿联系我们』页面。它在向用户传达一种意思，那就是『我们豆瓣是刻意增加你联系我们的成本的，因为我们就是不欢迎你联系我们。』这就好比说，我们都知道每一家7-11都会在cashier前有一个放满了condom的架子，而豆瓣是一家和特别的7-11，它在那里放了一个牌子然后写上『我们就是不卖condom，你就不要找了，也不要问我们的售货员』。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-6525023023684770507?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/No91mdczfPL88mZhappS8TrDkGs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/No91mdczfPL88mZhappS8TrDkGs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/No91mdczfPL88mZhappS8TrDkGs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/No91mdczfPL88mZhappS8TrDkGs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=KOS2BTszuC8:cgdhQU5cTxI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=KOS2BTszuC8:cgdhQU5cTxI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=KOS2BTszuC8:cgdhQU5cTxI:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=KOS2BTszuC8:cgdhQU5cTxI:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=KOS2BTszuC8:cgdhQU5cTxI:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=KOS2BTszuC8:cgdhQU5cTxI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/KOS2BTszuC8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/6525023023684770507/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=6525023023684770507" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/6525023023684770507?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/6525023023684770507?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/KOS2BTszuC8/blog-post.html" title="豆瓣的『请勿联系我们』页面" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/04/blog-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EFR3k9fyp7ImA9WxVbE04.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-1070750174860953923</id><published>2009-03-29T23:13:00.001+08:00</published><updated>2009-03-29T23:13:36.767+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-29T23:13:36.767+08:00</app:edited><title>Microsoft MVP Global Summit 2009 （Part 2 - Sessions)</title><content type="html">这次去参加MVP Summit，我计划主要是听ASP.NET 4.0及Silverlight 3有关的session，结果在Microsoft Campus的两天也就泡在&lt;a href="http://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=microsoft+conference+center&amp;sll=47.826064,-122.063599&amp;sspn=0.379866,1.268921&amp;ie=UTF8&amp;ll=47.642232,-122.12574&amp;spn=0,359.960346&amp;t=h&amp;z=15&amp;layer=c&amp;cbll=47.642226,-122.125878&amp;panoid=1_3x0exrmtMDTB_tXwI7PA&amp;cbp=12,31.607595721512496,,0,0.03246753246752945"&gt;MSCC&lt;/a&gt;，也就是往返于Hood和Rainier这两个room。这些内容之前一直都不能说，现在MIX09开完之后，大部分的内容我们都可以自由讨论了。&lt;h4&gt;ASP.NET 4.0&lt;/h4&gt;ASP.NET 4.0方面，我最关注的是ASP.NET AJAX 4.0，不过Preview 4和Preview 3比起来没多少新增功能，我只能希望在Preview 3中存在的bug在Preview 4都修复了。在demo方面，DataView控件加上Live Binding确实非常cool，可以一个DataView做master另一个做details，在其中任何一处作出的修改立即同步到所有地方，包括对ADO.NET Data Service作出更新请求。不过demo始终就只是demo，现实中的场景总有一些更复杂更需要创意的地方。&lt;br /&gt;&lt;br /&gt;此外，ASP.NET 4.0还包括ASP.NET MVC和Dynamic Data，前者终于发布了1.0，后者比最初我看到的第一个Preview已经要强大很多了，当然也换上了Entity Model这样的数据访问层组件。&lt;h4&gt;Silverlight 3&lt;/h4&gt;我对Silverlight 3的关注原本只是很表面的，但却发现Silverlight 3的新增功能比ASP.NET 4.0要多得多，这意味着Microsoft对Silverlight的投入巨大吧，因此我也觉得有必要去好好研究Silverlight 3了。&lt;br /&gt;&lt;br /&gt;Silverlight 3的突破是巨大的，假如我们都承认原来的Silverlight 2其实很弱的话。在工具方面，现在终于可以在Visual Studio 2010里面拖放编辑XAML了，而Expression Blend也支持C#编辑了，不再像过去的版本那样，Visual Studio和Blend各自只能完成一半的工作，必须在两个工具之间切换来切换去。&lt;br /&gt;&lt;br /&gt;视频播放一直都是Silverlight非常重视的功能之一，Silverlight 3将会支持H.264，并把H.264/AAC/MP4作为标准的视频封装格式。如今有越来越多的设备支持H.264的硬件解码，而且Adobe与Apple的软件也广泛采用H.264作为高清视频的编码格式，所以将来我们无需再为编码格式的区别而烦恼了。&lt;br /&gt;&lt;br /&gt;另外，Silverlight 3还引入了GPU硬件加速的支持，用于UIElement的合成与拉伸，同时支持浏览器内嵌模式与全屏模式。不过，如果你要让你的Silverlight 3应用支持GPU加速，还必须在浏览器插件及UIElement两个层面声明启用GPU加速，以保证其他内容不受此新功能引入的影响。&lt;br /&gt;&lt;br /&gt;GPU硬件加速带来的是什么？当然是视觉上的新功能啦。过去Silverlight 2无法做梯形变换，所以也就做不了真正的Coverflow。现在Silverlight 3可以做Perspective 3D了，Coverflow也就能实现了。另外WPF拥有的Effects在Silverlight 3中也能用了，虽然Silverlight 3现在的Preview仅仅自带了两个Effects，但WPF自带的Effects甚至是第三方的Effects都可以导入到Silverlight 3中使用，因为它们都是用HLSL写的Pixel Shaders。&lt;br /&gt;&lt;br /&gt;如果你对这些新功能感兴趣，我建议你去看看&lt;a href="http://sessions.visitmix.com/MIX09/"&gt;MIX 09的视频&lt;/a&gt;，里面涵盖了我所说到的所有内容。我准备接下来用Silverlight 3做一个Coverflow，如果你有兴趣了解这是怎么做的，欢迎&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;订阅Cat in Chinese&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-1070750174860953923?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Ksm8RNGm8suTq4mrewGKTnAo1qQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ksm8RNGm8suTq4mrewGKTnAo1qQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Ksm8RNGm8suTq4mrewGKTnAo1qQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ksm8RNGm8suTq4mrewGKTnAo1qQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=wKrenTqogPo:QCXdqdjNEbA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=wKrenTqogPo:QCXdqdjNEbA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=wKrenTqogPo:QCXdqdjNEbA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=wKrenTqogPo:QCXdqdjNEbA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=wKrenTqogPo:QCXdqdjNEbA:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=wKrenTqogPo:QCXdqdjNEbA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/wKrenTqogPo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/1070750174860953923/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=1070750174860953923" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/1070750174860953923?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/1070750174860953923?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/wKrenTqogPo/microsoft-mvp-global-summit-2009-part-2.html" title="Microsoft MVP Global Summit 2009 （Part 2 - Sessions)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/03/microsoft-mvp-global-summit-2009-part-2.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A04ERH4zeip7ImA9WxVUF04.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-7104862254563699323</id><published>2009-03-23T00:38:00.001+08:00</published><updated>2009-03-23T00:38:25.082+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-23T00:38:25.082+08:00</app:edited><title>Microsoft MVP Global Summit 2009 （Part 1 - Trip &amp; Food)</title><content type="html">虽然MVP Summit发生在3月初，但由于我一直都很懒，所以现在才来写写文章。而且，很多MVP Summit上讨论的受NDA保护的内容，过了MIX09也就成为公开内容了，我也就可以在这里说说了。&lt;br /&gt;&lt;br /&gt;今年买票的时候，竟然找不到第二个人跟我同行。上海的一群MVP，都为了省钱买了海航的机票，从上海飞到北京再直飞西雅图。我还是更愿意飞NWA，因为里程可以积累到我的南航帐号上，所以虽然票价贵一些，我还是选择了一个人飞NWA。直到起飞前两天，才确定上海的包包跑来北京了，因为面签时被check了拖到最后一刻才买票，结果就和我同机了。&lt;br /&gt;&lt;br /&gt;飞到东京之后，有趣的事情发生了，我们见到了一群穿着校服的日本女生走进来候机室，登机之后其中一位就坐在我旁边。我在吃完机上第一餐之后，无所事事就开始找她聊天，问问她有没有用过Baidu.jp，然后说说去美国做什么，她说她其实是去Vancouver参加交流活动。因为飞机引擎噪声很大，我们又都不太懂对方的口音，所以我拿了一个小本子出来把英语写下来让她看。在她有不懂表达的意思时，她拿了一个电子词典出来用日文查对应的英文，不过有时候我看看其中的日文解释，上面的汉字也足够我理解了。这样的沟通挺有趣的。&lt;br /&gt;&lt;br /&gt;到了Seattle之后，打车去Sheraton Seattle Hotel，然后还是和去年一样带着今年第一次来的MVP去海边走走，去&lt;a href="http://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=crab+pot+in+seattle&amp;sll=23.127041,113.341527&amp;sspn=0.260163,0.63446&amp;ie=UTF8&amp;ll=47.606069,-122.340596&amp;spn=0,359.990087&amp;t=h&amp;z=17&amp;layer=c&amp;cbll=47.605987,-122.340526&amp;panoid=nf7OPg_ucgXegx2YNQIuLQ&amp;cbp=12,254.33470283211116,,0,3.53896103896104"&gt;Crab Pot&lt;/a&gt;吃蟹。我们要了便宜的snow crab和dungeon crab，没有选最贵的king crab，吃起来感觉还不错，不过snow crab实在是太小了。最后一天晚上也是在Crab Pot吃，就是吃king crab，但这其实不是我们吃过最好的king crab。&lt;br /&gt;&lt;br /&gt;说到这里，就不得不重点推荐一下我们在&lt;a href="http://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=Oceanaire+Seafood+Room%E2%80%8E&amp;sll=47.614279,-122.336824&amp;sspn=0.006046,0.019827&amp;ie=UTF8&amp;ll=47.61545,-122.336819&amp;spn=0,359.980173&amp;z=16&amp;iwloc=A&amp;layer=c&amp;cbll=47.613662,-122.335255&amp;panoid=ybI6heZPOkLJcMS25mV-Ag&amp;cbp=12,56.00923511055042,,0,5"&gt;Oceanaire Seafood Room&lt;/a&gt;吃的那一餐Alaska red king crab了。虽然这里的king crab要$45/pound，而Crab Pot的只要$22/pound，但是这里的king crab确实比较大，而且非常甜！如果你想要尝一尝king crab是有多么的好吃，那就一定要去Oceanaire Seafood Room点Alaska red king crab。一般来说，一个人吃1~2 pounds是没问题的，如果你不再点其他东西的话。面包是送的，但既然是专门来吃king crab的，当然就不再点其他东西了，甚至连面包都不吃。&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/45201686@N00/3376168452" title="View 'Alaska Red king crab' on Flickr.com"&gt;&lt;img src="http://farm4.static.flickr.com/3556/3376168452_1766b5b121_m.jpg" alt="Alaska Red king crab" border="0" width="240" height="180" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;美国人吃法比较奇怪的地方在于，他们用石头那么硬的面包来搭配甜美的king crab。不仅仅这样，他们还会上热好了的黄油，让你点king crab来吃。我试了一下，发现这一点都不好吃，原本爽口的蟹肉变得油油的，感觉很奇怪。&lt;br /&gt;&lt;br /&gt;今年MVP Summit的开场中说到去年MVP Summit抱怨最厉害的事情，那就是salmon吃得太多了，每餐都有salmon，所以今年的口号是&lt;em&gt;No Salmon&lt;/em&gt;。我自然很失望啦，Seattle作为一个salmon产量如此多的地方，我过来自然要吃salmon啊，怎么可以没salmon呢？结果后来几天真的很多人在Twitter上抱怨&lt;em&gt;No Salmon&lt;/em&gt;，于是乎今年最大的抱怨也就正好与去年相反了。看来做如此大的一场活动，关于吃什么的问题真是众口难调哦。&lt;br /&gt;&lt;br /&gt;有关MVP Summit旅行与饮食的话题就说到这里吧，如果你有兴趣了解我接下来要说的技术内容，欢迎&lt;a href="http://feedproxy.google.com/CatChen/Chinese"&gt;订阅Cat in Chinese&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-7104862254563699323?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/_paRitJK7ZUfflaEs-rZalFiX-w/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_paRitJK7ZUfflaEs-rZalFiX-w/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/_paRitJK7ZUfflaEs-rZalFiX-w/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_paRitJK7ZUfflaEs-rZalFiX-w/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pC3qtfScRNY:aoFx9nPI4uY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pC3qtfScRNY:aoFx9nPI4uY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pC3qtfScRNY:aoFx9nPI4uY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=pC3qtfScRNY:aoFx9nPI4uY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pC3qtfScRNY:aoFx9nPI4uY:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=pC3qtfScRNY:aoFx9nPI4uY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/pC3qtfScRNY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/7104862254563699323/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=7104862254563699323" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/7104862254563699323?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/7104862254563699323?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/pC3qtfScRNY/microsoft-mvp-global-summit-2009-part-1.html" title="Microsoft MVP Global Summit 2009 （Part 1 - Trip &amp;amp; Food)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/03/microsoft-mvp-global-summit-2009-part-1.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4HQHk_fyp7ImA9WxVUEE0.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-526804060764137712</id><published>2009-03-14T11:37:00.004+08:00</published><updated>2009-03-14T12:28:51.747+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-14T12:28:51.747+08:00</app:edited><title>ASP.NET AJAX 4.0 Preview 3 (Part 2 - ASP.NET AJAX Template)</title><content type="html">在上一篇文章里，我们说到了如何使用ADO.NET Data Service Client Library能够轻松访问到存在服务器端的数据，然而将数据展现出来仍需要人手拼接HTML这点就实在是让人难以接受，所以我们现在就来看看如何利用 ASP.NET AJAX Template解决这个问题。文章中所用到的示例代码，可以在这里下载：&lt;a href="http://files.cnblogs.com/cathsfz/AspNetAjaxPreview.zip"&gt;ASP.NET AJAX 4.0 Preview 3 Demo&lt;/a&gt;，然后参考里面的AspNetAjaxTemplateDemo.aspx。&lt;h4&gt;Sys.UI.DataView&lt;/h4&gt;为了解决展示数据的问题，我们需要用到一个全新的客户端控件，那就是&lt;code&gt;Sys.UI.DataView&lt;/code&gt; 了，简称&lt;code&gt;DataView&lt;/code&gt;。我们会用&lt;code&gt;DataView&lt;/code&gt;替换掉上一篇文章中所说到的人手拼接HTML的部分，用于迭代生成一个ul中的li元素，因此看起来是把&lt;code&gt;DataView&lt;/code&gt;当作&lt;code&gt;Repeater&lt;/code&gt;来用。实际上，&lt;code&gt;DataView&lt;/code&gt;的功能类似于&lt;code&gt;ListView&lt;/code&gt;加上&lt;code&gt;DetailsView&lt;/code&gt;。&lt;br /&gt;&lt;br /&gt;如果你把一个Array绑定到&lt;code&gt;DataView&lt;/code&gt;上，它会显示为一个&lt;code&gt;ListView&lt;/code&gt;。与&lt;code&gt;ListView&lt;/code&gt; 的&lt;code&gt;LayoutTemplate&lt;/code&gt;相类似的是，它也能够定义控件展示的整体布局，并且仅仅是迭代输出其中的一小部分。例如说，编写一个有thead的table，并且仅仅是迭代输出thead之后的tr。在这方面，是&lt;code&gt;DataView&lt;/code&gt;和&lt;code&gt;ListView&lt;/code&gt;完全一致的。唯一不同的是，客户端暂时还没有&lt;code&gt;DataPager&lt;/code&gt;这样的控件，所以&lt;code&gt;DataView&lt;/code&gt;必须一次性的完整显示整个Array的数据。&lt;br /&gt;&lt;br /&gt;如果你把单个Object绑定到&lt;code&gt;DataView&lt;/code&gt;上，它就会显示为一个&lt;code&gt;DetailsView&lt;/code&gt;。这使得你可以使用两个&lt;code&gt;DetailsView&lt;/code&gt;就做出经典的Master-Details展示模式，和在服务器端分别用&lt;code&gt;ListView&lt;/code&gt;和&lt;code&gt;DetailsView&lt;/code&gt;做出来的一样。当然，你不能指望&lt;code&gt;DataView&lt;/code&gt;能够好像&lt;code&gt;DetailsView&lt;/code&gt;那样，自动帮你分析每一个数据项并映射出对应的HTML模板，因此HTML 模板还是要你自己手写的，但至少那是在HTML中编写模板，编写时能够享受IDE带来的各种好处，维护时也更容易看懂自己（或别人）原来写下的 HTML。&lt;h4&gt;JavaScript语法&lt;/h4&gt;接下来，我们就要把&lt;code&gt;DataView&lt;/code&gt;投入到实际应用中去了。首先，我们说一下如何用JavaScript来实例化一个&lt;code&gt;DataView&lt;/code&gt;。有编写ASP.NET AJAX客户端代码经验的人对&lt;code&gt;$create&lt;/code&gt;应该不会觉得陌生，因为&lt;code&gt;DataView&lt;/code&gt;继承自&lt;code&gt;Sys.UI.Control&lt;/code&gt;，我们仍然可以用&lt;code&gt;$create&lt;/code&gt;来实例化它。不过，在此之前，我们先要把对应的 HTML编写好：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;ul id="itemTemplate" class="sysTemplate"&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;li&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;span class="award"&amp;gt;{{ Award }}&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;span class="winner"&amp;gt;{{ Winner }}&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;span class="film"&amp;gt;{{ Film }}&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;然后我们就可以基于itemTemplate这个HTML元素创建控件了：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;$create(Sys.UI.DataView, {&lt;br /&gt;&amp;nbsp; &amp;nbsp; dataSource: new Sys.Data.AdoDataSource(),&lt;br /&gt;&amp;nbsp; &amp;nbsp; serviceUri: "WebDataService.svc",&lt;br /&gt;&amp;nbsp; &amp;nbsp; query: "OscarWinners"&lt;br /&gt;&amp;nbsp; }, {}, {}, $get("itemTemplate"));&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;现在，页面显示出来的结果和之前我们人手拼接HTML的版本完全一致，不过我们已经不在需要维护嵌入在JavaScript中的HTML代码了。&lt;h4&gt;声明性语法&lt;/h4&gt;如果你觉得上面的做法还不够好，要在&lt;code&gt;pageLoad()&lt;/code&gt;里面写一个&lt;code&gt;$create&lt;/code&gt;，那么声明性语法可能正是你需要的。大家应该记得很久很久之前，在ASP.NET AJAX还叫做Atlas的时候，就已经有过声明性语法的设计，那就是xml-script。不知为何，后来Microsoft放弃了这一设计，现在声明性语法又回来了，而且设计得比以前的xml-script还要更好。假如不用&lt;code&gt;$create&lt;/code&gt;的话，通过声明性语法实例化一个&lt;code&gt;DataView&lt;/code&gt;仅需要这样做：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&amp;lt;body&lt;br /&gt;&amp;nbsp; xmlns:sys="javascript:Sys"&lt;br /&gt;&amp;nbsp; xmlns:dataView="javascript:Sys.UI.DataView"&lt;br /&gt;&amp;nbsp; sys:activate="*"&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;ul id="itemTemplate" class="sysTemplate"&lt;br /&gt;&amp;nbsp; &amp;nbsp; sys:attach="dataView"&lt;br /&gt;&amp;nbsp; &amp;nbsp; dataView:datasource="{{ new Sys.Data.AdoNetDataSource() }}"&lt;br /&gt;&amp;nbsp; &amp;nbsp; dataView:serviceuri="WebDataService.svc"&lt;br /&gt;&amp;nbsp; &amp;nbsp; dataView:query="OscarWinners"&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;li&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class="award"&amp;gt;{{ Award }}&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class="winner"&amp;gt;{{ Winner }}&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class="film"&amp;gt;{{ Film }}&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/li&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;/ul&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;我们所需要更改的代码包括：&lt;ol&gt;&lt;li&gt;在body元素上声明有关的xmlns，将JavaScript中的名字空间映射到HTML上，或者你可以理解为映射到XML/XHTML上。 &lt;/li&gt;&lt;li&gt;通过sys:activate="*"这个声明，让ASP.NET AJAX知道它需要去解释页面上所有的声明性语法，并激活对应的组件。 &lt;/li&gt;&lt;li&gt;将原本使用&lt;code&gt;$create&lt;/code&gt;初始化时传递给实例的属性、事件、引用改为用声明性语法，直接写在HTML元素的定义上。&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;经过这三步，我们就可以将原来使用&lt;code&gt;$create&lt;/code&gt;创建的组件改为使用声明性语法创建了。&lt;h4&gt;小结&lt;/h4&gt;&lt;code&gt;DataView&lt;/code&gt; 使得我们能够使用HTML模板，来避免手工拼接HTML带来的种种问题，同时声明性语法让我们能够如同声明服务器端控件一样声明客户端组件。虽然在 ASP.NET AJAX 4.0 Preview 3中这些功能仍有小bug，例如我想用声明性语法创建我自己编写的&lt;code&gt;InPlaceEditoBehavior&lt;/code&gt;，这在初始化阶段毫无问题，但却会在页面卸载销毁对象之时抛出脚本错误。&lt;br /&gt;&lt;br /&gt;由于我觉得ASP.NET AJAX 4.0 Preview 4很快就要出来了，所以我也就不准备去深入研究Preview 3了，等Preview 4出来了再去好好看看源代码。如果你有兴趣关注这方面的技术文章，欢迎订阅我的博客，点击侧栏上的订阅链接就可以了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-526804060764137712?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/cCQvYeG_-QYky6VE1ujBJUzhA68/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cCQvYeG_-QYky6VE1ujBJUzhA68/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/cCQvYeG_-QYky6VE1ujBJUzhA68/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cCQvYeG_-QYky6VE1ujBJUzhA68/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=W0I9RGNjARc:-KdgOe_LoQY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=W0I9RGNjARc:-KdgOe_LoQY:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=W0I9RGNjARc:-KdgOe_LoQY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=W0I9RGNjARc:-KdgOe_LoQY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=W0I9RGNjARc:-KdgOe_LoQY:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=W0I9RGNjARc:-KdgOe_LoQY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/W0I9RGNjARc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/526804060764137712/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=526804060764137712" title="1 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/526804060764137712?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/526804060764137712?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/W0I9RGNjARc/aspnet-ajax-40-preview-3-part-2-aspnet.html" title="ASP.NET AJAX 4.0 Preview 3 (Part 2 - ASP.NET AJAX Template)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/03/aspnet-ajax-40-preview-3-part-2-aspnet.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8DQHY7eSp7ImA9WxVUEE0.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-2074738250302440689</id><published>2009-03-13T16:22:00.002+08:00</published><updated>2009-03-14T11:54:31.801+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-14T11:54:31.801+08:00</app:edited><title>ASP.NET AJAX 4.0 Preview 3 (Part 1 - ADO.NET Data Service Client Library)</title><content type="html">自从Microsoft与jQuery合作以来，ASP.NET AJAX与jQuery就被定位为两个互补的AJAX库。既然jQuery已经实现了如此多轻量级的AJAX特性，自然ASP.NET AJAX会继续专注于富客户端所需的一些重量级特性。&lt;br /&gt;&lt;br /&gt;在ASP.NET AJAX 4.0 Preview 3里面，开发人员能够接触到的两个重要的新特性就是ADO.NET Data Service Client Library以及ASP.NET AJAX Template。对于熟悉ASP.NET服务器端开发但不熟悉客户端开发的人来说，你可以简单地把这两个特性理解为存在于客户端的DataSource 以及ListView，只要把数据通过ADO.NET Data Service输出到前端，你就可以如同使用DataSource和ListView的组合一样在客户端开发数据驱动的应用程序了。&lt;br /&gt;&lt;br /&gt;在这篇文章里，我们将来看看如何使用ADO.NET Data Service Client Library，将ADO.NET Data Service暴露的REST数据接口直接拿到客户端JavaScript代码中去调用。文章中所用到的示例代码，可以在这里下载：&lt;a href="http://files.cnblogs.com/cathsfz/AspNetAjaxPreview.zip"&gt;ASP.NET AJAX 4.0 Preview 3 Demo&lt;/a&gt;，然后参考里面的AdoNetDataServiceDemo.aspx。&lt;h4&gt;服务器端准备工作&lt;/h4&gt;在我们接下来要讲到的示例当中，我们会用到一个SQL Server 2005 Express Edition的数据库，里面有一张名为OscarWinners的表，记录的是本年度奥斯卡获奖名单，字段包括AwardID、Award、 Winner、Film。然后我们为这张表创建ADO.NET Entity Model，接着再为它生成的实体类创建ADO.NET Data Service。这些都是在Visual Studio 2008中点几下鼠标就能完成的操作，就不再详细解释了。在ADO.NET Data Service的InitializeService()方法内，我们仅仅给它提供一个最宽松的规则：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;config.SetEntitySetAccessRule("*", EntitySetRights.All);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;到这里，我们就把服务器端的要做的工作都准备好了。打开你创建的ADO.NET Data Service地址，看看是否输出了正确的Atom格式数据。如果没有，请检查一下你机器上的WCF是否已经正确安装和配置好了。确保服务器端的准备工作都做好了，然后再进入客户端的开发工作。&lt;h4&gt;连接Data Service&lt;/h4&gt;在客户端使用ADO.NET Data Service，我们需要接触到的类只有一个，那就是&lt;code&gt;Sys.Data.AdoNetServiceProxy&lt;/code&gt;。首先，我们要连接到ADO.NET Data Service，也就是使用ADO.NET Data Service的URL来实例化此类：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;var dataService = new Sys.Data.AdoNetServiceProxy("WebDataService.svc");&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;然后，我们就可以利用&lt;code&gt;dataService&lt;/code&gt;来调用ADO.NET Data Service进行CRUD操作了。&lt;h4&gt;CRUD操作&lt;/h4&gt;所有的CRUD操作都在&lt;code&gt;Sys.Data.AdoNetServiceProxy&lt;/code&gt;对象上执行，方法分别名为&lt;code&gt;query()&lt;/code&gt;、&lt;code&gt;insert()&lt;/code&gt;、&lt;code&gt;update()&lt;/code&gt;、&lt;code&gt;remove()&lt;/code&gt;。在我们的示例当中，会用到&lt;code&gt;query()&lt;/code&gt; 和&lt;code&gt;update()&lt;/code&gt;方法，另外两个方法是用起来和&lt;code&gt;update()&lt;/code&gt;很类似，就不再详细说明了。&lt;h5&gt;查询操作&lt;/h5&gt;&lt;code&gt;dataService.query("OscarWinners", function(result, context, operation) {&lt;br /&gt;&amp;nbsp; /* display result */&lt;br /&gt;}, errorHandler);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt; 使用上述语句，我们查询出了OscarWinners表中的所有数据。随后的第一个回调函数会在查询成功时被调用，因此我们可以在其中编写拼接HTML以显示结果的逻辑，具体的代码请参考下载中的AdoNetDataServiceDemo.aspx。第二个回调函数会在查询失败时被调用，我们可以编写一个统一的错误处理函数，名为errorHandler，然后将它传递给此参数。&lt;br /&gt;&lt;br /&gt;如果需要传递复杂的查询参数，使用ADO.NET Data Service的格式就可以了，这可以在&lt;a href="http://msdn.microsoft.com/zh-cn/library/cc907912%28en-us%29.aspx"&gt;MSDN&lt;/a&gt;上查到。例如说查询&lt;em&gt;Slumdog Millionaire&lt;/em&gt;这部电影夺取了多少个奥斯卡奖项，然后把奖项按照名称排序输出，可以这样子写：&lt;br /&gt;&lt;br /&gt;&lt;code&gt;dataService.query("OscarWinners?$filter=Film eq 'Slumdog Millionaire'&amp;amp;$orderby=Award", function(result, context, operation) {&lt;br /&gt;&amp;nbsp; /* display result */&lt;br /&gt;}, errorHandler);&lt;/code&gt;&lt;h5&gt;更新操作&lt;/h5&gt;&lt;code&gt;dataService.update(item, function(result, context, operation) { }, errorHandler);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;尽管将查询结果保存下来成为items集合，并且根据用户在界面上执行的操作修改item上的属性，这些逻辑都需要我们手动维护，然而最后将item更新到服务器上则只需要如此简单的一句调用。&lt;br /&gt;&lt;br /&gt; 在我给出的示例代码中，我自己写了一个InPlaceEditBehavior，也就是所谓的“就地编辑器”，能够让用户点击显示文本后把显示文本变成输入框。然后我把这个InPlaceEditBehavior绑定到每一条记录显示的Winner字段和Film字段的span上，使得这些span都能接收用户输入。最后，我为InPlaceEditBehavior添加了一个onchanged事件，并在该事件的处理函数中完成更新item以及调用&lt;code&gt;update()&lt;/code&gt;的操作。&lt;h4&gt;小结&lt;/h4&gt;在这篇文章里，我简单地介绍了ADO.NET Data Service Client Library的易用性，并且通过一个具体的示例说明了如何用它来节省大量的数据交互代码。&lt;br /&gt;&lt;br /&gt; 如果你曾经写过AJAX-Enabled WCF Service，你应该知道把实体类暴露为WCF Service接口是多么麻烦的事情，就算每个实体类就简单地支持CRUD方法，你也必须手动编写这4个方法。ADO.NET Data Service相当于帮你把这一切都做好了，只要给它实体类和规则，它就帮你生成一个Data Service。另外，通过AJAX-Enabled WCF Service所包括的数据接口，会自动生成一大堆客户端代理类，而ADO.NET Data Service Client Library则只有一个固定的代理类，客户端代码体积不会随着接口复杂度的增加而增加。&lt;br /&gt;&lt;br /&gt;说了 ADO.NET Data Service Client Library的那么多好处，那么这个示例中又有什么做得不够好的地方呢？我觉得最难维护的地方就是获取到数据后拼接HTML的代码了，人手写的HTML 拼接代码难免容易出错，而且日后更新起来也很麻烦，出错了调试时也不容易定位问题。ASP.NET AJAX Template能够帮助我们解决这个问题，这就是下一篇文章中将会讲到的内容。&lt;br /&gt;&lt;br /&gt;如果你不想错过接下来的文章，欢迎订阅我的博客，点击侧栏上的订阅链接就可以了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-2074738250302440689?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/-KpIrzp5XxzBXHu-v_PfDX-Ca_Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-KpIrzp5XxzBXHu-v_PfDX-Ca_Q/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/-KpIrzp5XxzBXHu-v_PfDX-Ca_Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-KpIrzp5XxzBXHu-v_PfDX-Ca_Q/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=WpT7Py_-qp0:DGI4ykIdNhc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=WpT7Py_-qp0:DGI4ykIdNhc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=WpT7Py_-qp0:DGI4ykIdNhc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=WpT7Py_-qp0:DGI4ykIdNhc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=WpT7Py_-qp0:DGI4ykIdNhc:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=WpT7Py_-qp0:DGI4ykIdNhc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/WpT7Py_-qp0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/2074738250302440689/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=2074738250302440689" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/2074738250302440689?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/2074738250302440689?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/WpT7Py_-qp0/aspnet-ajax-40-preview-3-part-1-adonet.html" title="ASP.NET AJAX 4.0 Preview 3 (Part 1 - ADO.NET Data Service Client Library)" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/03/aspnet-ajax-40-preview-3-part-1-adonet.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkANRHs5cCp7ImA9WxVVGU4.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-5701042976445821380</id><published>2009-03-13T15:53:00.001+08:00</published><updated>2009-03-13T15:53:15.528+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-13T15:53:15.528+08:00</app:edited><title>如何购买 Amazon Kindle 书籍</title><content type="html">在美国旅行时，无聊地在iPhone上装了&lt;a href="http://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/browserRedirect?url=itms%253A%252F%252Fax.itunes.apple.com%252FWebObjects%252FMZStore.woa%252Fwa%252FviewSoftware%253Fid%253D302584613%2526mt%253D8"&gt;Kindle for iPhone&lt;/a&gt;，然后挑了几本技术书的sample来看看，发现在iPhone上这样看电子书还是挺方便的，就是有些代码块不能自动放大到正常显示字体看起来有点辛苦。我觉得Amazon的sample还是做得挺大方的，每本书都开放了整个chapter 1，技术书的话基本上看chapter 1就能看出作者讨论问题的深度。&lt;br /&gt;&lt;br /&gt;我利用旅行的时间，看完了&lt;a href="http://www.amazon.com/LINQ-Unleashed/dp/B001GSTO6G/ref=sr_1_1?ie=UTF8&amp;s=digital-text&amp;qid=1236929628&amp;sr=1-1"&gt;LINQ Unleashed&lt;/a&gt;和&lt;a href="http://www.amazon.com/Pro-LINQ-Language-Integrated-Query/dp/B001CD1X7S/ref=sr_1_1?ie=UTF8&amp;s=digital-text&amp;qid=1236929568&amp;sr=1-1"&gt;Pro LINQ&lt;/a&gt;的sample，然后决定买一本来学习一下LINQ。我个人更倾向于买Pro LINQ，因为它在chapter 1就提到了deferred query这样的概念，应该是一个本深入讨论LINQ内部技术的书籍。不过我还是看了一下Amazon上的评分，发现两本都是5星的，但Pro LINQ有32个review，而Linq Unleashed只有4个，于是决定就买Pro LINQ。&lt;br /&gt;&lt;br /&gt;开头以为买本Kindle书就是直接刷卡那么简单的事情，结果发现Kindle书是有区域限制的，只能发送到美国。但是我上次买LEGO Mindstorm NXT也是这样买啊，中国的信用卡加美国的送货地址就可以了，不允许送到美国之外的东西就让别人在美国待我签收。神奇的是，Kindle书是只能1-Click购买的，不能在购买时填写送货地址，那Amazon凭什么说我就是在美国之外呢？虽然我禁用了1-Click购买，但保留在1-Click购买的默认地址是美国地址，真是想不明白。&lt;br /&gt;&lt;br /&gt;在网上看了很多资料，别人都是可以在美国境外购买Kindle书的，我想来想去也不明白Amazon凭什么判定不能把Kindle书卖给我。本来我不想把帐号上记录了的信用卡信息和地址信息删除的，这样将来要用时又要重新输入，最后实在想不到办法了，决定把这些信息都删掉！接着点1-Click购买，Amazon重新问我要了一个地址，我填了一个美国地址，就购买成功了。&lt;br /&gt;&lt;br /&gt;另外，大家都说只能用中国的信用卡购买Amazon Gift Card，再用Gift Card买Kindle书，我也不知道是不是这样，反正我是这样买的。一开始Amazon提示区域限制时，我就怀疑是信用卡的区域问题，所以就去买了Gift Card。能否不用Gift Card直接购买我还不知道，下次可以这样试试。&lt;br /&gt;&lt;br /&gt;最后，Amazon是有iPhone版页面的，但它默认打开的购买Kindle书页面缺不是iPhone版的。如果在iPhone版页面上搜索Kindle书，结果能显示出来，然而旁边会写着『This item is currently unavailable』，并且点击进去就会说找不到。只能说，Kindle for iPhone确实还没完全准备好，配套的购买功能都还做好针对iPhone的优化。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-5701042976445821380?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/6_YnrjSnS84n_dvwDVlI6txT84M/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/6_YnrjSnS84n_dvwDVlI6txT84M/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/6_YnrjSnS84n_dvwDVlI6txT84M/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/6_YnrjSnS84n_dvwDVlI6txT84M/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Dbnvts_9Fyo:tcZ79x2CmgE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Dbnvts_9Fyo:tcZ79x2CmgE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Dbnvts_9Fyo:tcZ79x2CmgE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?i=Dbnvts_9Fyo:tcZ79x2CmgE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Dbnvts_9Fyo:tcZ79x2CmgE:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/Chinese?a=Dbnvts_9Fyo:tcZ79x2CmgE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/Chinese?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/Dbnvts_9Fyo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/5701042976445821380/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=5701042976445821380" title="2 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/5701042976445821380?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/5701042976445821380?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/Dbnvts_9Fyo/amazon-kindle.html" title="如何购买 Amazon Kindle 书籍" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/03/amazon-kindle.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE8EQH49eCp7ImA9WxVWE00.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-323318083707605102</id><published>2009-02-22T20:46:00.001+08:00</published><updated>2009-02-22T20:46:41.060+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-22T20:46:41.060+08:00</app:edited><title>Beijing Open Party</title><content type="html">这是我第一次参加&lt;a href="http://www.beijing-open-party.org/"&gt;Beijing Open Party&lt;/a&gt;，主要是为了了解&lt;a href="http://en.wikipedia.org/wiki/Unconference"&gt;unconference&lt;/a&gt;的具体组织形式，其次是想谈谈&lt;a href="http://chinese.catchen.biz/2008/12/gmp-party-ep2.html"&gt;GMP Party与Beijing Open Party合作&lt;/a&gt;的事情。&lt;br /&gt;&lt;br /&gt;下午1:00，我们到达了东直门的ThoughtWorks办公室，门口的签到挺方便的——假如曾经在网上报名或者参与过过往活动，输入名字后其他信息就会自动显示出来。然后进入了ThoughtWorks的办公区域——一个无阻隔的大hall，四周除了窗户就是涂鸦墙，水房仅仅用吧台分隔开来，只有娱乐室是用墙壁分隔的。这样的办公区域感觉挺好的，特别是用来做各种创意活动的话，但是如果想要专心coding，不知道会不会很容易受周围讨论的影响。无论如何，这样的地方用来举办Open Party倒是不错的。&lt;br /&gt;&lt;br /&gt;&lt;object width="480" height="295"&gt;&lt;param name="movie" value="http://www.youtube.com/v/v-ZpGNGFpoU&amp;hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/v-ZpGNGFpoU&amp;hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="295"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;1:30开场之后，先有30分钟的『调侃5分钟』活动，任何人都能够上去做个简单的分享或者广告，每个人的时间限制在5分钟之内。有些人上去介绍了一下自己的网站或者开源项目，也有些人上去分享了一些想法和工作成果。这种形式的分享在同一专业人群内总是很有效率的，不需要演讲者有非常优秀的演讲技能把专业内容转化为适合大众口味的内容或调动听众情绪，只要能把专业内容正确表达出来，听众们一定能听懂，并且也知道哪些内容是更能体现演讲者的技术深度。&lt;br /&gt;&lt;br /&gt;&lt;object width="480" height="295"&gt;&lt;param name="movie" value="http://www.youtube.com/v/VijqU1OhBAo&amp;hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/VijqU1OhBAo&amp;hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="295"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;在『调侃5分钟』结束后，正式开始对话题的投票，虽然不限制每个人的投票次数，不过还是鼓励大家只投真正想听的话题，以便把话题分散开来，人人都能听到自己想听的，而对能拿到奖品的top 3话题评价也更准确一些。投票完毕，根据4 tracks * 3 periods选出了12个话题，然后就正式进入话题演讲阶段了，其中每个period为一个小时。&lt;br /&gt;&lt;br /&gt;我一直留在大厅听较为大众化的话题，偶尔去各个会议室看看其他小众话题是如何进行的。由于这次是Mozilla赞助的活动，所以有3个Mozilla相关的话题，其中第一个是在大厅的&lt;a href="http://blog.mozilla.com/seth/"&gt;Seth&lt;/a&gt;演讲，讲的是Mozilla Community Localization。&lt;br /&gt;&lt;br /&gt;随后还有图灵的书友会以及CC商业模式介绍的话题，讲得都不错。比较可惜的是，到后期饮料都被拿光了，剩下很多曲奇却没有人吃。&lt;br /&gt;&lt;br /&gt;总的来说，Beijing Open Party确实挺geeky的，不过放在大厅的话题还算是大众化，而且演讲的质量也挺高的，所以即使没有哪个小众话题吸引你，也可以在大厅听听，或者走到旁边跟其他人聊聊天。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-323318083707605102?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/PM5Xgholc5zLZE7RRt0Tp1o0eFY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PM5Xgholc5zLZE7RRt0Tp1o0eFY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/PM5Xgholc5zLZE7RRt0Tp1o0eFY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PM5Xgholc5zLZE7RRt0Tp1o0eFY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=CjPSHxY5"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=zdodpwcH"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=68PuFX0Q"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=68PuFX0Q" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=SOEoRkk5"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=0Ezcehvk"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/E8CMxsN84Pk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/323318083707605102/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=323318083707605102" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/323318083707605102?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/323318083707605102?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/E8CMxsN84Pk/beijing-open-party.html" title="Beijing Open Party" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/02/beijing-open-party.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkUFSX04eCp7ImA9WxVXFk0.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-8152805409341596174</id><published>2009-02-14T16:24:00.001+08:00</published><updated>2009-02-14T16:30:18.330+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-14T16:30:18.330+08:00</app:edited><title>Facebook News Feed 上的内容强调与弱化</title><content type="html">我之前曾经思考过&lt;a href="http://chinese.catchen.biz/2007/12/facebook-news-feed.html"&gt;Facebook News Feed的价值&lt;/a&gt;，觉得News Feed中良好的信息筛选与排序算法是我喜欢使用Facebook的原因之一。当时我只想到了News Feed显示的是一种trend，以及这种trend能够带来的经济利益，不过貌似至今Facebook也没有开发这方面潜在赢利点的倾向。&lt;br /&gt;&lt;br /&gt;最近我又对比了一下Facebook和众多国内SNS网站的首页输出，发现Facebook的News Feed其实很侧重于用户交互，并且这个侧重点不仅仅与信息输出的条数有关，还与版面设计有关。&lt;br /&gt;&lt;br /&gt;如果你把Facebook的News Feed当作报纸来看，那么哪些是能够第一时间成功吸引你注意力的“头条”？我想应该是用户上传或分享的视频、图片，以及有评论的信息。首先，这些信息占的版面大；其次，视频与图片的丰富色彩及评论区域的背景颜色明显地和颜色单调的描述性文字区分开来。我们来看一个具体的例子：&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/45201686@N00/3278392042" title="View 'Facebook | Home' on Flickr.com"&gt;&lt;img src="http://farm4.static.flickr.com/3499/3278392042_7d38b02dc7_o.jpg" alt="Facebook | Home" border="0" width="502" height="459" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;我想你肯定能够轻易地看得出中间照片集一段的强调作用——占用空间更大、粗体的标题、字号更大的评论链接。这说明了，Facebook是刻意设计为强调这一条信息的，而非仅仅因为照片占用更多空间而“意外”造成的效果。&lt;br /&gt;&lt;br /&gt;那么有评论的信息呢？评论区域淡淡的背景色，也成功地将它从其他同等级别的信息中凸显出来：&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/45201686@N00/3277629383" title="View 'Facebook | Home' on Flickr.com"&gt;&lt;img src="http://farm4.static.flickr.com/3347/3277629383_5e3e4c2013_o.jpg" alt="Facebook | Home" border="0" width="469" height="334" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;大量弱化的信息，往往会被淹没在News Feed中，然而只要有评论，你就一定不会错过。评论区域的背景色一定会捕捉到你的注意力，甚至你会因为评论数量之多，而好奇到底是什么事情吸引了那么多人的注意力。&lt;br /&gt;&lt;br /&gt;校内在一定程度上抄袭了Facebook的这些设计，所以首页看起来还是挺顺眼的，虽然我实在搞不懂校内是如何选择强调哪些信息的，而且在中文字体上用粗体能得到的强调效果我觉得并不明显，此外弱化信息的弱化做得也不够明显。&lt;br /&gt;&lt;br /&gt;海内在这方面显得有点点“自作聪明”了，结果得到的效果更糟糕。例如用户改变状态（或曰“签名”、“迷你博客”……），这类信息数量之大，显然应该弱化之，最多仅仅强调有评论的几条。海内则是反过来做：&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/45201686@N00/3278479868" title="View '首页_海内' on Flickr.com"&gt;&lt;img src="http://farm4.static.flickr.com/3338/3278479868_1fe88e6ca8_o.jpg" alt="首页_海内" border="0" width="589" height="379" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;这种对好友意义不大的信息（相对发照片或写博客来说），竟然要暂用三行的空间。而且，海内还默认把评论隐藏起来了，在一堆同类信息当中，你实在看不出哪一条是因为能够得到别人注意而拥有评论的，于是扫一眼就全部跳过去了。此外，海内的评论（或曰“回复”）也不突出，即使默认展开了强调效果也不明显。&lt;br /&gt;&lt;br /&gt;最后总结一下，SNS的首页信息必须做到两点&lt;ol&gt;&lt;li&gt;能够有所侧重地选择推送给用户的内容&lt;/li&gt;&lt;li&gt;在版面设计上把重点强调出来&lt;/li&gt;&lt;/ol&gt;至于哪些是应该重点推送的信息，我想除了可以通过复杂的算法计算之外，有些简单的规则是普遍适用的。例如说评论，能够引起部分人注意的信息，至少比没人会留意到的信息更值得我关注。又例如说照片，别人原意花时间上传的照片，至少比无意修改的状态更值得我关注。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-8152805409341596174?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/IfM2RCVuhyXUcUaqp72ncNAZwjs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IfM2RCVuhyXUcUaqp72ncNAZwjs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/IfM2RCVuhyXUcUaqp72ncNAZwjs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/IfM2RCVuhyXUcUaqp72ncNAZwjs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=CX205hQu"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=O5rAz1oL"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=GzDc30bc"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=GzDc30bc" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=roHRi2m6"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=xECnE9zj"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/BXryfqUkq0U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/8152805409341596174/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=8152805409341596174" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8152805409341596174?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8152805409341596174?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/BXryfqUkq0U/facebook-news-feed.html" title="Facebook News Feed 上的内容强调与弱化" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2009/02/facebook-news-feed.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0MAQXo_eSp7ImA9WxVTFkw.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-6606511057287499392</id><published>2008-12-30T01:02:00.003+08:00</published><updated>2008-12-30T12:30:40.441+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-30T12:30:40.441+08:00</app:edited><title>GMP Party EP2</title><content type="html">Geeky meets Pretty的第2场大party终于来了（平时的小聚会不计算场次）。自从我在&lt;a href="http://chinese.catchen.biz/2008/11/chinese-blogger-conference-2008.html"&gt;网志年会&lt;/a&gt;上见识到了&lt;a href="http://www.punchparty.asia/"&gt;Punch Party&lt;/a&gt;后，我就一直想在GMP内搞这样一个活动，在小组内进行过若干讨论后我们最终决定要搞一个2008年年度“大”party，形式当然就是Punch Party了。&lt;br /&gt;&lt;br /&gt;这次party，要感谢&lt;a href="http://chloe.mindmeters.com/"&gt;Chloe&lt;/a&gt;和Piggest帮忙组织，是她们帮忙确定了整个活动的rundown和预订到了场地（&lt;a href="http://storygarden.me/"&gt;奇遇花园&lt;/a&gt;）。然后要感谢所有的speakers，是他们带来了如此丰富的speech：&lt;ul&gt;&lt;li&gt;尚文欢: &lt;a href="http://www.slideshare.net/whshang/why-we-twitter-presentation/"&gt;为什么喋喋不休？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Jinghua: &lt;a href="http://www.slideshare.net/CatChen/where-to-meet-pretties-presentation/"&gt;Where to meet pretties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://webleon.org/"&gt;Webleon&lt;/a&gt;: &lt;a href="http://www.slideshare.net/webleon/my-blogger-blackboard-presentation"&gt;Blogger黑板报─GMP新年山寨特别版&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Chloe: &lt;a href="http://www.slideshare.net/ichloe/gmp-version-feminine-approach-of-fashion-site-product-designs-presentation"&gt;What a girl wants? a feminine user behavior for fashion website design&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://blog.csdn.net/jobchanceleo"&gt;Leo&lt;/a&gt;: &lt;a href="http://www.slideshare.net/CatChen/ss-presentation-878494/"&gt;你为什么找不到喜欢的工作&lt;/a&gt;&lt;/li&gt;&lt;li&gt;龚纹: &lt;a href="http://www.slideshare.net/ichloe/ss-presentation-878596/"&gt;其实我们可以更懂星座&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://yinluotianse.blogbus.com/"&gt;璎珞天色&lt;/a&gt;: &lt;a href="http://www.slideshare.net/CatChen/open-party-presentation/"&gt;如何组织一场Open Party&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Jinghua和Chloe的session做成了series，让大家非常期待下一个episode。其中Jinghua说到清华校园不适合搭讪美女时，&lt;a href="http://www.mindmeters.com/day.asp"&gt;方军&lt;/a&gt;立即表示反对并解释了清华哪里适合寻找美女。Leo因为是在看过广州Punch Party的视频后做的准备，所以他的session风格也更接近之前广州Punch Party的，据说他之前按照7分钟的时间安排排练了好几次哦。龚纹的星座解释好复杂哦，听着听着就让我感觉在上高中的政治课，可惜现场没有人主动出来让龚纹做一下分析，否则会好玩很多。&lt;br /&gt;&lt;br /&gt;作为一个party，少不了的当然是&lt;a href="http://www.slideshare.net/CatChen/questions-and-gifts-presentation"&gt;回答问题抢奖品&lt;/a&gt;的环节，我的geeky问题加上Chloe的fashion问题还有龚纹的星座问题，确保了大家不仅仅要有实力还要有运气才能拿到奖品。对我来说，最好玩的就是问Office 2007下一版本号的问题了，竟然那么多人不知道Office 13这个版本号被跳过了。&lt;br /&gt;&lt;br /&gt;这次的party虽然并没有Carol组织的Punch Party那么紧凑和快节奏，不过我觉得作为第一次已经做得不错了，相对来说不够2.0（主要是单向交流，听众互动少），而且也没有sponsor。方军建议我们可以和&lt;a href="http://www.beijing-open-party.org/"&gt;Beijing Open Party&lt;/a&gt;合作，做成他们的一个track，因为我们在某些方面正好和他们互补哦——他们缺少美女，而我们性别比例极力保持在1:1；他们缺乏非技术话题因而显得范围比较窄，而我们追求多样性有各式各样的话题；我们没有sponsor所以要收场地费用，而他们拥有不少的sponsor；我们还没有固定的流程，而他们已经有相当的活动经验。&lt;br /&gt;&lt;br /&gt;anyway，接下来我们GMP也要建立一个对外发布信息及接受投票和留言的blog，然后逐步把GMP Party的筹办过程流程化，让GMP Party成为一个相对稳定的活动。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-6606511057287499392?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/iMy7ISGPDZ4A1GU2C-HvDMmYOsY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/iMy7ISGPDZ4A1GU2C-HvDMmYOsY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/iMy7ISGPDZ4A1GU2C-HvDMmYOsY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/iMy7ISGPDZ4A1GU2C-HvDMmYOsY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=tbI3DeyL"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=1YUW4mQY"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=6Dqz4zx3"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=6Dqz4zx3" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=sHy7CXaa"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=KJtUEaNh"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/iW65UKAK_W4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/6606511057287499392/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=6606511057287499392" title="1 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/6606511057287499392?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/6606511057287499392?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/iW65UKAK_W4/gmp-party-ep2.html" title="GMP Party EP2" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://chinese.catchen.biz/2008/12/gmp-party-ep2.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEQMRXs8eSp7ImA9WxRaEkg.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-1929511362222715040</id><published>2008-12-12T12:00:00.001+08:00</published><updated>2008-12-14T18:59:44.571+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-14T18:59:44.571+08:00</app:edited><title>假如你愿意以原版或影印版价格购买翻译书籍的话</title><content type="html">这篇文章是接着Tony Qu的《&lt;a href="http://www.cnblogs.com/tonyqus/archive/2008/12/12/1353372.html"&gt;批“觉得有必要记一下的东西——关于翻译”一文&lt;/a&gt;》写的，就讨论一个问题——你原意以原版或影印版的价格购买翻译书籍吗？&lt;br /&gt;&lt;br /&gt;为什么问这个问题？这源自Tony Qu批判的&lt;a href="http://www.cnblogs.com/PatrickChen/archive/2008/12/11/1352800.html"&gt;原文&lt;/a&gt;中的一句话：&lt;blockquote&gt;很多国内程序员看书就只看英文原版，开发就只用原版VS，就是这个原因。&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;我承认，在英语能力相当的程序员当中，这句话说的是事实。能够看原版书的尽量看原版书，不仅仅因为看起来舒服，更因为容易在P2P网络上找到OCR后完美排版的PDF（甚至是官方PDF）。但这部分人当中，又有多少是真真正正是去买原版书的（原价格按汇率算），或者至少去买影印版的（价格是翻译版的一倍以上，原版的一半以下）？我想就很少了，我仅仅知道&lt;a href="http://www.cnblogs.com/JeffreyZhao/"&gt;Jeffrey Zhao&lt;/a&gt;会从国外买二手原版书。很老实说，假如读者原意为翻译版支付引进版的价格，那么翻译版能够做得比现在的要好得多。但有多少人是连翻译版的价格也不愿意支付的，宁愿上网找极度难看的未OCR扫描版？此时，结论已经很明确，翻译版质量差不完全是出版社或者译者的问题，是经济利益驱使，这有电影工业与唱片工业在大陆的做法为证！&lt;br /&gt;&lt;br /&gt;虽然现在DVD的分区对购买者来说已经毫无意义，但当初就把大陆分作一个6区是有理由的。话说刚刚有DVD的时候，大陆的DVD市场跟国际市场同一个运作方法，电影上映后至少3个月才发行DVD，以免影响票房。但DVD就是卖不出去，不仅仅是因为太贵，还因为国内的购买者不愿意等那3个月，当然也不愿意买票进电影院看。他们买什么？买盗版DVD。因为DVD必须有正版片源出来后，才可能有正版片源的盗版DVD，所以3个月未到，所有的盗版都只可能是枪版DVD。然而大陆消费者就是不在乎画质和音质，他们就是想以最低的价格抢先看到电影，看完就算。当这种需求被中国电影工业从业人员知道以后，一种新的商业模式发展出来了，以满足这部分消费者的需求——大陆的引进版DVD的发行和电影的上映几乎同步进行，价格仅仅比盗版略高，并且最重要的是，人为地把画质和音质降低到枪版的水平。就这样，电影工业能够在大陆盗版市场上分到一杯羹，我的意思是他们算是卖官方枪版的了，同时也不影响他们的国际市场价格——即使存在购买渠道，港台消费者也不会接受这样廉价的劣质DVD，仍然会等上映3个月后买原版DVD。&lt;br /&gt;&lt;br /&gt;唱片工业做的事情是类似的，大陆所谓的引进版虽然价格更容易被消费者接受，但音质是经过人为降低的。这就是价格歧视，因为大陆消费者只愿意支付一个更低的价格，也只需要购买质量更低的商品，所以供应商就可以把这划分为一个独立的价格歧视区域市场。我不知道图书行业是否存在这样人为控制的“阴谋”，但只要大陆消费者的需求还是这样的“另类”，价格歧视原则就会一直适用，这样的事情就会无意识的继续存在，以远低于影印版价格售卖的翻译版质量就肯定还是那么低。对了，顺带说一句，港台翻译版的价格是和大陆影印版差不多的。&lt;br /&gt;&lt;br /&gt;总结一下，我也是译者，翻译工作低廉的价格以及编辑工作的效率低下Tony Qu说过我就不再说了。如果大陆翻译版最终按照港台翻译版的价格去销售也能获得同样的销量的话，我相信译者可能愿意为这份价钱做全职的翻译工作，我相信出版社愿意聘请更多专业编辑并且合理安排他们的工作量。&lt;br /&gt;&lt;br /&gt;P.S.如果要认真学习和研究中国的传统语言文化，我个人还是建议去台湾。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-1929511362222715040?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/z7kl1ocr_1XJTGJCM4xqrlx-UBw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/z7kl1ocr_1XJTGJCM4xqrlx-UBw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/z7kl1ocr_1XJTGJCM4xqrlx-UBw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/z7kl1ocr_1XJTGJCM4xqrlx-UBw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=Z7t72Z5E"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=8wymGlcL"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=smYE6D3L"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=smYE6D3L" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=WKtiOXjr"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=CXCuqMRk"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/qFhD_2KEYw0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/1929511362222715040/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=1929511362222715040" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/1929511362222715040?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/1929511362222715040?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/qFhD_2KEYw0/blog-post_12.html" title="假如你愿意以原版或影印版价格购买翻译书籍的话" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2008/12/blog-post_12.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0cBQXs-cCp7ImA9WxVXFkw.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-9042115181604477227</id><published>2008-12-07T00:36:00.004+08:00</published><updated>2009-02-14T20:37:30.558+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-14T20:37:30.558+08:00</app:edited><title>SD2C 2008</title><content type="html">&lt;p&gt;这几天参加了SD2C，也就是“软件开发2.0技术大会”。规矩当然是照旧的，social第一，session第二。&lt;/p&gt;&lt;h4&gt;Day 1&lt;/h4&gt;&lt;p&gt;第一天想着12:00开始签到，于是慢吞吞地准备出发，去到九华山庄已经是11:00。这是我第一次在没有车接送的前提下一个人跑来九华山庄，路上浪费了不少时间。签到后开始乱逛，然后陆续找到了公司不同部门来的同事，发现大家都没吃午餐。虽然我自己从McDonald's带了一个汉堡来，不过还是跟大家一起打车到小汤山街口的镇上吃饭。吃完饭就13:00了，应该开场了，可是打车回九华山庄实在太难，最后无奈打了黑车回去。我个人认为，把开场放在13:00但又不提供午餐是十分不体贴的。在市区的话也就算了，在九华山庄这样一个三流装一流的地方，选择不多又不怎么好吃的自助餐竟然要¥68一位，方便面也要¥20一盒，不提供午餐这样的做法实在是不人道。&lt;/p&gt;&lt;p&gt;下午的议程看起来全是keynote级别，听过后才发现那完全是赞助商专场——逐个赞助商轮流上去宣传自己的技术或者观点，而且还不提供任何课程反馈的机会，它讲得再烂我也不能表达一下我的不满。赞助商专场分为上下两个半场，上半场的人都是来吹水的，就吹嘘自己公司的东西，泛泛而谈，听了也没什么收获；下半场讲的东西稍微实在一些，雷军和戴志康上来讲的话都很真诚，也很有感染力。总之，要是早知道第一天下午是赞助商专场的话，我就去WinHEC玩到15:00再过来好了，然后18:00吃饭，接着参加晚上的活动。这只能怪我参加会议的经验还是太少了，没有把这么明显的安排给看出来。&lt;/p&gt;&lt;p&gt;下午听了那么多个小时，最让我感到震撼观点来自于雷军。他在说他投资的&lt;a href="http://www.ucweb.com/"&gt;UCWeb&lt;/a&gt;时提到了这样一个观点：我们曾经都是互联网用户中的领头羊，有什么新鲜的东西出来我们都先去尝试，然后慢慢普及到大众用户。但在移动互联网就不是这样，移动互联网的主要用户是那些比较少机会通过计算机接入互联网的人，例如学生、民工和军人。也就是说，我们这些每天使用计算机连接着互联网做开发的人，其实是不可能知道移动互联网的真实需求的，我们无法体会使用不到计算机接入互联网会使怎样的。过往我一直认为移动互联网用户就是高端移动用户和高端互联网用户的交集，现在我终于明白到不是这么一回事了。&lt;/p&gt;&lt;p&gt;晚上去各个沙龙看了一下，没有什么印象特别深刻的。只记得在“职业规划与成长论坛”上听到这样一句搞笑的话——“我们的HR自裁了，也就没有HR了”。晚会后有班车把我们送回到地铁站，然后回到家也就是23:00了。&lt;/p&gt;&lt;h4&gt;Day 2&lt;/h4&gt;&lt;p&gt;第二天早上9:00出发，去到就是11:00了。我觉得把第一个session放在9:00开始是很不合理的，九华山庄这么偏僻的地方外加不包住宿，谁原意那么早起床赶7:00从市区开出的班车啊，结果当然是大家都很晚才到啊。对于speaker来说，如果自己的session被放在早上，可能也会觉得大会不重视他的session，因为早上能赶来参加session的人就是少一些，这个大家都明白的。我去到时已经是11:00，然后去听了一下郭安定的课，中午和郭安定、老孟、莫依等MVP一起吃饭，还是挺好玩的。&lt;/p&gt;&lt;p&gt;从郭安定讲的内容来看，他很注重企业生态圈。也就是说，只要你能够帮助别人解决实际问题，赚钱的机会总是有的，但也没必要自己把什么都做了，也要留机会让别人帮你，赚你的钱。把这个道理传播出去，让更多人赚得到钱，让更多人受惠。这种思想我是十分认同的，如果帮助别人的同时又帮助了你自己，这有什么不好呢？&lt;/p&gt;&lt;p&gt;下午我去听了一下周爱民的JavaScript+Delphi+ErLang，语言层面的东西不用怎么在乎，他也没做很应用性的展示，我觉得最重要的一个思想还是分层——只要实现了分层，层与层之间是针对接口设计的，那么随时换掉一个层都是可以的，只要接口保持不便，什么语言并不重要，不同层的需求不同实现语言自然也可以不同。另外还听了李建忠的.NET设计模式，讲的是一些老生常谈的内容，没什么非常特别的。其实讲什么技术并不重要，最重要是把那种思想说清楚，让听众都能领悟到，那就算是成功了。&lt;/p&gt;&lt;p&gt;晚上是CSDN专家沙龙，本来说是让各位专家说说对CSDN服务的不满与建议的，结果大家的自我介绍都拖得很长，介绍完也就完了，赠送给各位专家一人一本绝影签名的《疯狂的程序员》，然后就赶班车去了。其中最有趣的一幕，就是莫依问老孟说，“是什么让你多年坚持在论坛上帮助别人解答问题”，旁边的郭安定小小声说了两个字，“伟哥”。&lt;/p&gt;&lt;h4&gt;Day 3&lt;/h4&gt;&lt;p&gt;最后一天，依旧是9:00出发11:00到。原本计划去听听iPhone开发的，发现基础部分都讲完了，开始讲OpenGL的使用，我又完全听不懂，听了三分钟就睡着了。&lt;/p&gt;&lt;p&gt;下午听的是使用YSlow2.0优化网络性能，主要是了解一下YSlow2.0的新特性，也就是如何自己设计符合自身网络特性的网络性能评分规则。接着去听了Amazon运计算服务的架构分析介绍，确实比较介绍性，让你知道就是有那么多的服务在运行着，并且能为企业省钱，就这么简单。最后听的一个session是面向对象的JavaScript，这是作为当今JavaScript开发人员必须知道的基础知识啦，听他说一遍只是为了好玩。&lt;/p&gt;&lt;p&gt;SD2C结束后，马宁准备把我和莫依还是陈敏送到地铁站，最后大发慈悲把我们都送回家了，超级好人，在这里需要感谢一下。建议明年真的不要选择九华山庄这样的地方了，交通实在是不方便。&lt;/p&gt;&lt;p&gt;P.S.发现自己最近参加的会议不少啊，整个blog塞满了关于各个会议的文章，如&lt;a href="http://chinese.catchen.biz/2008/10/china-mvp-open-day-2008.html"&gt;China MVP Open Day 2008&lt;/a&gt;、TechED 2008 China、&lt;a href="http://chinese.catchen.biz/2008/11/chinese-blogger-conference-2008.html"&gt;Chinese Blogger Conference 2008&lt;/a&gt;、&lt;a href="http://chinese.catchen.biz/2008/12/winhec-2008-china.html"&gt;WinHEC 2008 China&lt;/a&gt;。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-9042115181604477227?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/AkNeVEF1uHez9qqQLXFXbu0RqYI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/AkNeVEF1uHez9qqQLXFXbu0RqYI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/AkNeVEF1uHez9qqQLXFXbu0RqYI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/AkNeVEF1uHez9qqQLXFXbu0RqYI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=ut1Y9Bsf"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=c4gpihpv"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=Y7BPDWON"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=Y7BPDWON" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=H5Ps065q"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=4zN507bW"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/7lsF91xjTA0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/9042115181604477227/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=9042115181604477227" title="2 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/9042115181604477227?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/9042115181604477227?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/7lsF91xjTA0/sd2c-2008.html" title="SD2C 2008" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://chinese.catchen.biz/2008/12/sd2c-2008.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkYCR3g_eCp7ImA9WxRbFUo.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-8200231346616167023</id><published>2008-12-06T22:36:00.001+08:00</published><updated>2008-12-06T22:36:06.640+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-06T22:36:06.640+08:00</app:edited><title>大陆技术书越来越台湾化了</title><content type="html">这次SD2C我总共搬了4本书回家，2本买的2本送的。我发现现在大陆出版的技术书越来越台湾化了，也就是“更多搞笑，更少大道理”，而这正好符合我近期的读书倾向。&lt;br /&gt;&lt;br /&gt;在CSDN专家沙龙上，每位专家都拿到了一本绝影的《&lt;a href="http://www.douban.com/subject/3267945/"&gt;疯狂的程序员&lt;/a&gt;》，听说是绝影自己经历的写照。虽然我还没仔细看，不过相信这本书会是本十分轻松的小说，这正是我喜欢的。之前看《&lt;a href="http://www.douban.com/subject/3142280/"&gt;梦断代码&lt;/a&gt;》也是足够轻松的，不过轻松背后也发人深思，让你不得不去思考很多软件工程和组织架构的问题。&lt;br /&gt;&lt;br /&gt;在SD2C的签售会上，我还买了一本《&lt;a href="http://www.douban.com/subject/3271104/"&gt;悟透JavaScript&lt;/a&gt;》。我已经不需要看任何对JavaScript泛泛而谈的书，但我仍然买了这本书，就是想看看其娱乐性的一面，如果确实写得好，包括插图画得好，那也是十分值得我学习得。什么时候我也要在我的blog上加上专业插图，否则就太没有竞争力了。&lt;br /&gt;&lt;br /&gt;另外我最近还买了一本《&lt;a href="http://www.douban.com/subject/2334288/?i=0"&gt;大话设计模式&lt;/a&gt;》，同样也是因为它搞笑的一面。虽然读起来会觉得某些剧情比较牵强，但至少这是一种写作风格，可能在大陆还不成熟，不过将来会被越来越多的作者采纳，自然会成熟起来。&lt;br /&gt;&lt;br /&gt;总的来说，我现在也看纯技术书看得有点累了，况且很多东西看了不用等于没看，过一段时间还真变成没用的了。轻松娱乐的东西，至少保证了阅读的过程是愉快的，即使将来用不着，用得着也就算是赚了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-8200231346616167023?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/KNFk3X4TeiG8yIqi_mAhwwtPeEE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KNFk3X4TeiG8yIqi_mAhwwtPeEE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/KNFk3X4TeiG8yIqi_mAhwwtPeEE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KNFk3X4TeiG8yIqi_mAhwwtPeEE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=tcwyMWFt"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=Tzhlm1Ve"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=yzlQ1pT3"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=yzlQ1pT3" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=WH6cSt78"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=VtDTPG9l"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/0IoCnH2vyiw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/8200231346616167023/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=8200231346616167023" title="2 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8200231346616167023?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8200231346616167023?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/0IoCnH2vyiw/blog-post.html" title="大陆技术书越来越台湾化了" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://chinese.catchen.biz/2008/12/blog-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYGR3Y-fSp7ImA9WxRbE00.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-8412654664213968823</id><published>2008-12-03T20:25:00.001+08:00</published><updated>2008-12-03T20:25:26.855+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-03T20:25:26.855+08:00</app:edited><title>WinHEC 2008 China</title><content type="html">今天是WinHEC第一天。WinHEC就是Windows Hardware Engineering Conference，中文名是做&lt;a href="http://www.microsoft.com/china/whdc/winhec/default.mspx"&gt;Windows硬件工程大会&lt;/a&gt;。昨晚紫柔告诉我9:00到场签到就可以了，因为9:30正式开始，但我早上还是忍不住多睡了一会儿，所以10:00才到场。当时还是张亚勤的演讲，我进入主会场后迅速找到剩余的几个空位坐下来了。&lt;br /&gt;&lt;br /&gt;张亚勤的演讲当然紧接着若干个demo，其中Surface的demo在MVP Open Day已经看过了，这次还是同一个人来做。还有那个health care的video，也是&lt;a href="http://chinese.catchen.biz/2008/10/china-mvp-open-day-2008.html"&gt;MVP Open Day&lt;/a&gt;上面放过的，看来这两个月没什么变化嘛。整体而言，张亚勤演讲后的中文demo都是做一些我们已经知道的东西，没什么值得看的。&lt;br /&gt;&lt;br /&gt;原本张亚勤的演讲只有1个小时，结果延长了很多，然后Dennis Flanagan才上来讲Windows 7。Windows 7的demo就非常有冲击力了，特别是device stage这样的特性。&lt;br /&gt;&lt;br /&gt;在Windows 7里面，每个外设（包括网络外设）都有自己的图标，厂商可以设计一个和外设外观一样的图标，使得用户更容易在Windows 7中识别这一设备。例如你有黑白两个USB drive，都插入到电脑后，你会发现不知道哪个盘符是哪个USB drive的，要看看里面的内容才知道。在Windows 7里面，只要厂商制作了对应的图标，用户就能在图形界面上看到有区别的两个图片，也就可以在不了解何谓盘符的前提下正确操作这两个USB drive了。&lt;br /&gt;&lt;br /&gt;此外，每一个外设都可以有自己的device stage，例如连接一部Zune后可以显示容量、电量，还可以显示为你推荐的MP3销售信息；又例如连接一部Nokia手机后可以显示有关的内部信息，以及Nokia网站上为这部手机推荐的新游戏。简单来说，硬件设备销售后不再是与硬件厂商脱离关系了，只要它连接到安装了Windows 7的PC，并且这台PC还连接着网络，硬件厂商就能得知这台设备的使用情况，并且为你推介更多的商品和服务，当然也可能包括免费的服务与技术支持。肯定有人会说这会导致隐私问题之类之类的，我相信最终Windows 7和硬件厂商会引入访问控制机制，让用户选择一台设备连接到Windows 7后是否允许相关信息通过网络传输，这个大家不用担心。&lt;br /&gt;&lt;br /&gt;两个keynote结束后，就是抽奖环节。早上每一个人进来时都拍了一张照片用于抽奖，然后用这些照片拼成了一个巨大的奥运五环徽标，最终用Silverlight Deep Zoom来随机zoom in到某一个人的照片之上。这样的抽奖形式还是挺有创意的，不过有些照片实在太不清晰了，最后拿到了大奖的人到底是不是照片上的人，我们都不知道。&lt;br /&gt;&lt;br /&gt;午餐是跟紫柔以及其它MVP一起吃的，紫柔带来了一个90后的实习生，是一个拿了国家奖学金去新加坡读高中的女生。她应该是紫柔的助手吧，不过我们说的东西她都听不懂，应该会觉得十分无聊。&lt;br /&gt;&lt;br /&gt;随后，让我最不满的事情就发生了！今年的WinHEC和TechED一样，通过盖章来增加互动，总共有1200份Windows 7光盘和1000只2G USB drive可以通过盖全10个章后获得，并且日程表写着下午2:00开始换领的。但我们2:00回到会场发现今天的400只2G USB drive已经换领完毕了，虽然Windows 7光盘还是有的，但因为必须同时换领，所以也就相当于不能换领。既然日程表写着下午2:00开始就应该下午2:00开始啊，怎么能够提前开始发呢？！&lt;br /&gt;&lt;br /&gt;下午的课程我们都没有去听，一群MVP聚在一起聊天。最有趣的一幕就是，竟然有人来主动搭讪那位90后女生。是不是非80后的人就分不清什么人是90后的呢？又或者是IT人士太闭塞了？反正我觉得90后的化妆和穿衣风格挺容易辨别的。我们分散地坐在媒体展示区的长沙发上，那个人直接无视我们问她还有没有盖章的那张纸。她说没有，那个人就问她是哪里的以及有没有名片。这时候作为大版主的紫柔自然冲出来说——我们都是微软中文技术论坛的！我相信那个人已经很无语了，但又不知道我们是干什么的，还是问我们有没有名片。紫柔就告诉他，你上微软中文论坛技术就知道啦。他估计还是不知道微软中文技术论坛是什么，不过很无奈地走了。真是难得见到有人如此直接搭讪的，而且还是在硬件工程大会上哦！&lt;br /&gt;&lt;br /&gt;最后一个session解释时，会根据收回的反馈表抽奖，于是我们就找了一个讲用VHD（虚拟硬盘）引导启动的session去听听。最后提问阶段，有人问了能否引导启动一个装有Linux的VHD，speaker说VHD的技术都是开放的，虽然Microsoft没有实现的意向，但开源社区可以去做这样的事情。其实我有个问题，不过一直懒得问，那就是Microsoft能否和Apple合作，做一个Mac上从VHD引导启动的东东，那么将来我就可以在MacBook装Windows 7并且仅仅装到VHD里。&lt;br /&gt;&lt;br /&gt;会议结束后，MVP在讨论是否组织一起吃饭，因为我约了人，要去拿张CF卡，所以就先走了。明天我要去参加&lt;a href="http://www.sd2china2008.com/"&gt;SD2C&lt;/a&gt;，也就无法参加WinHEC了。&lt;br /&gt;&lt;br /&gt;P.S.这篇文章不是因为MVP换取赠票而必须写的，而是因为换领礼品这件事实在搞得太不合理了，当时我就决定要写WinHEC的负面。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-8412654664213968823?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/rdA4iTkymsca-aIFwpqqOwTFBzI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rdA4iTkymsca-aIFwpqqOwTFBzI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/rdA4iTkymsca-aIFwpqqOwTFBzI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/rdA4iTkymsca-aIFwpqqOwTFBzI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=DQe1l0CJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=SHgt1Mnb"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=sYZSUez9"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=sYZSUez9" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=DXvJwr8G"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=JxWhZqgT"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/6Iz4tSWgSJE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/8412654664213968823/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=8412654664213968823" title="2 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8412654664213968823?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/8412654664213968823?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/6Iz4tSWgSJE/winhec-2008-china.html" title="WinHEC 2008 China" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://chinese.catchen.biz/2008/12/winhec-2008-china.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUACQH49fyp7ImA9WxRbEEQ.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-5486552313218388519</id><published>2008-11-23T20:34:00.001+08:00</published><updated>2008-12-01T10:16:01.067+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-01T10:16:01.067+08:00</app:edited><title>Chinese Blogger Conference 2008</title><content type="html">上个周末请了两天年假，回广州参加网志年会顺便回家。&lt;br /&gt;&lt;br /&gt;第一天去到会场，就见到了&lt;a href="http://www.ilemoned.com/"&gt;LEMONed&lt;/a&gt;和&lt;a href="http://www.hidecloud.com/blog/"&gt;hidecloud&lt;/a&gt;，然后拿到了我的fail whale tee。接下来的session……其实我不知道session讲什么，我想大多数人都跟我一样不关心session讲什么，大家只是找个机会来聚会而已。中午跟Elliot Ng一起吃了午餐，并且拿了一件&lt;a href="http://cnreviews.com/"&gt;CNReviews&lt;/a&gt;的tee。下午的session也是不知道讲了什么，然后就是自助晚餐了和Punch Party了。&lt;br /&gt;&lt;br /&gt;自助晚餐吃的是点心，虾饺和牛肉丸都不错，但是鸡翅就很难吃。接着Punch Party正是开始，第一位上台的是&lt;a href="http://carol.bluecircus.net/"&gt;Carol&lt;/a&gt;，介绍什么是Punch Party。所谓的Punch Party，就是每位speaker有7分钟的时间，大概就是20个slide每个slide讲20秒的时间再多一点。当晚的Punch Party做得非常perfect，每一个人讲的话题都十分之吸引人，做到了Punch Party所强调的punch效果——让人感觉耳目一新。Carol作为第一个speaker，把全场的气氛都调动起来了。中间有介绍大陆吃喝玩乐之行有多艰险的&lt;a href="http://worker.bluecircus.net/"&gt;工头坚&lt;/a&gt;，还有介绍胖卡如何把宅男配送到乡下的。最后一位导演先生，表演了7分钟的无声演讲——开头只说了一句，“由于接下来的内容都很晦涩，所以自动进行静音处理”，接着做了7分钟演讲的动作与口型，幻灯片翻了几页，但是没说过一个字。对于所有参加Punch Party的人来说，这样的结尾实在是太impressive了！&lt;br /&gt;&lt;br /&gt;Punch Party的好处显而易见，能够在最短的时间内让你了解到一些你可能永远都接触不到的知识，同时给你无限的震撼感。不过我觉得Punch Party对speaker的要求也挺高，否则也不容易做好。而且作为organizer，一场Punch Party必须组合不同类型的speaker。&lt;br /&gt;&lt;br /&gt;第二天的session稍微有趣一些，不过依然没怎么听，莫非年会的习俗就是把重点放在晚上？下午年会结束后大家就跟着&lt;a href="http://isaacmao.com/"&gt;Issac Mao&lt;/a&gt;到“友鸡会”吃了火锅，然后到北风的凸凹酒吧去聊天。我也不知道最后大家是什么时候才回家的，我快到凌晨时已经想睡觉了，于是就回家了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-5486552313218388519?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/UhvHIX1MBTjll8afmIzB5sT4t3U/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UhvHIX1MBTjll8afmIzB5sT4t3U/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/UhvHIX1MBTjll8afmIzB5sT4t3U/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UhvHIX1MBTjll8afmIzB5sT4t3U/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=4x1UNIG1"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=YADMJ6NN"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=EBaYk290"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=EBaYk290" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=0gcwxolR"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=jJuWINsX"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/OU07U5_R9HU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/5486552313218388519/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=5486552313218388519" title="12 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/5486552313218388519?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/5486552313218388519?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/OU07U5_R9HU/chinese-blogger-conference-2008.html" title="Chinese Blogger Conference 2008" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">12</thr:total><feedburner:origLink>http://chinese.catchen.biz/2008/11/chinese-blogger-conference-2008.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkQAQn0zfSp7ImA9WxRVFUU.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-7196478807669164018</id><published>2008-11-13T22:59:00.001+08:00</published><updated>2008-11-13T22:59:03.385+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-13T22:59:03.385+08:00</app:edited><title>Live Mesh vs MobileMe</title><content type="html">最近在用&lt;a href="http://www.mesh.com/"&gt;Live Mesh&lt;/a&gt;在多台计算机上同步文件，而之前一直用&lt;a href="http://www.me.com/"&gt;MobileMe&lt;/a&gt;同步联系人、日程等信息外加系统备份，真希望把这两个服务的好处叠加起来。&lt;br /&gt;&lt;br /&gt;Live Mesh的好处是可以指定任何一个目录进行同步，而非指定一个同步目录然后要同步的东西必须放在里面，这方面就比MobileMe要好。而且Live Mesh的自动同步客户端同时兼容Windows和Mac，而MobileMe仅仅支持Mac同步。Live Mesh唯一不足就是没有版本控制，不过我想要是有版本控制占用的网络空间和流量开销就成倍增加了。&lt;br /&gt;&lt;br /&gt;MobileMe给我印象最深刻的是，我重装了我的MacBook后一登录MobileMe帐号就帮我把整个System Preferences给恢复了。当然，联系人和日程信息也都恢复了，这是现在Live Mesh完全没覆盖到的功能。另外对iPhone的push服务也做得很好，更新什么信息都在Mac上进行就可以了，根本不用管它什么时候同步到iPhone上。&lt;br /&gt;&lt;br /&gt;我觉得最好就是把这两种服务结合到一起，一个帐号一个服务就把这些都覆盖了，当然我不介意付费。在TechED MVP Dinner上，我和一位做Azure的Microsoft员工聊了一下，说最好Windows上的程序都遵守Vista的规范，把代码和数据分开存放，也就是数据都放到C:\ProgramData下面，这样Windows也就如同Linux一样能够轻松备份纯数据了。接着只要把数据同步到cloud上面，例如说是Azure上层的Live Mesh，重装机器后就能够把数据重新同步回来了，只要再装上应用程序就一切恢复原装了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-7196478807669164018?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/iXpilJXfk_rQ699RDEHBtUo9fdo/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/iXpilJXfk_rQ699RDEHBtUo9fdo/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/iXpilJXfk_rQ699RDEHBtUo9fdo/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/iXpilJXfk_rQ699RDEHBtUo9fdo/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=piQkw2Te"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=RIC82cx6"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=C7BGv5BQ"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=C7BGv5BQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=jzo6pqVd"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=7KyJ4E8K"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/cIH-11zRewU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/7196478807669164018/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=7196478807669164018" title="0 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/7196478807669164018?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/7196478807669164018?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/cIH-11zRewU/live-mesh-vs-mobileme.html" title="Live Mesh vs MobileMe" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://chinese.catchen.biz/2008/11/live-mesh-vs-mobileme.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4EQX0yeSp7ImA9WxRVEkk.&quot;"><id>tag:blogger.com,1999:blog-7005036.post-5399901977187992163</id><published>2008-11-10T00:03:00.001+08:00</published><updated>2008-11-10T00:08:20.391+08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-10T00:08:20.391+08:00</app:edited><title>开始翻译 Adobe AIR in Action</title><content type="html">&lt;a href="http://chinese.catchen.biz/2007/08/prototype-and-scriptaculous-in-action.html"&gt;Prototype and Scriptaculous的翻译&lt;/a&gt;已经是一年前的事情了，翻译了半年，休息了大半年，现在继续接受翻译的工作。仍然是图灵引进的书，仍然是Manning的In Action系列，这次的是&lt;a href="http://www.manning.com/lott/"&gt;Adobe AIR in Action&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;其实AIR出来之前我就想去学习，很多很多技术都如此，不过翻译算是一个很好的学习过程，因为它逼着你认认真真把一本书读懂。这次着手翻译Adobe AIR in Action，我想半年后我也会熟悉AIR相关的技术。虽然我一直以来都对Flash和Flex没什么深入了解，但我希望这些知识反过来也能用到Silverlight开发上，结合两派的模式从而进化出一种更好的实践方法。&lt;br /&gt;&lt;br /&gt;另外我还介绍了身边的朋友去翻译&lt;a href="http://www.amazon.com/gp/product/1590599195?ie=UTF8&amp;tag=scriptaculous-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=1590599195"&gt;Practical Prototype and Script.aculolus&lt;/a&gt;。Manning即将发布&lt;a href="http://www.manning.com/campbell/ "&gt;Silverlight 2 in Action&lt;/a&gt;，如果图灵引进这本书的话，我也去看看是否能够参与翻译。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7005036-5399901977187992163?l=chinese.catchen.biz'/&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Cc8jNEmd2lrSN1sSKI0WsTaMQtU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Cc8jNEmd2lrSN1sSKI0WsTaMQtU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Cc8jNEmd2lrSN1sSKI0WsTaMQtU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Cc8jNEmd2lrSN1sSKI0WsTaMQtU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=wmnFoWhA"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=41" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=MPw2bjTA"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=50" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=62l22NOh"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?i=62l22NOh" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=94KmM36T"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=133" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CatChen/Chinese?a=4WYxDBbz"&gt;&lt;img src="http://feeds.feedburner.com/~f/CatChen/Chinese?d=52" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/Chinese/~4/iJP1fUizJQQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://chinese.catchen.biz/feeds/5399901977187992163/comments/default" title="帖子评论" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=7005036&amp;postID=5399901977187992163" title="2 条评论" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/5399901977187992163?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/7005036/posts/default/5399901977187992163?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/Chinese/~3/iJP1fUizJQQ/adobe-air-in-action.html" title="开始翻译 Adobe AIR in Action" /><author><name>Cat Chen</name><uri>http://www.blogger.com/profile/17757778716179768922</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05314858900917087509" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://chinese.catchen.biz/2008/11/adobe-air-in-action.html</feedburner:origLink></entry></feed>
