<?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:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><title type="text">Cat in dotNET</title><subtitle type="text" /><id>http://feed.cnblogs.com/blog/u/22107/rss</id><updated>2012-05-09T14:08:29Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/cathsfz/" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/CatChen/dotNET" /><feedburner:info uri="catchen/dotnet" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><geo:lat>23.1</geo:lat><geo:long>113.2</geo:long><logo>http://static.flickr.com/95/buddyicons/45201686@N00.jpg?1161510808</logo><feedburner:emailServiceId>CatChen/dotNET</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><entry><id>http://www.cnblogs.com/cathsfz/archive/2012/05/09/2493385.html</id><title type="text">怎么样才算是 RESTful？读 REST in Practice</title><summary type="text">最近 O’Reilly 搞活动，我就半价买了一本《REST in Practice》（Kindle 版链接）。对于 O’Reilly 的书，我通常会对比 O’Reilly 打折后的价钱和 Kindle 版的价格，通常是那家更便宜就在那家买，但图表或代码比较多的我就会坚持买 O’Reilly 的版本，因为 PDF 能够最好地保存这些格式。回到 REST 的话题上。尽管这个概念 2000 年就被提出来了，2007 年成为了一个热词，随后越来越多的服务都宣称自己是 RESTful 的，但是到底真么做才是真正的 REST 我从来没有自习学习过。由于 2007 年的时候 Ruby on Rails 也十</summary><published>2012-05-09T14:08:00Z</published><updated>2012-05-09T14:08:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2012/05/09/2493385.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/B8nH8dpyCjM/2493385.html" /><content type="html">&lt;p&gt;最近 O&amp;#8217;Reilly 搞活动，我就半价买了一本《&lt;a href="http://shop.oreilly.com/product/9780596805838.do"&gt;REST in Practice&lt;/a&gt;》（&lt;a href="http://www.amazon.com/gp/product/B0046RERXY/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=baindo-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B0046RERXY"&gt;Kindle 版链接&lt;/a&gt;）。对于 O&amp;#8217;Reilly 的书，我通常会对比 O&amp;#8217;Reilly 打折后的价钱和 Kindle 版的价格，通常是那家更便宜就在那家买，但图表或代码比较多的我就会坚持买 O&amp;#8217;Reilly 的版本，因为 PDF 能够最好地保存这些格式。&lt;/p&gt;&lt;p&gt;回到 REST 的话题上。尽管这个概念 2000 年就被提出来了，2007 年成为了一个热词，随后越来越多的服务都宣称自己是 RESTful 的，但是到底真么做才是真正的 REST 我从来没有自习学习过。由于 2007 年的时候 Ruby on Rails 也十分热门，所以我以为 Rails 风格的 CRUD API 就是 REST 了，同时对于外界关于「什么算是 REST 什么不算是 REST」的争论没怎么关心过。&lt;/p&gt;&lt;p&gt;记得之前&lt;a href="http://blog.zhaojie.me/"&gt;老赵&lt;/a&gt;提到过《REST in Practice》是本好书，所以我就收藏到 wish list 里面了，在 O&amp;#8217;Reilly 打折时就下狠心买下来，然后看看争论得如此多的 REST 到底是什么东东。书中提到 Richardson 的 REST 成熟度模型，通过这个模型你可以理解什么是 REST 什么不是 REST。这个模型是这样子的：&lt;/p&gt;&lt;p&gt;第 0 级服务：只使用一个 URI 作为一个服务端口，也只使用一个 HTTP 方法传输数据。大多数 WS-* 服务都是这个级别的，XML-RPC 和 POX 也是。这种做法相当于把 HTTP 这个应用层协议降级为传输层协议用。HTTP 头和有效载荷是完全隔离的，HTTP 头只用于保证传输，不涉及业务逻辑；有效载荷包含全部业务逻辑，因此 API 可以无视 HTTP 头中的任何信息。&lt;/p&gt;&lt;p&gt;第 1 级服务：使用多个 URI，不同的 URI 代表不同的调用入口，但只使用同一个 HTTP 方法传输数据。&lt;/p&gt;&lt;p&gt;第 2 级服务：使用多个 URI，不同的 URI 代表不同的资源，同时使用多个 HTTP 方法操作这些资源，例如使用 POST/GET/PUT/DELET 分别进行 CRUD 操作。这时候 HTTP 头和有效载荷都包含业务逻辑，例如 HTTP 方法对应 CRUD 操作，HTTP 状态码对应操作结果的状态。我们现在看到的大多数所谓 RESTful API 做到的也就是这个级别。&lt;/p&gt;&lt;p&gt;第 3 级服务：使用超媒体（hypermedia）作为应用状态引擎。要解释这个概念先要解释什么是超媒体：&lt;/p&gt;&lt;p&gt;我们已经知道什么是多媒体（multimedia），以及什么是超文本（hypertext）。其中超文本特有的优势是拥有超链接（hyperlink）。如果我们把超链接引入到多媒体当中去，那就得到了超媒体，因此关键角色还是超链接。使用超媒体作为应用引擎状态，意思是应用引擎的状态变更由客户端访问不同的超媒体资源驱动。&lt;/p&gt;&lt;p&gt;举个例子来说，用户在论坛的帖子列表点击超链接进入某个帖子，这时候浏览器作为一个客户端就会去 GET 这个帖子的链接 URI，应用状态就切换为显示某个帖子了。接着用户输入回复提交表单，浏览器就会根据 &lt;code&gt;form&lt;/code&gt; 上面的 &lt;code&gt;action&lt;/code&gt; 属性 POST 内容给目标 URI，应用状态就再一次发生切换，完成了回复的存储。&lt;/p&gt;&lt;p&gt;使用超媒体与前面第 1 级、第 2 级的显著区别是，客户端不再和 URI 紧耦合。在第 1 级或者第 2 级的应用里面，客户端都需要知道资源使用的 URI 模版（如 &lt;code&gt;/orders/{id}&lt;/code&gt;），然后要操作什么样的资源就生成什么样的 URI。超媒体客户端只知道入口 URI，之后的每一个 URI 都是通过超链接获得的。&lt;/p&gt;&lt;p&gt;还是用上述论坛例子来解释，假若这个论坛通过 Atom 协议支持非浏览器的客户端访问。客户端是不需要知道论坛帖子的 URI 模版的，因为客户端可以通过帖子列表的 Atom 获得帖子的超链接，然后在用户选择浏览帖子时获取对应 URI 的内容。获取回来的结果不会带有 &lt;code&gt;form&lt;/code&gt;，但会带有 &lt;code&gt;&amp;lt;link rel="reply" /&amp;gt;&lt;/code&gt;，通过这个 &lt;code&gt;link&lt;/code&gt; 客户端又知道了用户提交的回复应该发往哪个 URI。&lt;/p&gt;&lt;p&gt;说完 Richardson 的成熟度模型，说说 REST 这篇论文作者 Roy Fielding 的回应。Roy Fielding 说「只有使用了超媒体的才能算是 REST」。简单来说，他认为第 3 级成熟度以外的都不算 REST。我个人的看法是，我支持 Roy Fielding 对 REST 的严格定义，我也认为一个真正使用了超媒体实现应用状态引擎的服务非常了不起，但是在普通 CRUD API 能满足需求的情况下我觉得使用 CRUD API 也可以。&lt;/p&gt;&lt;p&gt;这本书后面还讲到了如何使用缓存来增加系统健壮性，如何使用 Atom 和 Atom 发布协议，如何使用 OpenID 和 OAuth 等认证授权技术。因为这本书我还没看完，所以这些内容我也不好做评价。&lt;/p&gt;&lt;p&gt;这本书总体来说写得不错，但就是废话稍微多了一点点。如果你跳着章节地去看，可能就不会觉得那是废话了，因为你挑选出来阅读的部分都包含所有的信息。但如果你像我这样从头到尾地阅读，就会觉得作者怎么前面提到过的事情后面还要再提及。&lt;/p&gt;&lt;p&gt;此外这本书提供的大量 .NET 和 Java 代码对我来说没什么意义。我觉得这本书更多是写给做企业服务的人看的，希望他们的思维模式能够从企业环境里常见的 WS-* 跳出来，同时希望证明给他们看实现 REST 是多么简单的事情。这对于天天研究互联网服务的我来说没什么必要，况且我也很久没写过 .NET 代码了，所以各式各样的示例代码给我无视掉了。我觉得我知道这件事情 WCF 能做也就足够了。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/2493385.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2012/05/09/2493385.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/pkQ27JtLwsf_pKSb1xCkjb1BFac/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/pkQ27JtLwsf_pKSb1xCkjb1BFac/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/pkQ27JtLwsf_pKSb1xCkjb1BFac/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/pkQ27JtLwsf_pKSb1xCkjb1BFac/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/dotNET?a=B8nH8dpyCjM:AUTXpds77qk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=B8nH8dpyCjM:AUTXpds77qk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=B8nH8dpyCjM:AUTXpds77qk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=B8nH8dpyCjM:AUTXpds77qk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=B8nH8dpyCjM:AUTXpds77qk:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=B8nH8dpyCjM:AUTXpds77qk:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/B8nH8dpyCjM" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2012/05/09/2493385.html</feedburner:origLink></entry><entry><id>http://www.cnblogs.com/cathsfz/archive/2012/01/13/2321219.html</id><title type="text">Covariant（协变）与 Contravariant（逆变）</title><summary type="text">今天为了解释某个问题而提到协变和逆变，发现每次解释这两个概念都会忘掉它们的本质，然后要重新看看定义，重新消化一下才能说明白。所以我决定把自己对协变和逆变的理解写下来，以免将来再次忘掉。我知道 .NET 的用户喜欢用 delegate TResult Func&lt;in T, out TResult&gt;(T arg); 来解释协变逆变，我则喜欢把 Func 的签名简写为 Haskell 签名形式。也就是说，把 Func&lt;T, TResult&gt; 写成 f :: a -&gt; b 的形式；把 Func&lt;T1, T2, Result&gt; 写成 f :: a -&gt; b</summary><published>2012-01-13T01:26:00Z</published><updated>2012-01-13T01:26:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2012/01/13/2321219.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/GLeWMCbCenU/2321219.html" /><content type="html">&lt;p&gt;今天为了解释某个问题而提到协变和逆变，发现每次解释这两个概念都会忘掉它们的本质，然后要重新看看定义，重新消化一下才能说明白。所以我决定把自己对协变和逆变的理解写下来，以免将来再次忘掉。&lt;/p&gt;&lt;p&gt;我知道 .NET 的用户喜欢用 &lt;code&gt;delegate TResult Func&amp;lt;in T, out TResult&amp;gt;(T arg);&lt;/code&gt; 来解释协变逆变，我则喜欢把 &lt;code&gt;Func&lt;/code&gt; 的签名简写为 Haskell 签名形式。也就是说，把 &lt;code&gt;Func&amp;lt;T, TResult&amp;gt;&lt;/code&gt; 写成 &lt;code&gt;f :: a -&amp;gt; b&lt;/code&gt; 的形式；把 &lt;code&gt;Func&amp;lt;T1, T2, Result&amp;gt;&lt;/code&gt; 写成 &lt;code&gt;f :: a -&amp;gt; b -&amp;gt; c&lt;/code&gt; 的形式。&lt;/p&gt;&lt;p&gt;其实无论是协变还是逆变，本质都是一样的：对于签名为 &lt;code&gt;f :: A -&amp;gt; B&lt;/code&gt; 的函数，实际可接受的参数范围为 &lt;code&gt;ASub&lt;/code&gt;，实际可返回的参数范围为 &lt;code&gt;BSub&lt;/code&gt;。这个很容易理解吧？任何时候子类的实例都可以当做超类实例来使用，无论是接受还是返回。&lt;/p&gt;&lt;p&gt;协变和逆变用于描述高阶函数签名，如 &lt;code&gt;f :: (X -&amp;gt; Y) -&amp;gt; Z&lt;/code&gt;。那上面的 &lt;code&gt;f :: A -&amp;gt; B&lt;/code&gt; 做模版，我们可以把 &lt;code&gt;(X -&amp;gt; Y)&lt;/code&gt; 看做 &lt;code&gt;A&lt;/code&gt;，把 &lt;code&gt;Z&lt;/code&gt; 看做 &lt;code&gt;B&lt;/code&gt;。应用同样的逻辑，函数实际可接受的参数范围是 &lt;code&gt;(X -&amp;gt; Y)&lt;/code&gt; 的子类，实际可返回的参数范围是 &lt;code&gt;Z&lt;/code&gt; 的子类。对于后者我们没什么疑问，但 &lt;code&gt;(X -&amp;gt; Y)&lt;/code&gt; 的子类到底是什么呢？它的所谓「子类」应该是 &lt;code&gt;(XSuper -&amp;gt; YSub)&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;为什么说 &lt;code&gt;(X -&amp;gt; Y)&lt;/code&gt; 的「子类」应该是 &lt;code&gt;(XSuper -&amp;gt; YSub)&lt;/code&gt; 呢？因为子类在能力上应该完整覆盖超类的能力，因此如果对方要求你提供一个函数，这个函数接受 &lt;code&gt;X&lt;/code&gt; 类型返回 &lt;code&gt;Y&lt;/code&gt; 类型，你提供的函数至少要能接受 &lt;code&gt;X&lt;/code&gt; 的超类而返回必须是 &lt;code&gt;Y&lt;/code&gt; 的子类。这时候 &lt;code&gt;X&lt;/code&gt; 是逆变参数（类型可以更宽松），而 &lt;code&gt;Y&lt;/code&gt; 是协变参数（类型可以更严格）。&lt;/p&gt;&lt;p&gt;一般来说，如果把「类型可以更严格」看做协变的话，函数的返回类型一定可以协变，非高阶函数的参数也可以协变，高阶函数的非函数参数同样可以协变。把「类型可以更宽松」看做逆变的话，只有高阶函数中的函数参数中会出现逆变，也就是作为参数的参数出现。那么参数的参数的参数呢？也就是说高阶函数的参数仍然是高阶函数，那会怎么样呢？这个大家可以尝试自行分析，盯住 &lt;code&gt;f :: ((X -&amp;gt; Y) -&amp;gt; Z) -&amp;gt; W&lt;/code&gt; 看一会儿，再不停类比上文的 &lt;code&gt;f :: A -&amp;gt; B&lt;/code&gt;，或许你就明白了。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/2321219.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2012/01/13/2321219.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/dJa7vqH4l9ookTTU5FWfVtfhCPA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dJa7vqH4l9ookTTU5FWfVtfhCPA/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/dJa7vqH4l9ookTTU5FWfVtfhCPA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dJa7vqH4l9ookTTU5FWfVtfhCPA/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/dotNET?a=GLeWMCbCenU:2sEivj3MDb4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=GLeWMCbCenU:2sEivj3MDb4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=GLeWMCbCenU:2sEivj3MDb4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=GLeWMCbCenU:2sEivj3MDb4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=GLeWMCbCenU:2sEivj3MDb4:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=GLeWMCbCenU:2sEivj3MDb4:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/GLeWMCbCenU" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2012/01/13/2321219.html</feedburner:origLink></entry><entry><id>http://www.cnblogs.com/cathsfz/archive/2012/01/08/2316653.html</id><title type="text">世界顶级黑客自传：Ghost in the Wires</title><summary type="text">上个星期终于把 Ghost in the Wires 看完了，现在就抽时间来写写书评吧。尽管我一向懒得写书评，不过这本书真的是超级推荐！最初我是在 Audible 上面买了这本书的有声读物，然后发现朗读得超级好，尤其是 Kevin 知道危险逼近时心中的独白——「Fuuuuuuuck!」朗读的语气掌握得非常好。后来我决定买 Kindle 版来仔细读一下，因为有声读物不会专心听，只能把我到大意。随后我边听边读把整本书看完了。这本书整体上分为 4 大部分。第 1 部分讲述 Kevin Mitnick 是如何入行的——他父亲和叔叔都是商人，这使得他很小的时候就意识到通过言语可以驱使他人做一些对自己有利</summary><published>2012-01-08T13:55:00Z</published><updated>2012-01-08T13:55:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2012/01/08/2316653.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/QD-bmaht9iA/2316653.html" /><content type="html">&lt;p&gt;上个星期终于把 &lt;a href="http://www.amazon.com/gp/product/B0047Y0F0K/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=baindo-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B0047Y0F0K"&gt;Ghost in the Wires&lt;/a&gt; 看完了，现在就抽时间来写写书评吧。尽管我一向懒得写书评，不过这本书真的是超级推荐！&lt;/p&gt;&lt;p&gt;最初我是在 &lt;a href="http://www.audible.com/"&gt;Audible&lt;/a&gt; 上面买了这本书的有声读物，然后发现朗读得超级好，尤其是 Kevin 知道危险逼近时心中的独白&amp;#8212;&amp;#8212;「Fuuuuuuuck!」朗读的语气掌握得非常好。后来我决定买 Kindle 版来仔细读一下，因为有声读物不会专心听，只能把我到大意。随后我边听边读把整本书看完了。&lt;/p&gt;&lt;p&gt;这本书整体上分为 4 大部分。第 1 部分讲述 Kevin Mitnick 是如何入行的&amp;#8212;&amp;#8212;他父亲和叔叔都是商人，这使得他很小的时候就意识到通过言语可以驱使他人做一些对自己有利的事情。同时他又是个超级 geek，从小喜欢在书店里面看一些稀奇古怪的书，例如说如何伪造身份让自己凭空消失，或者是如何获取他人的驾驶记录、资产记录、信用报告、银行信息、非公开电话号码。&lt;/p&gt;&lt;p&gt;由于这些爱好的驱使，他接触到了电话玩家（&lt;a href="http://en.wikipedia.org/wiki/Phreaking"&gt;phone phreak&lt;/a&gt;），也就是一群喜欢研究电话系统和拿电话系统做实验的人。然后他开始学习如何打免费电话（Steve Jobs 和 Steve Wozniak 也做个类似的事情），并且如同研究黑箱电路一样通过不停地打电话探索电话公司内部的组织形式。&lt;/p&gt;&lt;p&gt;后来由于同伴的出卖（这又是因为 Kevin 爱在同伴面前耍小聪明），Kevin 被捕并被起诉。在这个过程中，公诉人和媒体想尽办法抹黑 Kevin，给他安上各种奇怪的罪名，例如黑进 NSA、切断假释官电话、篡改法官信用报表等等，其中最莫名其妙的是说他由于喜欢明星 Kristy McNichol 而切断她的电话服务。（Kevin 独白：有见过因为喜欢一个人而切断她电话的吗？）正是由于这段经历，导致 Kevin 后来被 FBI 追捕时选择了逃亡，因为他认定政府是邪恶的，只会想往他头上加更多的罪名。&lt;/p&gt;&lt;p&gt;第 2 部分讲 Kevin 假释后的生活。尽管他的假释条款规定了他不能再进行违背道德的黑客行为，也不能再跟别的黑客有所接触，但为了调查弟弟死亡的真相，他还是忍不住黑进电话公司并且监听被怀疑对象的电话。随后他又回到了黑客道路上来。这时候他碰到了一位神秘黑客 Eric，看样子跟娱乐圈混得很近，不用工作也能过得很滋润，而且跟 Kevin 一样对电话系统有着深入的了解。最最重要的是，Eric 知道 Kevin 不知道的一种设备，叫做 SAS，可以用来全自动地监听任意线路。&lt;/p&gt;&lt;p&gt;Kevin 一方面对 Eric 有所怀疑，觉得他可能是卧底或者是线人；另一方面又受不住 SAS 的诱惑，希望搞清楚这套系统是怎么用的。最终他假装电话公司员工，向 SAS 制造商要到了完整的 SAS 设计手册，里面的资料比电话公司拥有的 SAS 用户手册还要丰富，包括用户用不到的调试指令。随后 Kevin 要想办法绕过 SAS 的安全策略来监听别人。&lt;/p&gt;&lt;p&gt;SAS 如此强大的设备自然有相对严谨的安全策略&amp;#8212;&amp;#8212;你可以拨号给 SAS 让它监听一条线路，SAS 也能帮你监听这条线路，但监听的结果不会输出到你拨入 SAS 的线路，只会输出到你指定的回拨线路，问题就在于每一台 SAS 都限制了回拨线路，你不能让它回拨到任意一个号码上。Kevin 想了一个很聪明的方法来绕过这个限制：他先用线路 A 拨入到 SAS 的回拨线路上，然后用线路 B 向 SAS 发送指令。SAS 要回拨时它自然会接上回拨线路，实际上就接到了线路 A 上，这时候 Kevin 就对着话筒哼一个线路就绪音（就是我们平时拿起听筒听到的声音），SAS 以为线路就绪了就开始拨号，实际上线路早就被接通了。&lt;/p&gt;&lt;p&gt;电话公司发现有人入侵了，安全小组就联通 FBI 一起侦察，而 Kevin 发现自己被监听以后，就想办法监听电话公司安全小组以及 FBI 的通话，以便搞清楚到底对方都收集到多少证据了，自己是否还有机会脱身。在如此几个回合后，Kevin 决定是时候人间蒸发了，于是就开始了他的逃亡之旅。&lt;/p&gt;&lt;p&gt;第 3 部分讲 Kevin 逃亡的过程。他先换了一个临时假身份来到了丹佛，然后再换一个长期假身份。每一次他都先要把出生证明和驾驶证骗到手，然后再去办社会保障号码卡。由于他用的都是真是存在的同龄人身份，简历上印的学历也是真实的，所以他能够找到工作赚钱生活下去。在这个过程中，他还弄到了一叠盖好章的出生证明！这意味着将来他想换身份就更容易了，直接把想要克隆的身份填写到出生证明上面就可以了，然后去考驾照和申请补发社会保障号码卡。&lt;/p&gt;&lt;p&gt;第 4 部分讲 Kevin 最终被捕的过程。Kevin 一直做足安全措施，包括使用克隆的手机号码，拨号时利用先前已经黑下来的电话公司交换机设置多重呼叫转移使得拨号源头难以跟踪，为此 FBI 一直都抓不到他。最后他黑了安全专家 Tsutomu Shimomura。对方是个高傲的黑客，因为被黑而决定帮助 FBI 抓住 Kevin，FBI 为此也给了他很多平民在合法程序上不可能获得的特权，例如监听电话和网络。最终 Kevin 被捕，同时公诉人再次想往他头上套上最严重的罪名，结果反而导致了 Free Kevin 活动的活跃。&lt;/p&gt;&lt;p&gt;媒体最初一直把 Kevin Mitnick 渲染为大黑客，说他只要对着电话吹口哨就能发射核导弹。现在由于政府玩大了，风向就发生了变化，人们开始支持 Free Kevin&amp;#8212;&amp;#8212;不是说他无罪，而是说他应该在合理的范围内承担他的罪名。他盗窃了大量的源代码和信用卡信息，但是他没有利用这些信息赚钱，所以造成的损失也就是各大公司少赚了一份源代码授权费用而已，因此应该根据这部分损失来做出判决。&lt;/p&gt;&lt;p&gt;公诉人和法官当然不这样想，但 Kevin 竟然找到了一份极具参考价值的判例&amp;#8212;&amp;#8212;一位税局官员曾经处于好奇心而获取了众多议员的税务信息，最终因为他没有因此获利而被轻判，理由是他只是好奇心驱使而已。这个判例使得 Kevin 获得了合理的对待，他只需要赔偿源代码授权费的损失就可以了，而且赔偿金额还要根据他未来几年的赚钱能力调整。考虑到假释条款上规定他不能使用电脑、不能接触电话、不能做技术咨询&amp;#8230;&amp;#8230;基本上他就只能去麦当劳煎牛肉了，法官判他 $4000 多一些的赔偿。&lt;/p&gt;&lt;p&gt;假释期满后，Steve Wozniak 亲自给他送了一台最新款的 PowerBook G4，然后他又回到了他的黑客世界。只不过这次他是开咨询公司，在道德的范围内，在客户的要求下，去黑客户的公司，帮助客户找出他们的漏洞。&lt;/p&gt;&lt;p&gt;总的来说，如果你对社会工程学有点好奇心，或者想知道美国政府是如何不小心把 Kevin Mitnick 搞成头号黑客的，&lt;a href="http://www.amazon.com/gp/product/B0047Y0F0K/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=baindo-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B0047Y0F0K"&gt;Ghost in the Wires&lt;/a&gt; 能够很好地满足你的好奇心。里面有技术细节，但非常少，就算你完全看不懂也不影响对情节的理解。&lt;/p&gt;&lt;p&gt;P.S. 这本书里面的每一章开头都有一段密文，有兴趣的人可以自己尝试去破解，懒得破解的可以搜索相关的文章。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/2316653.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2012/01/08/2316653.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/menfVuGFKYNfJzqoz1ZlkLMgI6E/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/menfVuGFKYNfJzqoz1ZlkLMgI6E/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/menfVuGFKYNfJzqoz1ZlkLMgI6E/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/menfVuGFKYNfJzqoz1ZlkLMgI6E/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/dotNET?a=QD-bmaht9iA:rM3EkvkPQlM:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=QD-bmaht9iA:rM3EkvkPQlM:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=QD-bmaht9iA:rM3EkvkPQlM:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=QD-bmaht9iA:rM3EkvkPQlM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=QD-bmaht9iA:rM3EkvkPQlM:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=QD-bmaht9iA:rM3EkvkPQlM:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/QD-bmaht9iA" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2012/01/08/2316653.html</feedburner:origLink></entry><entry><id>http://www.cnblogs.com/cathsfz/archive/2011/12/14/front-end-engineers-career-path.html</id><title type="text">前端工程师的职业发展路线在哪？</title><summary type="text">我猜想国内很多前端工程师都想过这个问题吧。前端工程师往往属于产品研发团队，但却很容易被边缘化——后端工程师觉得自己才是主力，没有后端工程师产品就不存在了，但没有前端工程师产品还能有，只是界面非常糟糕而已。这时候前端工程师就开始感觉自己像是个外包似的，只是来帮别人完成一些任务而已，对产品没有归宿感。这时候前端工程师的职业发展路线在哪？成为一个更好的外包吗？要做关键任务我觉得，要别人重视你的工作，不仅仅是你做得好就行了，还要求你的工作对别人来说足够重要。这跟产品定位有关——例如说对搜索引擎来说，前端对产品的影响不会非常大，用户只要能搜索到自己想要的结果就行了。搜索引擎最复杂的交互可能就是搜索框的自</summary><published>2011-12-14T00:40:00Z</published><updated>2011-12-14T00:40:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2011/12/14/front-end-engineers-career-path.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/kUG7ttYaP2A/front-end-engineers-career-path.html" /><content type="html">&lt;p&gt;我猜想国内很多前端工程师都想过这个问题吧。前端工程师往往属于产品研发团队，但却很容易被边缘化&amp;#8212;&amp;#8212;后端工程师觉得自己才是主力，没有后端工程师产品就不存在了，但没有前端工程师产品还能有，只是界面非常糟糕而已。这时候前端工程师就开始感觉自己像是个外包似的，只是来帮别人完成一些任务而已，对产品没有归宿感。这时候前端工程师的职业发展路线在哪？成为一个更好的外包吗？&lt;/p&gt;&lt;p&gt;&lt;strong&gt;要做关键任务&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;我觉得，要别人重视你的工作，不仅仅是你做得好就行了，还要求你的工作对别人来说足够重要。这跟产品定位有关&amp;#8212;&amp;#8212;例如说对搜索引擎来说，前端对产品的影响不会非常大，用户只要能搜索到自己想要的结果就行了。搜索引擎最复杂的交互可能就是搜索框的自动完成了，但有自动完成和无自动完成的区别到底有多大呢？跟准确率和召回率相比，有没有自动完成实在没有多重要。况且，自动完成的结果本身也依赖于准确率和召回率，所以后端工程师比前端工程师重要得多。&lt;/p&gt;&lt;p&gt;因此，前端工程师在选择工作时首先要选择前端足够重要的工作。重要用什么来衡量？务实的话，是钱；务虚的话，是产品。如果一个功能只能在前端实现，并且这个实现能够提高多少的转化率，使得多少原本不产生利润的点击产生利润，那么前端对这个产品来说一定十分重要。可惜往往跟钱相关的事情不由前端工程师来研究和决定，所以这部分工作还是安心交给产品设计师来做吧，让他们来决定怎么样的产品能赚钱，然后由你来完成这个产品的实现，这时候你的目标就是把产品做好。&lt;/p&gt;&lt;p&gt;回到刚才的问题，有些产品更依赖于后端，例如搜索引擎，当然也有些产品更依赖于前端。什么样的产品更依赖于前端？就是后端难以建立起技术壁垒的产品。这类产品要抄袭一个功能差不多的并不难，因此只有细节做得最好的能够获得足够多的用户。这类产品在 iOS App Store 上很常见&amp;#8212;&amp;#8212;有很多 app 拥有相似的功能，而其中只有一个交互设计得最好的能够获得绝大多数的用户。尽管 app 不存在 HTML + CSS + JS 这个前端，不过道理是一样的。当年 Tweetie 能够取代老牌的 Twitterrific 成为主流 Twitter 客户端，靠的就是交互上的创新，外加不差的性能和稳定性。如果交互对于一个 web app 来说十分重要，这个 web app 自然也就需要十分优秀的前端工程师。&lt;/p&gt;&lt;p&gt;总结一下，由于前端工程师的价值在于实现复杂的前端细节，因此如果可以选择的话尽量选择一个细节决定成败的产品。如果产品的成败已经由后端工程师决定了，例如某某数据规模要么能做要么不能做，那么这个产品就没你什么事了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;要懂核心业务&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;每一个公司，每一个项目，都有它的官方语言。不是指普通话，也不是指 C++，我指的是大家围绕什么问题来展开项目，什么问题的讨论能让大家为之兴奋。举个例子来说，百度的官方语言就是搜索，跟搜索没有关系的产品也会使用「准确率」、「召回率」这样的术语用来做比喻。前端工程师有多少知道什么是「准确率」、「召回率」的？估计不多，因为前端根本没有这样的概念。这时候前端工程师要跟后端工程师沟通也就不容易了。久而久之，你对人家很兴奋在讨论的什么 O(1) 还是 O(n) 不感兴趣，人家也不理解你的 &lt;code&gt;{} != {}&lt;/code&gt; 是什么意思，你就被边缘化了。&lt;/p&gt;&lt;p&gt;如果不想被边缘化，就算前端不是公司的核心业务，你也必须懂公司的核心业务，然后说着官方语言，而不是前端的方言。这就意味着，如果你在一家后端技术很强大的公司，你最好也懂后端技术。我知道国内有很多前端工程师并不是计算机系毕业的，就算是国内的教育也不怎么样，这时候你只能恶补相关的基础知识了。如果你不懂这些，就算你能把整本《JavaScript 权威指南》背下来，你说的还是方言，说官话的人还是会鄙视你。如果公司主要服务于某个垂直领域的话，你必须对这个垂直领域十分了解，随时能用这个领域的行话来沟通。&lt;/p&gt;&lt;p&gt;总结一下，由于每个人已经熟悉的领域都不一样，所以没办法说哪个领域更适合前端工程师。如果你原本已经有某个领域的从业经验，进入服务于该领域的技术公司总是有显著优势的。如果你进入了一个自己不熟悉的领域，那就一定要补充相关基础知识，否则你对这个领域不感兴趣，这个领域也不会对你的前端工作感兴趣。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;实际例子&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;为什么我选择加入&lt;a href="http://wandoujia.com/"&gt;豌豆荚&lt;/a&gt;？主要考虑的还是上面两点。&lt;/p&gt;&lt;p&gt;我在百度的时候一直就在想，既然前端对搜索引擎来说不重要，那对什么类型的应用来说比较重要呢？当时看到 Facebook 做得不错，所以觉得社区会需要复杂的交互，而如果复杂交互做不好则会影响用户使用，因此前端对社区来说应该十分重要。现在看来，也不完全是这样子。前端对社区来说确实重要，但 Facebook 并不是一个典型的例子，它是一个前端做得尤其优秀的例子。&lt;/p&gt;&lt;p&gt;在我了解到豌豆荚 Windows 客户端的实现方式时，我立即意识到它可以通过我的第一个判别标准&amp;#8212;&amp;#8212;前端对它来说是关键任务。它使用 Webkit 做了一个容器，然后把所有的交互都通过 web app 的形式做在里面，然后通过一组接口跟 native 进行交互。如果一个应用决定要这样做了，那么前端就能影响到它的成败，因为这时候前端后端的分隔线已经很明确了。如果一项功能应该由前端来做那就必须由前端来做，后端基本不可能成为实现此项功能的备选方案，这时候前端就具备了无可替代的位置。&lt;/p&gt;&lt;p&gt;至于第二个判别标准&amp;#8212;&amp;#8212;豌豆荚的核心业务是什么？我觉得豌豆荚做的很多事情都是以产品设计为起点的，而这至少是我感兴趣并且也有点感觉的东西。从细节上来说，就是大家喜欢谈论的事情是一致的，例如产品如何做一些很智能的设计，最新的技术方案如何能够巧妙地帮助这些设计得以实现。&lt;a href="http://wangjunyu.net/"&gt;Junyu&lt;/a&gt; 说「设计就是创造性地解决问题」，这是我喜欢的解决问题方式。这个世界上能够把逻辑转化为代码的人非常多，同时有一定数学和计算机专业基础的人也不少，因此要拼谁的解决方案更好的话那还要加上创造力。&lt;/p&gt;&lt;p&gt;我知道国内有很多产品设计师，在考虑产品时首先想到的是百万千万级用户量，这样无论从单个用户身上赚到的钱多么的少，最终产品还是能赚大钱。百度曾经就属于这种思维方式，但这不是我喜欢的风格，因为没有明确的目标用户定位。我知道国内由很多工程师，在编写代码时用尽各种技巧以展示自己过人的才智，但是这样的代码还有可复用性吗？除了作者本人没有人能够维护啊。不同的人有不同的品味，能够跟品味一致的人一起工作是一件幸福的事情。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;额外信息&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;这个话题到此就结束了吗？其实不是的。关于前端工程师的职业发展，我还有很多可以说的。不过我觉得找到一份让自己满意的工作必然是其中的第一步，因为你必须对工作充满兴趣，然后才能把事情做好，所以我把这部分内容放在最前面并且先发出来了。如果你不想错过后继讨论的话，欢迎&lt;a href="http://feeds.feedburner.com/CatChen/dotNET"&gt;订阅&lt;/a&gt;本博客。&lt;/p&gt;&lt;p&gt;此外，豌豆荚现在还招前端工程师，包括全职和实习，有兴趣的可以联系我：&lt;a href="&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#116;&amp;#111;&amp;#58;&amp;#99;&amp;#97;&amp;#116;&amp;#99;&amp;#104;&amp;#101;&amp;#110;&amp;#64;&amp;#99;&amp;#97;&amp;#116;&amp;#99;&amp;#104;&amp;#101;&amp;#110;&amp;#46;&amp;#109;&amp;#101;"&gt;catchen@catchen.me&lt;/a&gt;。对于全职的前端工程师，我期望你熟悉 web app 的开发与调试，如果我让你手写一个 HTTP GET 请求你连个大概都写不出来，那我就要怀疑你平时都有多少时间对着 debug console 调试 AJAX 代码了。对于实习生，我期望你至少有扎实的 web page 基础，能够用简洁的代码实现符合语义的页面。至于豌豆荚提供什么？就是我前面所说的，但还有个前提&amp;#8212;&amp;#8212;至少我们要有一致的品味。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/2287034.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2011/12/14/front-end-engineers-career-path.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/FvNIfr60MdUiI17493iC9FAuq6c/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FvNIfr60MdUiI17493iC9FAuq6c/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/FvNIfr60MdUiI17493iC9FAuq6c/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FvNIfr60MdUiI17493iC9FAuq6c/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/dotNET?a=kUG7ttYaP2A:bsCkgSgBS7w:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=kUG7ttYaP2A:bsCkgSgBS7w:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=kUG7ttYaP2A:bsCkgSgBS7w:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=kUG7ttYaP2A:bsCkgSgBS7w:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=kUG7ttYaP2A:bsCkgSgBS7w:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=kUG7ttYaP2A:bsCkgSgBS7w:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/kUG7ttYaP2A" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2011/12/14/front-end-engineers-career-path.html</feedburner:origLink></entry><entry><id>http://www.cnblogs.com/cathsfz/archive/2011/12/13/2285588.html</id><title type="text">看对的书 (Part 1 - Tribal Leadership)</title><summary type="text">很久很久之前（准确来说是两年前），我开始了一个系列叫做「看对的书」，然后只写了一篇 intro 就没有写下去。背后的原因包括，我确实越来越懒了，同时写书评也不容易——剧透太多了，大家觉得没必要去看原书；剧透太少了，大家觉得看不出书的吸引力在哪里。如何把握剧透多少，怎么剧透才能吸引大家取看原书，我还没有想好，不过我可以先尝试着写。我今天想要推荐的书是《Tribal Leadership》，也就是《部落领导力》，不过这本书暂时没有中文版。尽管这是一本讲「领导力」的书，但里面说的并不是你要做什么事情才能拥有领导力。书中说得更多的是部落——只有在你理解到部落的 5 个阶段后，你才能够使用你的能力把你的</summary><published>2011-12-13T01:27:00Z</published><updated>2011-12-13T01:27:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2011/12/13/2285588.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/qOsYzr3ORm8/2285588.html" /><content type="html">&lt;p&gt;很久很久之前（准确来说是两年前），我开始了一个系列叫做&lt;a href="http://www.cnblogs.com/cathsfz/archive/2009/12/21/1628540.html"&gt;「看对的书」&lt;/a&gt;，然后只写了一篇 intro 就没有写下去。背后的原因包括，我确实越来越懒了，同时写书评也不容易&amp;#8212;&amp;#8212;剧透太多了，大家觉得没必要去看原书；剧透太少了，大家觉得看不出书的吸引力在哪里。如何把握剧透多少，怎么剧透才能吸引大家取看原书，我还没有想好，不过我可以先尝试着写。&lt;/p&gt;&lt;p&gt;我今天想要推荐的书是《&lt;a href="http://www.amazon.com/gp/product/B0012GTZFC/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=baindo-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B0012GTZFCC"&gt;Tribal Leadership&lt;/a&gt;》，也就是《部落领导力》，不过这本书暂时没有中文版。尽管这是一本讲「领导力」的书，但里面说的并不是你要做什么事情才能拥有领导力。书中说得更多的是部落&amp;#8212;&amp;#8212;只有在你理解到部落的 5 个阶段后，你才能够使用你的能力把你的部落往更高的阶段迁移。那到底什么是部落了？不知道是否还有人记得「为什么早期手机只能存储 150 个联系人」的问题，答案是 150 是 &lt;a href="http://en.wikipedia.org/wiki/Dunbar's_number"&gt;Dunbar&amp;#8217;s number&lt;/a&gt;（&lt;a href="http://zh.wikipedia.org/wiki/%E9%82%93%E5%B7%B4%E6%95%B0"&gt;邓巴数&lt;/a&gt;），这个数是一个人能够维持的熟人数量。而一个部落，就是有 20 到 150 个熟人组成的组织。如果一个组织的人数超过 150 人，它会自动分裂为多个部落，从而形成一个由部落组成的部落。&lt;/p&gt;&lt;p&gt;在部落形成后，你就可以尝试去识别他们到底处于哪一个阶段。这本书的作者发现了一个有效识别部落所处阶段的方法，就是看部落成员说话的方式。例如说，如果你在医院的电梯里听到 3 个医生在交谈&amp;#8212;&amp;#8212;第一个医生说「你看到我在《新英格兰医学杂志》上发表的文章了吗？」第二个医生用讽刺的语气说「看到了，印象深刻。然而在你忙于做研究的时候，我完成的手术比楼层内的任何一个人都要多。」大家笑了一笑，第三个医生接着说「当你忙于码字的时候，当你忙于切肉的时候，我教授的新人比本院的任何人都要多。」大家又笑了，互相拍拍肩膀走出电梯。这 3 个医生所说的话，背后的意思都是一致的&amp;#8212;&amp;#8212;「我比你牛，而且我有数据支撑。」这时候，你就可以认定这是一个处于第 3 阶段的部落了。&lt;/p&gt;&lt;p&gt;部落的 5 个阶段对应的话语都很有特色，所以要鉴别一个部落处于哪个阶段并不难：&lt;/p&gt;&lt;ol&gt;     &lt;li&gt;「人生就是个悲剧」&amp;#8212;&amp;#8212;第 1 阶段的人认为没有谁的生活是好过的。&lt;/li&gt;     &lt;li&gt;「我就是个悲剧」&amp;#8212;&amp;#8212;第 2 阶段的人看得到别人生活美好的一面，只是觉得自己的生活很糟糕。&lt;/li&gt;     &lt;li&gt;「我很牛，但你不是」&amp;#8212;&amp;#8212;第 3 阶段的人觉得自己的生活不错，因为自己很牛，但觉得自己身边的人都不如自己牛，也不愿意给予自己更多支持，因此自己没办法完成更大的目标。&lt;/li&gt;     &lt;li&gt;「我们很牛，但你们不是」&amp;#8212;&amp;#8212;第 4 阶段的人觉得自己的部落很牛，而且竞争对手都不如自己。&lt;/li&gt;     &lt;li&gt;「世界很美好」&amp;#8212;&amp;#8212;第 5 阶段的人只专注于做一些很崇高的事业，无视竞争对手的存在。&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;就美国而言，有一半的人处于第 3 阶段的部落，而且绝大多数都是专业化的知识工作者，例如医生、律师，当然也包括工程师。由于现代教育体系奖励很牛的小朋友，而不是奖励很会跟别人合作的小朋友，所以大多数专业人士到达第 3 阶段后就难以获得突破，除非他们能够获得顿悟。顿悟的来源通常有两种：要么是明白到自己想要追求的目标实在是太大，无论自己有多牛都不可能实现，所以必须放弃单干的幻想，学会通过支持别人来换取自己的目标得以实现；要么是觉得自己足够牛了，获取更多的个人成就已经变得没意思了，那还不如帮助别人实现他们的目标。只有在不看重个人成就高低的前提下，部落才可能进入第 4 阶段。&lt;/p&gt;&lt;p&gt;此外，美国还有四分之一的人处于第 2 阶段。他们往往有一个处于第 3 阶段的老板，这些老板为了保证自己能够获得最好的职业发展而只雇佣能力比自己低的第 2 或第 3 阶段下属。第 2 阶段的人往往就在这样的老板手下痛苦挣扎，看着老板的生活很好，同时感觉自己生活很悲催，因此发誓将来成为老板的话一定不能这样子。但是，等第 2 阶段的人成为老板并且过上好生活后，他们会进入第 3 阶段，成为他们过去痛恨的老板。这时候，他们就不想放弃晋升带来的冲劲了，希望能够更好地证明自己也是很牛的，同时迅速往上爬。&lt;/p&gt;&lt;p&gt;这背后的道理是，人的行为变更是不可能跨越阶段进行的。书中甚至把每一个阶段分成了 3 个子阶段&amp;#8212;&amp;#8212;初期、中期、末期。你不能指望一个人在脱离第 2 阶段后能够立即拥有第 4 阶段的意识，放弃个人成长优势而去帮助他人。因此，如果你想要成为一个部落领袖的话，你需要能够鉴别部落所处的阶段，同时你自己要能穿梭于不同的阶段，去到部落所处的阶段然后把部落带到更高级的阶段。至于每一个子阶段的特征是什么，以及如何能够帮助部落迁移到更高的阶段，我在这里就不详细解释了，大家去看书中的内容吧。如果不愿意看书，到 &lt;a href="http://www.triballeadership.net/"&gt;Tribal Leader 官网&lt;/a&gt;上下载有声读物也可以，而且还是免费的，只不过需要注册一下。如果买书的话，我推荐买 &lt;a href="http://www.amazon.com/gp/product/B0012GTZFC/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=baindo-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B0012GTZFC"&gt;Kindle 版&lt;/a&gt;，因为看起来方便，不用想方设法把一本进口书弄回国内。&lt;/p&gt;&lt;p&gt;我之所以选择推荐这本书，是因为我很认同里面所说的部落阶段划分。我经历过不少第 3 阶段的部落&amp;#8212;&amp;#8212;名校、名企大多如此。我知道，自己再牛能做的事情也有限；但是，我不知道如何能够突破。这本书解释清楚了一个问题&amp;#8212;&amp;#8212;个人要突破，必须先放下职业发展的得失。然后还要推动整个部落突破，这需要部落拥有核心价值观和高尚的目标，同时制定达成高尚目标的战略。我相信国内也会有很多优秀的工程师卡在第 3 阶段找不到突破口，所以我建议大家读读这本书看看有什么启发。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/2285588.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2011/12/13/2285588.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/KyQhNmIeTQRzMZjqvmFdNLnj-oY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KyQhNmIeTQRzMZjqvmFdNLnj-oY/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/KyQhNmIeTQRzMZjqvmFdNLnj-oY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KyQhNmIeTQRzMZjqvmFdNLnj-oY/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/dotNET?a=qOsYzr3ORm8:aq-oJ1qEwUg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=qOsYzr3ORm8:aq-oJ1qEwUg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=qOsYzr3ORm8:aq-oJ1qEwUg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=qOsYzr3ORm8:aq-oJ1qEwUg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=qOsYzr3ORm8:aq-oJ1qEwUg:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=qOsYzr3ORm8:aq-oJ1qEwUg:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/qOsYzr3ORm8" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2011/12/13/2285588.html</feedburner:origLink></entry><entry><id>http://www.cnblogs.com/cathsfz/archive/2011/12/04/2276058.html</id><title type="text">工具：开发者使用，企业埋单</title><summary type="text">我喜欢写一些小工具来简化我的工作，通常是一些小组件。利用这些小工具我可以提高自己代码的可读性，同时维持我的 DRY (Don't Repeat Yourself) 洁癖。工具对我来说很重要，因为时间对我来说很宝贵。能够用工具自动化完成的事情绝对不手工反复操作，能够用工具避免人为错误的地方一定让工具来确保质量。使用工具节省下来的时间用于玩游戏的话，绝对是值得的！过去我主要做工具给自己用，或者是给自己所在的团队使用，所以觉得自己就是用户，自己设计产品给自己用肯定是没问题的。然而做工具给更多人用呢？看起来就不那么容易了。之前在百度经历过 Tangram 的需求分析与设计阶段，每个产品线都派出</summary><published>2011-12-04T13:18:00Z</published><updated>2011-12-04T13:18:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2011/12/04/2276058.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/AKwiqkJphEk/2276058.html" /><content type="html">&lt;p&gt;我喜欢写一些小工具来简化我的工作，通常是一些小组件。利用这些小工具我可以提高自己代码的可读性，同时维持我的 DRY (Don't Repeat Yourself) 洁癖。工具对我来说很重要，因为时间对我来说很宝贵。能够用工具自动化完成的事情绝对不手工反复操作，能够用工具避免人为错误的地方一定让工具来确保质量。使用工具节省下来的时间用于玩游戏的话，绝对是值得的！&lt;/p&gt;&lt;p&gt;过去我主要做工具给自己用，或者是给自己所在的团队使用，所以觉得自己就是用户，自己设计产品给自己用肯定是没问题的。然而做工具给更多人用呢？看起来就不那么容易了。之前在百度经历过 &lt;a href="http://tangram.baidu.com/"&gt;Tangram&lt;/a&gt; 的需求分析与设计阶段，每个产品线都派出了前端工程师代表参与需求评审，通过多轮的辩论来决定哪些需求是要覆盖到的，哪些需求是可以忽略的。最后做出来的产品？只获得了一部分产品线的认可。推广也得不到公司层面的支持。&lt;/p&gt;&lt;p&gt;工具的需求难道不是来自使用者吗？使用者就是坐在自己隔壁并且做在同一领域开发的团队，这样都可能出现需求理解偏差吗？这个问题我一直没有想明白。&lt;/p&gt;&lt;p&gt;后来我到了 Yahoo，在对 YUI 表示恶心一端时间后，我开始理解到恶心背后的原因。无论是 Yahoo YUI 还是 Google Closure，目的都是一样的&amp;#8212;&amp;#8212;通过工程性来保证，就算工程师素质很低，产出的代码质量很差，项目也能交付。我当时只是觉得大公司为了交付而造出一些压制工程师创意的工具是很恶心的事情，而没有考虑到这是否跟我要思考的问题相关。&lt;/p&gt;&lt;p&gt;到了&lt;a href="http://wandoujia.com/"&gt;豌豆实验室&lt;/a&gt;后，我开始着手构建一些前端工具，这一次我终于明白到需求源自于哪里了。工具确实是给开发者使用的，而需求来源于企业。企业在不同时期可能有不同的需求。企业刚刚成立时，肯定希望尽快把产品概念做出来，所以要求工具能够快速实现产品原型。等到产品成功发布后，企业又希望能够拓宽产品的市场，于是要求工具能保证产品复杂度增加的同时维护成本可控。随后企业会同时开发多个产品，这时候就会要求工具能够帮助降低产品共有的成本。&lt;/p&gt;&lt;p&gt;在文化相对开放的企业，工具的选择看起来是由开发者决定的。开发者爱用什么工具就用什么工具，企业不干预。实际上，企业不是因为文化相对开放而不干预，而是缺乏干预的动机。如果提供足够的动机，例如使用某种工具能够使得利润翻倍，任何企业都会愿意为之而干预。不干预的企业，往往只是没看清楚工具能给它的成本造成什么影响而已。&lt;/p&gt;&lt;p&gt;此外，在企业决定干预开发者使用什么工具时，企业肯定不会在乎工具使用起来爽不爽。开发者不爽是一种成本，但把这项成本计算在内后，如果特定的工具还是能降低企业成本，企业还是会选择这种工具。至于开发者不爽的成本，是将来可以通过优化工具来避免的。&lt;/p&gt;&lt;p&gt;回顾之前百度 Tangram 推广不利的问题，答案就显而易见了。为什么 Tangram 缺乏百度自顶而下的支持？为什么作为同类产品的 YUI 却能得到 Jerry Yang 的支持并在整个 Yahoo 得到推广？因为 Tangram 覆盖了开发者的需求，但没有很好地考虑到百度作为企业的需求，尤其是没有考虑到这种需求在百度成长中的变化。&lt;/p&gt;&lt;p&gt;Tangram 在设计初期，考虑的是如网页搜索这样的产品线的需求，要求 JavaScript 尽可能优化，少占用带宽。于是 Tangram 设计为支持函数级别的依赖项。然而等 Tangram 做出来时，贴吧则决定直接使用 jQuery。这是因为网页搜索和贴吧的成长进入了两个不同的阶段，网页搜索要求在细节上继续优化，而贴吧要求通过快速迭代尝试不同的产品概念。需求差异如此之大，一个工具又怎么可能都覆盖到？既然 Tangram 没办法在多个产品线证明它能有效降低成本，百度又如何下决心为它埋单？&lt;/p&gt;&lt;p&gt;这同时解释了一个问题：为什么企业内部工具应该由架构师来主导。（YUI 及后来的 Cocktails 都是由架构师主导的。）因为架构师的职位能让他更好地解企业的需求，同时他的位置也更靠近老板。此外，架构师在一家企业内的资历也会对此有帮助。跟随这家企业成长的经历能让他更好地看到这家企业将来的需求。因此，企业内部工具不是随随便便挑一个技术足够好的人来主导就行的，他的职位会影响到他是否胜任此项工作。&lt;/p&gt;&lt;p&gt;对此我可以补充一点信息。我见过 Tangram 推广所用的幻灯片，里面堆满了对开发者有用的信息&amp;#8212;&amp;#8212;Tangram 在 gzip 前后跟其他库的体积比较、能够覆盖到多少功能点、在百度产品线的代码覆盖率能够达到多少。（代码覆盖率还曾经是 Tangram 的执行目标。）然而里面缺乏对老板有用的信息&amp;#8212;&amp;#8212;使用 Tangram 能够省多少钱。由于位置的缘故，做 Tangram 的团队确实缺乏获取相关信息的途径，也更没办法获取到老板对不同产品线的长远规划。&lt;/p&gt;&lt;p&gt;再来看看 Yahoo 架构师 &lt;a href="http://www.linkedin.com/in/brunofr"&gt;Bruno&lt;/a&gt; 是怎么介绍 Cocktails 项目的。幻灯片里面没有一点开发者关心的信息，前半部分说的都是 Yahoo 接下来的战略&amp;#8212;&amp;#8212;必须同时抢占桌面、平板、移动 3 个平台，后半部分讲的是如果为这 3 个平台独立开发应有成本有多高，而使用 Cocktails 的话能够缩减多少成本。我听 Bruno 讲完后，就立即明白到这其实是用来向老板要钱的幻灯片，只是顺带再讲一遍以便让开发者知道公司战略而已。&lt;/p&gt;&lt;p&gt;老板实际上不明白也不在乎开发者对工具的需求是什么，幻灯片上他能看懂的就是那一堆 $ 后面跟着的数字。如果你给不出这堆数字，老板对工具的态度就会是既不支持也不反对。因此，找一个足够了解公司现有数字同时知道如何利用技术去改变这些数字的人，对内部工具项目来说尤其重要。这个人往往就是个架构师。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/2276058.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2011/12/04/2276058.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/VEc2UIMHdwhgeIT7Jip8-8QI7tY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VEc2UIMHdwhgeIT7Jip8-8QI7tY/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/VEc2UIMHdwhgeIT7Jip8-8QI7tY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/VEc2UIMHdwhgeIT7Jip8-8QI7tY/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/dotNET?a=AKwiqkJphEk:z0OgSNCdepA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=AKwiqkJphEk:z0OgSNCdepA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=AKwiqkJphEk:z0OgSNCdepA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=AKwiqkJphEk:z0OgSNCdepA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=AKwiqkJphEk:z0OgSNCdepA:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=AKwiqkJphEk:z0OgSNCdepA:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/AKwiqkJphEk" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2011/12/04/2276058.html</feedburner:origLink></entry><entry><id>http://www.cnblogs.com/cathsfz/archive/2011/08/09/2131615.html</id><title type="text">「云端 JavaScript 漫游指南」</title><summary type="text">7 月 30 日在 w3ctech 的 JavaScript 活动广州场 讲了一节「云端 JavaScript 漫游指南」，实质上就是 Node.js 入门讲座。希望通过这一节讲座，让原本熟悉 JavaScript 的前端工程师尝试使用 Node.js 开发一些应用，探索 Node.js 为 Web 开发带来的可能性。这场讲座上主要使用了两个小 demo 来解释 Node.js 的一些基础概念，并且展示了如何把自己的应用部署到云端。需要使用 Node.js ，最起码你要知道 CommonJS Modules 的概念，然后你才能在一个 js 文件里面使用另一个 js 文件实现的功能。这是通过 r</summary><published>2011-08-08T16:00:00Z</published><updated>2011-08-08T16:00:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2011/08/09/2131615.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/-YxpyDPKm1g/2131615.html" /><content type="html">&lt;p&gt;7 月 30 日在 w3ctech 的 &lt;a href="http://www.w3ctech.com/2011/js/schedule"&gt;JavaScript 活动广州场&lt;/a&gt; 讲了一节「云端 JavaScript 漫游指南」，实质上就是 Node.js 入门讲座。希望通过这一节讲座，让原本熟悉 JavaScript 的前端工程师尝试使用 Node.js 开发一些应用，探索 Node.js 为 Web 开发带来的可能性。&lt;/p&gt;&lt;p&gt;这场讲座上主要使用了两个小 demo 来解释 Node.js 的一些基础概念，并且展示了如何把自己的应用部署到云端。需要使用 Node.js ，最起码你要知道 &lt;a href="http://wiki.commonjs.org/wiki/Modules"&gt;CommonJS Modules&lt;/a&gt; 的概念，然后你才能在一个 js 文件里面使用另一个 js 文件实现的功能。这是通过 &lt;code&gt;require&lt;/code&gt; 和 &lt;code&gt;exports&lt;/code&gt; 这两个接口实现了。接下来，你还应该知道 &lt;a href="http://wiki.commonjs.org/wiki/Packages"&gt;CommonJS Packages&lt;/a&gt; 的概念，这样你就可以复用别人写好的包，无需样样从头开发。跟现在非常热门的脚本语言如 Python 、 Ruby 一样， Node.js 也有丰富的包仓库，基本上你能想得到的基础功能都有别人做好的包可以实现。&lt;/p&gt;&lt;p&gt;在有了一个好的产品创意后，你就可以以搭积木的形式构建自己的应用了。由于基础功能都有做好的包了，你可以专注于应用的业务逻辑，快速把产品做出来。在我的讲座中，我讲解了两个我自己编写给自己使用的小应用，一个用于把我的 catchen.biz 链接全部重定向到 catchen.me 去，另一个用于将短地址解析为原地址。关于这两个应用的技术细节我会在将来的文章中解释清楚，在这里我们就先跳过这部分的内容吧。&lt;/p&gt;&lt;p&gt;产品做好后，最后一步就是部署。过去我们需要购买或租用服务器，至少租用虚拟主机， OS 级别的事情还是要自己动手处理的。要在一个全裸的 OS 上把 Node.js 跑起来，还是要安装和配置一堆东西的。幸好现在有了如 &lt;a href="http://www.heroku.com/"&gt;Heroku&lt;/a&gt; 这样的云端解决方案，我们可以完全不管 OS 上跑着的是什么，把写好的 Node.js 做成包推到目标 Git 库上就可以了，所有的依赖项和配置都不需要自己来管理。&lt;/p&gt;&lt;p&gt;上述讲座内容都可以在我的 &lt;a href="http://github.catchen.me/"&gt;Github&lt;/a&gt; 上找到，包括&lt;a href="http://github.catchen.me/nodejs-introduction-slides/?full#Cover"&gt;幻灯片&lt;/a&gt;以及 &lt;a href="https://github.com/CatChen/biz-to-me"&gt;biz-to-me&lt;/a&gt; 和 &lt;a href="http://traceurl.herokuapp.com/"&gt;traceurl&lt;/a&gt; 这两个 demo 。下面的是讲座现场的视频： &lt;a href="http://v.youku.com/v_show/id_XMjkyNzg3NjQw.html"&gt;Youku&lt;/a&gt; | &lt;a href="http://www.youtube.com/playlist?list=PL862408FB7D740D3B"&gt;YouTube&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;embed src='http://player.youku.com/player.php/sid/XMjkyNzg3NjQw/v.swf' quality='high' width='480' height='400' align='center' allowScriptAccess='sameDomain' type='application/x-shockwave-flash'&gt;&lt;/embed&gt;&lt;/p&gt;     &lt;p&gt;关于视频的任何问题，你都可以留言提问。如果你关注 Node.js 开发，欢迎&lt;a href="http://feeds.feedburner.com/CatChen/dotNET"&gt;订阅我的博客&lt;/a&gt;。 &lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/2131615.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2011/08/09/2131615.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/qzioXef4O23SK8XqLohs5cjRlX8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qzioXef4O23SK8XqLohs5cjRlX8/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/qzioXef4O23SK8XqLohs5cjRlX8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qzioXef4O23SK8XqLohs5cjRlX8/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/dotNET?a=-YxpyDPKm1g:613be7UjD6g:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=-YxpyDPKm1g:613be7UjD6g:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=-YxpyDPKm1g:613be7UjD6g:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=-YxpyDPKm1g:613be7UjD6g:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=-YxpyDPKm1g:613be7UjD6g:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=-YxpyDPKm1g:613be7UjD6g:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/-YxpyDPKm1g" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2011/08/09/2131615.html</feedburner:origLink></entry><entry><id>http://www.cnblogs.com/cathsfz/archive/2011/07/08/2100839.html</id><title type="text">为什么 script 标签不能写成自关闭形式</title><summary type="text">今天早上在 Stack Overflow 看到了这个问题： Why don't self-closing script tags work? 。答案给出的解释是，在 XHTML 的标准里面规定非 EMPTY 标签不能使用自关闭形式。注意这里使用的是全大写的 EMPTY ，所以我不把它翻译为「空白」。那么 EMPTY 到底是什么呢？写过 DTD 的人应该知道它是个关键字，用来指明一个标签的内容必须是空白，而不能包含文本内容或子节点。看看具体的例子就很容易明白了：&lt;!ELEMENT img EMPTY&gt;&lt;!ATTLIST img %attrs; src %URI; #RE</summary><published>2011-07-08T05:50:00Z</published><updated>2011-07-08T05:50:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2011/07/08/2100839.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/aW2YuBqZPOY/2100839.html" /><content type="html">&lt;p&gt;今天早上在 Stack Overflow 看到了这个问题： &lt;a href="http://stackoverflow.com/questions/69913/why-dont-self-closing-script-tags-work"&gt;Why don't self-closing script tags work?&lt;span id="goog_2120592535"&gt;&lt;/span&gt;&lt;/a&gt; 。答案给出的解释是，在 XHTML 的标准里面规定非 &lt;code&gt;EMPTY&lt;/code&gt; 标签不能使用自关闭形式。注意这里使用的是全大写的 &lt;code&gt;EMPTY&lt;/code&gt; ，所以我不把它翻译为「空白」。&lt;/p&gt;&lt;p&gt;那么 &lt;code&gt;EMPTY&lt;/code&gt; 到底是什么呢？写过 DTD 的人应该知道它是个关键字，用来指明一个标签的内容必须是空白，而不能包含文本内容或子节点。看看具体的例子就很容易明白了：&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;!ELEMENT img EMPTY&amp;gt;&lt;br /&gt;&amp;lt;!ATTLIST img&amp;nbsp; %attrs;&lt;br /&gt;&amp;nbsp; src         %URI;          #REQUIRED&lt;br /&gt;&amp;nbsp; alt         %Text;         #REQUIRED&lt;br /&gt;&amp;nbsp; longdesc    %URI;          #IMPLIED&lt;br /&gt;&amp;nbsp; height      %Length;       #IMPLIED&lt;br /&gt;&amp;nbsp; width       %Length;       #IMPLIED&lt;br /&gt;&amp;nbsp; usemap      %URI;          #IMPLIED&lt;br /&gt;&amp;nbsp; ismap       (ismap)        #IMPLIED&lt;br /&gt;&amp;nbsp; &amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;这是 &lt;code&gt;img&lt;/code&gt; 标签的定义。 &lt;code&gt;ELEMENT&lt;/code&gt; 关键字说明它是一个元素， &lt;code&gt;EMPTY&lt;/code&gt; 关键字说明它的内容必须是空白。因此，我们可以使用自关闭形式：&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;img src="image.png" alt="some image" /&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;留意 &lt;code&gt;ATTLIST&lt;/code&gt; 里面声明了两个属性是 &lt;code&gt;#REQUIRED&lt;/code&gt; 的，所以必须提供。&lt;/p&gt;&lt;p&gt;接下来我们再看看 &lt;code&gt;script&lt;/code&gt; 标签的定义：&lt;/p&gt;&lt;p&gt;&lt;code&gt;&amp;lt;!ELEMENT script (#PCDATA)&amp;gt;&lt;br /&gt;&amp;lt;!ATTLIST script&lt;br /&gt;&amp;nbsp; id          ID             #IMPLIED&lt;br /&gt;&amp;nbsp; charset     %Charset;      #IMPLIED&lt;br /&gt;&amp;nbsp; type        %ContentType;  #REQUIRED&lt;br /&gt;&amp;nbsp; language    CDATA          #IMPLIED&lt;br /&gt;&amp;nbsp; src         %URI;          #IMPLIED&lt;br /&gt;&amp;nbsp; defer       (defer)        #IMPLIED&lt;br /&gt;&amp;nbsp; xml:space   (preserve)     #FIXED 'preserve'&lt;br /&gt;&amp;nbsp; &amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;可以看到 &lt;code&gt;script&lt;/code&gt; 标签通过 &lt;code&gt;(#PCDATA)&lt;/code&gt; 声明了它的内部允许包含 CDATA 数据，因此它不是一个带 &lt;code&gt;EMPTY&lt;/code&gt; 关键字的标签，也就不可能使用自关闭的写法。&lt;/p&gt;&lt;p&gt;总结一下：有空看看 &lt;a href="http://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; 还是挺有意思的。学习 DTD 的语法，并且看看 &lt;a href="http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_XHTML-1.0-Strict"&gt;XHTML 1.0 Strict DTD&lt;/a&gt; 也会帮助你增加对 XHTML 的理解。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/2100839.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2011/07/08/2100839.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/zcwVj2hZ_-5pyFtYcLUUiGDgDI8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zcwVj2hZ_-5pyFtYcLUUiGDgDI8/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/zcwVj2hZ_-5pyFtYcLUUiGDgDI8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/zcwVj2hZ_-5pyFtYcLUUiGDgDI8/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/dotNET?a=aW2YuBqZPOY:eFPOss2DFos:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=aW2YuBqZPOY:eFPOss2DFos:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=aW2YuBqZPOY:eFPOss2DFos:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=aW2YuBqZPOY:eFPOss2DFos:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=aW2YuBqZPOY:eFPOss2DFos:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=aW2YuBqZPOY:eFPOss2DFos:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/aW2YuBqZPOY" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2011/07/08/2100839.html</feedburner:origLink></entry><entry><id>http://www.cnblogs.com/cathsfz/archive/2011/05/29/2062382.html</id><title type="text">在 JavaScript 中监听 IME 键盘输入事件</title><summary type="text">在 JavaScript 中监听用户的键盘输入是很容易的事情，但用户一旦使用了输入法，问题就变得复杂了。输入法应当如何触发键盘事件呢？是每一下击键都触发一次事件，还是选词完毕才触发事件呢？整句输入又该如何触发事件呢？不同的操作系统和不同的浏览器对此有不同的看法。在最糟糕的情况下，用户使用输入法后浏览器就只触发一次 keydown ，之后就没有任何的键盘事件了。这对于 Suggestion 控件的实现来说是个大问题，因为 Suggestion 控件需要监听文本输入框的变化，而事件是最准确也最节省计算资源的做法，如果换成轮询的话性能就可能受到影响。 首先，要监听启用输入法后的击键事件应当使用 ke</summary><published>2011-05-29T14:26:00Z</published><updated>2011-05-29T14:26:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2011/05/29/2062382.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/uwXdPCFkWts/2062382.html" /><content type="html">&lt;p&gt;在 JavaScript 中监听用户的键盘输入是很容易的事情，但用户一旦使用了输入法，问题就变得复杂了。输入法应当如何触发键盘事件呢？是每一下击键都触发一次事件，还是选词完毕才触发事件呢？整句输入又该如何触发事件呢？不同的操作系统和不同的浏览器对此有不同的看法。在最糟糕的情况下，用户使用输入法后浏览器就只触发一次 &lt;code&gt;keydown&lt;/code&gt; ，之后就没有任何的键盘事件了。这对于 Suggestion 控件的实现来说是个大问题，因为 Suggestion 控件需要监听文本输入框的变化，而事件是最准确也最节省计算资源的做法，如果换成轮询的话性能就可能受到影响。 &lt;/p&gt;&lt;p&gt;首先，要监听启用输入法后的击键事件应当使用 &lt;code&gt;keydown&lt;/code&gt; 事件，这是信息最丰富的一个事件，因为在启用输入法后别的键盘事件可能不会被触发。其次，大多数操作系统和浏览器都实现了一个事实标准，就是在用户使用输入法输入时， &lt;code&gt;keydown&lt;/code&gt; 事件传入的 &lt;code&gt;keyCode&lt;/code&gt; 取值为 &lt;code&gt;229&lt;/code&gt; 。然而触发 &lt;code&gt;keydown&lt;/code&gt; 的频率是不确定的，有些情况下每一下击键都触发事件，有些情况下只有选词完毕才触发事件。这时候，如果我们还是要实时监控文本框的内容变化，就必须使用轮询了。 &lt;/p&gt;&lt;p&gt;&lt;code&gt;var timer;     &lt;br /&gt;var imeKey = 229;      &lt;br /&gt;      &lt;br /&gt;function keydownHandler (e) {      &lt;br /&gt;&amp;nbsp; clearInterval(timer)      &lt;br /&gt;&amp;nbsp; if (e.keyCode == imeKey) {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; timer = setInterval(checkTextValue, 50);      &lt;br /&gt;&amp;nbsp; } else {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; checkTextValue();      &lt;br /&gt;&amp;nbsp; }      &lt;br /&gt;}      &lt;br /&gt;      &lt;br /&gt;function checkTextValue() {      &lt;br /&gt;&amp;nbsp; /* handle input text change */      &lt;br /&gt;}&lt;/code&gt; &lt;/p&gt;&lt;p&gt;Opera 是一款有趣的浏览器，别人做的事情它都不做，别人都不做的事情它都喜欢做。例如说，它偏偏不支持 &lt;code&gt;keyCode == 229&lt;/code&gt; 这个事实标准，而要使用 &lt;code&gt;keyCode == 197&lt;/code&gt; 来表示输入法的使用。因此，你需要在上述代码的基础上做一下改良，如果监测到是 Opera 浏览器，就换一个 keyCode 常量来做比较。 &lt;/p&gt;&lt;p&gt;&lt;code&gt;var imeKey = (UA.Opera == 0) ? 229 : 197;&lt;/code&gt; &lt;/p&gt;&lt;p&gt;最后，还有一个更不受重视的浏览器叫做 Firefox for Mac 。估计是因为 Mac 版本对于 Mozilla 来说实在是太不重要了，所以很多 Windows 版本都没问题的地方 Mac 版本就会出小问题，例如说对上述事件的支持。 Firefox for Mac 不会出现 &lt;code&gt;keyCode == 229&lt;/code&gt; 的情况，而且在输入法启用后只有第一下击键会触发 &lt;code&gt;keydown&lt;/code&gt; 事件，因此只能在击键后一直使用轮询。 &lt;/p&gt;&lt;p&gt;&lt;code&gt;if (e.keyCode == imeKey || UA.Firefox &amp;gt; 0 &amp;amp;&amp;amp; UA.OS == 'Macintosh') {&lt;/code&gt; &lt;/p&gt;&lt;p&gt;在添加了这两项改进后，实时监控文本框的变化就没有问题了，即使用户启用了输入法。完整的代码如下： &lt;/p&gt;&lt;p&gt;&lt;code&gt;var timer;     &lt;br /&gt;var imeKey = (UA.Opera == 0) ? 229 : 197;      &lt;br /&gt;      &lt;br /&gt;function keydownHandler (e) {      &lt;br /&gt;&amp;nbsp; clearInterval(timer)      &lt;br /&gt;&amp;nbsp; if (e.keyCode == imeKey || UA.Firefox &amp;gt; 0 &amp;amp;&amp;amp; UA.OS == 'Macintosh') {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; timer = setInterval(checkTextValue, 50);      &lt;br /&gt;&amp;nbsp; } else {      &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; checkTextValue();      &lt;br /&gt;&amp;nbsp; }      &lt;br /&gt;}      &lt;br /&gt;      &lt;br /&gt;function checkTextValue() {      &lt;br /&gt;&amp;nbsp; /* handle input text change */      &lt;br /&gt;}&lt;/code&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/2062382.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2011/05/29/2062382.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ejNRodQVlDh-NcFw3xqeMZ-G-Us/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ejNRodQVlDh-NcFw3xqeMZ-G-Us/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/ejNRodQVlDh-NcFw3xqeMZ-G-Us/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ejNRodQVlDh-NcFw3xqeMZ-G-Us/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/dotNET?a=uwXdPCFkWts:CpCR5BvCPzg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=uwXdPCFkWts:CpCR5BvCPzg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=uwXdPCFkWts:CpCR5BvCPzg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=uwXdPCFkWts:CpCR5BvCPzg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=uwXdPCFkWts:CpCR5BvCPzg:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=uwXdPCFkWts:CpCR5BvCPzg:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/uwXdPCFkWts" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2011/05/29/2062382.html</feedburner:origLink></entry><entry><id>http://www.cnblogs.com/cathsfz/archive/2010/12/24/1916106.html</id><title type="text">Tangram 前端库通过 Github 开源了</title><summary type="text">Tangram 是百度内部一直在开发和使用的前端库之一，功能与 jQuery 、 Prototype 等库类似，主要功能是简化 DOM 操作，并且扩展 JavaScript 语言。这部分功能准确来说属于 Tangram Core ，另外一个叫做 Tangram Component 的库提供一些类似 YUI 、 Sencha 这个级别的组件。之前 Tangram 说要开源很久了，一直卡在流程上，并且也有人觉得必须把库做得足够好了才好意思拿出来开源。我个人的看法是，跟 John Resig 的一样，前端库应该从第一天开始就开源，因为就算你不开源别人也一样能看到，所以还不如开放出来接受别人的贡献。如</summary><published>2010-12-24T08:20:00Z</published><updated>2010-12-24T08:20:00Z</updated><author><name>Cat Chen</name><uri>http://www.cnblogs.com/cathsfz/</uri></author><link rel="alternate" href="http://www.cnblogs.com/cathsfz/archive/2010/12/24/1916106.html" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/CatChen/dotNET/~3/V7y_s75O6xs/1916106.html" /><content type="html">&lt;p&gt;&lt;a href="http://tangram.baidu.com/"&gt;Tangram&lt;/a&gt; 是百度内部一直在开发和使用的前端库之一，功能与 jQuery 、 Prototype 等库类似，主要功能是简化 DOM 操作，并且扩展 JavaScript 语言。这部分功能准确来说属于 Tangram Core ，另外一个叫做 Tangram Component 的库提供一些类似 YUI 、 Sencha 这个级别的组件。&lt;/p&gt;&lt;p&gt;之前 Tangram 说要开源很久了，一直卡在流程上，并且也有人觉得必须把库做得足够好了才好意思拿出来开源。我个人的看法是，跟 John Resig 的一样，前端库应该从第一天开始就开源，因为就算你不开源别人也一样能看到，所以还不如开放出来接受别人的贡献。如果你写得不好，就算你不开源，别人要看也还是能看得到的，所以还是直接把代码晒出来好了，看得不顺眼的可以直接说，实在看不下去了可以动手改，改完了再把代码贡献回来。&lt;/p&gt;&lt;p&gt;说完了我对开源的看法，接下来我们看看 Tangram 和 Git 分别有什么好，先从 Tangram 说起。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Tangram 能做什么&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;熟悉我的人都知道，我从来不讨论哪个工具更好的，我只讨论在特定的情况下哪个工具更适用。因此，我们来看一下什么情况下 Tangram 是适用的。&lt;/p&gt;&lt;p&gt;Tangram 的总体设计很大程度上是参考了 Mootools 的做法，就是将框架拆散到函数的级别，你可以引用单个函数，而不一定要加载整个库。这样做的好处是节省带宽流量，尤其适用于那些流量很大但 AJAX 功能不多的网站。百度的很多服务流量都不小，而且常用页面上需要的 AJAX 功能也不多，因此 Tangram 成为了一个很好的解决方案。&lt;/p&gt;&lt;p&gt;那么什么情况下 Tangram 不适用呢？如果你要写一个 AJAX Web App ， Tangram 就没有什么特别的优势了，除非你尤其熟练使用 Tangram 。一个 AJAX Web App 本身就依赖于库中大量分散的功能，把一个库拆分到函数级别并没有什么意义。当然，在 AJAX Web App 中， Tangram 也没有什么明显的劣势，跟 jQuery 、 Prototype 都差不多，这时候就由团队成员对不同库的使用熟练程度来决定选用哪个库了。&lt;/p&gt;&lt;p&gt;现在 Tangram 的最大弱势在于，它缺乏一种机制让你对页面逻辑的描述变得流畅（ fluent ），而这正是我们使用 DSL 时所追求的。过去我也说过 &lt;a href="http://www.cnblogs.com/cathsfz/archive/2009/08/17/1547361.html"&gt;jQuery 是一种 DSL&lt;/a&gt; ，它允许你用一种很流畅的语言来描述页面的交互行为，这使得页面交互行为变得很容易管理&amp;#9472;&amp;#9472;读懂别人写的 jQuery 页面并不难，在上面做调整也很简单。这是 Tangram 为了减少下载体积做作出的牺牲，不过我希望它将来可以通过编译工具等方法来弥补这个缺陷&amp;#9472;&amp;#9472;例如说，我还是用某一种 DSL 来描述页面交互，然后这种 DSL 能够被编译为 Tangram 代码。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Git 有什么好&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;为什么选择开源到 Github ？ Git 到底有什么好处？我觉得一个简单的例子就能很好地说明问题。&lt;/p&gt;&lt;p&gt;例如说，你想看看 Tangram 的源代码，那么你可以直接打开 &lt;a href="https://github.com/BaiduFE/Tangram-base"&gt;Tangram Github&lt;/a&gt; 的首页，然后以只读的方式把代码都复制到本地。&lt;/p&gt;&lt;p&gt;&lt;code&gt;git clone https://github.com/BaiduFE/Tangram-base.git&lt;/code&gt;&lt;/p&gt;&lt;p&gt;读着读着，你觉得 Tangram 写得也不是那么好，想改改看。于是你回到刚才那个页面上，点 Fork 按钮，然后就相当于把 BaiduFE 下面的 Tangram 项目整个复制到你个人帐号下了。你当然拥有你个人帐号下 Tangram 项目的完全读写权限啦，这时候你就可以把它复制到本地了。&lt;/p&gt;&lt;p&gt;&lt;code&gt;git clone git@github.com:CatChen/Tangram-base.git&lt;/code&gt;&lt;/p&gt;&lt;p&gt;可以看到，这是我的帐号（ CatChen ）下的 Tangram ，不再是 BaiduFE 下面的。这时候你就可以随意改动啦，改动完提交就是了。&lt;/p&gt;&lt;p&gt;&lt;code&gt;git commit -a -m 'Tangram improvement'&lt;/code&gt;&lt;/p&gt;&lt;p&gt;如果你习惯使用 SVN 或者 CVS ，那么你需要注意啦， Git 的提交都是本地的，不会提交到服务器上去。你 Github 帐号下的 Tangram 是一个仓库，你本地编辑的则是另外一个仓库。别忘记了，你刚刚是用克隆命令把 Github 上的仓库复制下来的。所以在提交后，你还必须用推送命令把本地仓库复制回 Github 去。&lt;/p&gt;&lt;p&gt;&lt;code&gt;git push origin master&lt;/code&gt;&lt;/p&gt;&lt;p&gt;在这里， origin 是一个远程仓库的别名。因为你本地的仓库是从 Github 上克隆下来的，所以 Github 上的仓库叫做 origin 。默认情况下，仓库只有一个分支，叫做 master ，所以你要把本地仓库推送到这个分支上去。&lt;/p&gt;&lt;p&gt;这时候，你自己的 Tangram 是更新了。如果你希望 Tangram 的官方版本也接受这个更新的话，你可以点击页面上的 Pull Request 按钮，这时候 Tangram 的管理员就可以考虑从你这里把更新拉取到官方版本上去。&lt;/p&gt;&lt;p&gt;如果你在开发自己版本的 Tangram 时，看到别人的 Fork 有更新了，并且也想要那个更新，怎么办呢？你可以主动地从别人那里拉取，然后 Git 就会帮你完成合并。例如说，我发现 Leeight 那里在做的一个 Tangram 升级不错，尽管他还没完成这个升级，也没提交到官方版本中去，但我就可以先把这部分升级拉取到我本地的仓库中来。&lt;/p&gt;&lt;p&gt;&lt;code&gt;git pull https://github.com/leeight/Tangram-base.git&lt;/code&gt;&lt;/p&gt;&lt;p&gt;这样子，我就能看到 Leeight 所做的升级，跟我正在做的改动是否能够良好地兼容了。或者，我可以先做一些依赖于他的升级的事情，等他把升级做完了并且被官方版本采纳了，我再向官方版本提出 Pull Request 。&lt;/p&gt;&lt;p&gt;可以看到， Git 对开源项目来说是非常友善的，尤其是跟 SVN 和 CVS 做对比的话。 SVN 和 CVS 尽管允许分支，但分支之后通常要到项目完成时才会进行合并，这时候主干已经发生了很多变更，合并起来就相当痛苦。 Git 允许你分支后随时从别人的分支拉取变更，同时你还可以在自己的仓库内做很多子分支，这就使得开源项目管理变得十分方便了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;小结&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;说了那么多道理，建议大家还是动手实验一下比较好。试用 Tangram 无需下载，直接创建一个页面然后引用我们放在 CDN 上的脚本即可，然后可以尝试按照&lt;a href="http://tangram.baidu.com/tangram/tutorial.html"&gt;入门指南&lt;/a&gt;做些简单的东西试试。&lt;/p&gt;&lt;p&gt;使用 Git 管理开源项目的话，推荐阅读 &lt;a href="http://blog.leezhong.com/translate/2010/10/30/a-successful-git-branch.html"&gt;Git 开发管理之道&lt;/a&gt; ，能够让你更好地了解 Git 项目一般是如何进行分支的，以及如何利用这些分支获得更好的灵活性。如果你想看完整的 Git 手册，可以看看 &lt;a href="http://progit.org/book/zh/"&gt;Pro Git&lt;/a&gt; 这本书，作者把这本书放到网上并且免费公开了。&lt;/p&gt;&lt;p&gt;最后插播一下小广告，我的 &lt;a href="https://github.com/CatChen/jsHelpers"&gt;jsHelpers&lt;/a&gt; 库也跟随着 Tangram 开源了，大家可以来 Fork ，我很欢迎大家提交 Pull Request 。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/cathsfz/aggbug/1916106.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/cathsfz/archive/2010/12/24/1916106.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/jgZkyVoavGxsGYukJA-j28MM0mU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/jgZkyVoavGxsGYukJA-j28MM0mU/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/jgZkyVoavGxsGYukJA-j28MM0mU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/jgZkyVoavGxsGYukJA-j28MM0mU/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/dotNET?a=V7y_s75O6xs:8GX2zt4DOMk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=V7y_s75O6xs:8GX2zt4DOMk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?i=V7y_s75O6xs:8GX2zt4DOMk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=V7y_s75O6xs:8GX2zt4DOMk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=V7y_s75O6xs:8GX2zt4DOMk:V-t1I-SPZMU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=V-t1I-SPZMU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CatChen/dotNET?a=V7y_s75O6xs:8GX2zt4DOMk:dnMXMwOfBR0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CatChen/dotNET?d=dnMXMwOfBR0" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CatChen/dotNET/~4/V7y_s75O6xs" height="1" width="1"/&gt;</content><feedburner:origLink>http://www.cnblogs.com/cathsfz/archive/2010/12/24/1916106.html</feedburner:origLink></entry></feed>

