<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_Novice Doodle from Gray Zhang</title><subtitle type="text">Too new to know anything</subtitle><id>http://feed.cnblogs.com/blog/u/34574/rss</id><updated>2011-10-14T10:32:13Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/34574/rss"/><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/09/07/csharp-vnext.html</id><title type="text">下一代C#里的async和await</title><summary type="text">纵观当下的软件工程界，最热门的话题莫过于并行计算，为此C#早在4.0版本中就已经引入了Parallel Linq扩展，简化并行的开发。但是这远远不够，即便Parallel Linq已经提供了极大的便利，但其执行-回调模型依旧打破了编码人员以往对代码就是一行一行顺序执行的习惯思维。因此，C# vNext的主导思想是在这之上再给予更多的进化</summary><published>2011-09-07T09:52:00Z</published><updated>2011-09-07T09:52:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/09/07/csharp-vnext.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/09/07/csharp-vnext.html"/><content type="html">&lt;p&gt;C#发展至今，已经从最初的1.0到了4.0版本，不如来回顾一下各个版本都带来了什么：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;1.0版本 - 基本C#语法。&lt;/li&gt;&#xD;
&lt;li&gt;2.0版本 - 泛型的支持，CLR进行了升级，从根本上支持了运行时泛型。&lt;/li&gt;&#xD;
&lt;li&gt;3.0版本 - LINQ，添加了&lt;code&gt;from&lt;/code&gt; / &lt;code&gt;join&lt;/code&gt;等类SQL关键字，添加了扩展函数，添加了编译期动态类型var关键字。&lt;/li&gt;&#xD;
&lt;li&gt;4.0版本 - dynamic关键字，CLR进行升级，加入DLR，开始对动态进行友好的支持。同时加入动态参数、参数默认值、泛型协变等特性。&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;可以看到，C#从诞生至今，经历2次CLR的升级，以及1次语法层面的扩展，其作为一个语言已经非常便利、强大。但是随着时代的发展，C#依旧在不断前进，而下一代C# vNext又即将诞生。每一代的C#都会在小的语法调整之外，带来一个震撼性的特性。从2.0的泛型、3.0的查询到4.0的动态，每一个版本的C#都有着一个主导的思想，而其他细节的改进和调整则是围绕着这个最基本的思想给予支持。&lt;/p&gt;&#xD;
&lt;p&gt;在这样一路明确的&lt;strong&gt;有且只有一个主导思想&lt;/strong&gt;的升级路线上，下一代的C# vNext的核心思想又是什么呢？纵观当下的软件工程界，最热门的话题莫过于&lt;strong&gt;并行计算&lt;/strong&gt;，为此C#早在4.0版本中就已经引入了Parallel Linq扩展，简化并行的开发。但是这远远不够，即便Parallel Linq已经提供了极大的便利，但其执行-回调模型依旧打破了编码人员以往对&lt;strong&gt;代码就是一行一行顺序执行&lt;/strong&gt;的习惯思维。因此，C# vNext的主导思想是在这之上再给予更多的进化，即C# vNext将着眼于：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;异步&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;C# vNext为了将异步变得更为简单，引入了2个关键字，&lt;strong&gt;async&lt;/strong&gt;和&lt;strong&gt;await&lt;/strong&gt;，下面简单介绍下这2个关键字给我们的编程带来怎么样的改变。&lt;/p&gt;&#xD;
&lt;p&gt;以一个标准的逻辑为例：下载一个远程URI，并将内容输出在界面上，假设我们已经有了显示内容的方法：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;void Display(string text) {&#xD;
    // 不管是怎么实现的&#xD;
}&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;如果用标准的同步式写法，这代码相当之容易：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;void ShowUriContent(string uri) {&#xD;
    using (WebClient client = new WebClient()) {&#xD;
        string text = client.DownloadString(uri);&#xD;
        Display(text);&#xD;
    }&#xD;
}&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;当然这不是我们讨论的重点，同步方式会造成线程的阻塞，必须选择&lt;code&gt;WebClient&lt;/code&gt;下载完成才可以继续运行，如果这个过程在UI线程上执行，则会造成UI无响应的情况。同时网络是非常不可预测的外部条件，很可能因为网络状况不好导致程序长时间没有响应，显然不是我们希望得到的结果。&lt;/p&gt;&#xD;
&lt;p&gt;所以我们又有了异步的方案，.NET中最早的异常编程模式是Begin/End模式，不过&lt;code&gt;WebClient&lt;/code&gt;作为&lt;code&gt;WebRequest&lt;/code&gt;的高层封装，已经把这个模式给封装了：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;void DownloadUri(string uri) {&#xD;
    using (WebClient client = new WebClient()) {&#xD;
        client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(ShowContent);&#xD;
        client.DownloadStringAsync(uri);&#xD;
    }&#xD;
}&#xD;
&#xD;
void ShowContent(object sender, DownloadStringCompletedEventArgs e) {&#xD;
    Display(e.Result);&#xD;
}&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;看看，好好的事情一变成异步，就变得麻烦无比。一个很明确逻辑的方法活生生拆成2个来处理，虽然可以用Lambda或者delegate来使代码上进行简化，但依旧无可避免一段逻辑被拆成两段的痛苦。当更多的异步操作交叉在一起的时候，无论是代码的组织还是逻辑的梳理都会变得更加麻烦。&lt;/p&gt;&#xD;
&lt;p&gt;正因为如此，C# vNext引入了关键了，从语法上对此进行了改进，当使用&lt;strong&gt;async&lt;/strong&gt;和&lt;strong&gt;await&lt;/strong&gt;时，我们的代码会变成这样：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;void async ShowUriContent(string uri) {&#xD;
    using (WebClient client = new WebClient()) {&#xD;
        string text = await client.DownloadStringTaskAsync(uri);&#xD;
        Display(text);&#xD;
    }&#xD;
}&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;悄悄地告诉你，我写上面这段代码的时候，是直接把同步方案的代码复制过来再稍微发了几个字符的&amp;hellip;&amp;hellip;由此可见，在语言级别给予支持后，代码的编写将会是如何地顺畅和简便。这段代码看上去就是一段典型的同步逻辑，创建-下载-显示按部就班，唯一不同地就是在方法声明中加入了&lt;strong&gt;async&lt;/strong&gt;关键字，在&lt;code&gt;DownloadStringTaskAsync&lt;/code&gt;方法的调用时加入了await关键字。就这么神奇地，运行时变成了异步。&lt;code&gt;ShowUriContent&lt;/code&gt;方法会在调用&lt;code&gt;DownloadStringTaskAsync&lt;/code&gt;后退出，而下载过程会异步进行，当下载完成后，再进入&lt;code&gt;Display&lt;/code&gt;方法的执行，期间不会阻塞线程，不会造成UI无响应的情况。&lt;/p&gt;&#xD;
&lt;p&gt;虽然高手们总是说不要关心语言，不要在意语言，真正重要的是思想。但是看着这样的代码，真的还能认为语言的优秀与否对生产效率没有影响吗？&lt;/p&gt;&#xD;
&lt;p&gt;至于如何实现这个效果，本篇并不想做太多的说明，因为本文的目的仅仅是向大家介绍一下下一代C#的一个特性。实现机制方面，相信大家都想得到，编译器会将方法体在&lt;strong&gt;await&lt;/strong&gt;关键字前后打断，编译为Begin/End模式的异步模型。这并不是什么难事，但是能想到并付诸于实施却并不容易。至少JAVA7虽然强化了异步编程，但却没有让语言达到这样的程度。请不要不屑于语法糖，正如高手们所说，无论什么语言都不见得能改变设计的代价，那么实现过程的效率，就决定了项目本身的生产效率。&lt;/p&gt;&#xD;
&lt;p&gt;说回来，这个思想和老赵的[Jscex](http://blog.zhaojie.me/tag/Jscex/ 老赵点滴 - 追求编程之美)非常类似，都是试图通过一步编译，将异步的编程模型统一为同步模型，简化开发复杂度，提升生产效率。时至如今，还想说中国的程序员搞不出创造性的东西吗？&lt;/p&gt;&#xD;
&lt;p&gt;PS1：怎么去体验下C# vNext。&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;装备好Visual Studio 2010 + SP1，无论什么版本。&lt;/li&gt;&#xD;
&lt;li&gt;把&lt;a href="http://www.microsoft.com/download/en/details.aspx?id=9983"&gt;Visual Studio Async CTP&lt;/a&gt;下载下来，并安装。&lt;/li&gt;&#xD;
&lt;li&gt;建个项目，现在你已经可以使用&lt;strong&gt;async&lt;/strong&gt;和&lt;strong&gt;await&lt;/strong&gt;关键字了，而诸如&lt;code&gt;WebClient&lt;/code&gt;下的&lt;code&gt;DownloadStringTaskAsync&lt;/code&gt;方法，则是在&lt;code&gt;%MyDocument%\Microsoft Visual Studio Async CTP\Samples\AsyncCtpLibrary.dll&lt;/code&gt;下定义的扩展方法。&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;关于具体的实践和原理，可以看一看[C# 5.0 vNext - New Asynchronous Pattern](http://www.codeproject.com/KB/cs/async.aspx CodeProject)一文。&lt;/p&gt;&#xD;
&lt;p&gt;PS：最后说说为什么我要写这一篇。原本我是不想写关于.NET的内容的，毕竟已经有不短的时间游走在.NET社区的边缘，没有深入地研究，并不适合来发表一些自己的论点。但是看到近段时间博客园首页上依旧遍历着诸如&amp;ldquo;C#最新特性&amp;rdquo;这样的文章时，真的觉得很不舒服。C# 4.0已经出来多久了，C# 5.0都离我们只有多少距离了，我相信关于Async CTP的内容有不少喜欢逛国外博客的园友是接触过的，但是却没有一个人愿意将这些最前沿的消息分享过来&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;当然我也并不认为这个社区的每一员都有义务将自己所知道的内容分享出来，但是现在国内的.NET社区确实存在着这样的现状：大家分享的多数是已经被嚼得稀巴烂的骨头，什么&amp;ldquo;C#的可变参数&amp;rdquo;，什么&amp;ldquo;自己写一个AJAX实现&amp;rdquo;，这些或许对你个人的学习有着历史性的意义，但是这样已经存在、流传已久，几乎家喻户晓的概念，在博客园首页这样的位置传播，真的有其意义所在吗？而这些最前沿的变化、那些更加深入的探究，或者是没人接触，或者就是接触了但不愿意分享。这并不是我所希望的社区状态，如今的互联网，无论是SNS还是微博，大家都在注重着消息的传播这一环节，为什么在.NET社区里，却是死气沉沉的状态，把旧菜炒了又炒，满眼都见不到一丝亮点？&lt;/p&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/2170121.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/09/07/csharp-vnext.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/05/19/doubt-on-fewer-http-requests.html</id><title type="text">对减少HTTP请求的疑问</title><summary type="text">教条根据各种Web性能优化手册，“减少HTTP请求”这一条始终被放在显眼的位置，其中就包括著名的YSlow和Google Page Speed，两者对这一教条的解释分别是：80% of the end-user response time is spent on the front-end. Most of this time is tied up in downloading all the components in the page: images, stylesheets, scripts, Flash, etc.YSlow表示，前端的多数时间是用在下载图片、样式表、脚本、Flash等</summary><published>2011-05-19T05:54:00Z</published><updated>2011-05-19T05:54:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/05/19/doubt-on-fewer-http-requests.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/05/19/doubt-on-fewer-http-requests.html"/><content type="html">&lt;p&gt;&lt;strong&gt;教条&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;根据各种Web性能优化手册，&amp;ldquo;减少HTTP请求&amp;rdquo;这一条始终被放在显眼的位置，其中就包括著名的&lt;a title="Best Practices for Speeding Up Your Web Site" href="http://developer.yahoo.com/performance/rules.html" rel="external"&gt;YSlow&lt;/a&gt;和&lt;a title="Minimize round-trip times" href="http://code.google.com/speed/page-speed/docs/rtt.html" rel="external"&gt;Google Page Speed&lt;/a&gt;，两者对这一教条的解释分别是：&lt;/p&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&#xD;
&lt;p&gt;80% of the end-user response time is spent on the front-end. Most of this time is tied up in downloading all the components in the page: images, stylesheets, scripts, Flash, etc.&lt;/p&gt;&#xD;
&lt;/blockquote&gt;&#xD;
&lt;p&gt;YSlow表示，前端的多数时间是用在下载图片、样式表、脚本、Flash等，所以要减少HTTP请求。&lt;/p&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&#xD;
&lt;p&gt;RTT is the major contributing factor to latency on "fast" (broadband) connections.&lt;/p&gt;&#xD;
&lt;/blockquote&gt;&#xD;
&lt;p&gt;Page Speed则表示，RTT（请求往返时间）是导致连接快不起来的主要原因，所以要减少，即减少HTTP的请求数。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;疑惑&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;很少人会对这2大优化守则产生怀疑，因为它们即真理、即教条、即必须遵守之则，如有违逆，虽远必诛&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;但是如果认真地去解读这2条规则，其他他们都表达了一个意思：网络上的往返越多，响应的速度就越慢。&lt;/p&gt;&#xD;
&lt;p&gt;但是他们却忽略了一个很重要的事情，那就是&lt;strong&gt;工作总用时多，并不代表任务完成时间也多&lt;/strong&gt;，因为这里有一个概念，叫作&lt;em&gt;并行&lt;/em&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;试想一个任务，用一个线程跑，和用100个线程跑，哪一个总用时更少？答案其实是一个线程，因为100个线程之间有线程切换的开销、有结果join的时间、有任务分割的时间&amp;hellip;&amp;hellip;但是从结论上来看，哪一个能更快地跑完任务？答案是多线程，这就是并行计算的理论来源。&lt;/p&gt;&#xD;
&lt;p&gt;事实上，资源的下载也是如此，如果并行的话，多个资源的下载时间不见得会小于一个资源，如下图是一个合并后的资源的下载示意：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img title="大资源下载示意" alt="大资源下载示意" src="http://www.otakustay.com/wp-content/uploads/2011/05/round-trip-single.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;上图用来表示一个较大的资源的下载过程，其中不同的颜色分别对应：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;&lt;span style="color: #b7cfff;"&gt;蓝色：&lt;/span&gt;TCP链接建立时间 &lt;/li&gt;&#xD;
&lt;li&gt;&lt;span style="color: #e2fdba;"&gt;绿色：&lt;/span&gt;请求头发送时间 &lt;/li&gt;&#xD;
&lt;li&gt;&lt;span style="color: #daccef;"&gt;紫色：&lt;/span&gt;服务器处理时间 &lt;/li&gt;&#xD;
&lt;li&gt;&lt;span style="color: #ffd3ae;"&gt;橙色：&lt;/span&gt;响应头发送时间 &lt;/li&gt;&#xD;
&lt;li&gt;&lt;span style="color: #ffd3ae;"&gt;红色：&lt;/span&gt;文件传输时间 &lt;/li&gt;&#xD;
&lt;li&gt;&lt;span style="color: #d5d5d5;"&gt;灰色：&lt;/span&gt;文件解析执行时间 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;如果将这个大资源分解成3个相等的小资源的话，那么他们的下载就可能是这样的：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img title="小资源下载示意" alt="小资源下载示意" src="http://www.otakustay.com/wp-content/uploads/2011/05/round-trip-multiple.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;由于浏览器只有一个线程可以对文件进行解析和执行，因此灰色的解析部分必定是串行的，需要相互等待。&lt;/p&gt;&#xD;
&lt;p&gt;比较2张图，假设其中的每一小块的时间为t，可以发现这样一个结论：一个大资源的加载，使用了13t的时间，而将大资源分解后，虽然总共用了21t的时间，但是客户端真正等待的时间却只有9t，比合并资源的方式节约了4t。&lt;/p&gt;&#xD;
&lt;p&gt;这就是并行的效果，当然要实现这个效果，是有前提的：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;并行的连接数没有超过浏览器的限制，这里假设4并行是一个比较合理地、照顾到各浏览器的值。 &lt;/li&gt;&#xD;
&lt;li&gt;服务器能顶得住，不会因为并发连接过多而导致处理时间变长。 &lt;/li&gt;&#xD;
&lt;li&gt;网络足够稳定，这一点将保证TCP建立、请求头发送、响应头发送这3段时间是稳定的。 &lt;/li&gt;&#xD;
&lt;li&gt;浏览器的资源加载不会阻塞，如IE6-7在下载js文件时会阻塞后续资源的请求，则不可能实现并行。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;缓存的考虑&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;合并资源有另一个好处，就是在缓存之后，只需向服务器验证一次即可。但是再和拆分资源的加载过程作一个比较，如果采用带验证的缓存，不难得出下图：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img title="服务器缓存的情况" alt="服务器缓存的情况" src="http://www.otakustay.com/wp-content/uploads/2011/05/round-trip-server-cached.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;而如果使用客户端的缓存，则是以下情况：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img title="客户端缓存的情况" alt="客户端缓存的情况" src="http://www.otakustay.com/wp-content/uploads/2011/05/round-trip-client-cached.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;从上面2张图中可以看到，即便在有缓存的情况下，如果满足一定的条件，可以进行并发的话，若干个小资源的加载情况下客户端的等待时间和合并为一个大资源后是相同的，并没有多余的消耗。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;一个最基础的结论是，&lt;em&gt;加载资源总用时&lt;/em&gt;和&lt;em&gt;客户端等待时间&lt;/em&gt;是2个完全不同的概念，他们之间并不存在正比的关系，而在宽带普及的当前时代，多数网络资源并不是按流量收费的，因此前端的优化应该更关注于&lt;em&gt;客户端等待时间&lt;/em&gt;，而不是&lt;em&gt;加载资源总用时和流量&lt;/em&gt;之上。&lt;/p&gt;&#xD;
&lt;p&gt;事实上，一个最经典的应用就是下载软件，从90年代的NetAnt开始，到其后的网际快车、迅雷，无一不具有&amp;ldquo;多线程下载&amp;rdquo;的能力。事实上多线程下载同样会因为线程的切换、文件分段的空间分配、最后多段碎片的拼接等导致总耗时更多，但也确确实实极大地缩短了下载的时间。在对页面的资源加载作优化时，是不是也可以参考一下这个模型呢？&lt;/p&gt;&#xD;
&lt;p&gt;由此，对资源的切分将会从单纯的&amp;ldquo;合并&amp;rdquo;的级别，提升到一种完全艺术的程度，综合考虑不同浏览器对资源加载的策略，最大限度利用浏览器的并行下载能力，从而正确、最优地分配静态资源域名，切分静态资源，压榨浏览器全部的能力，进一步地提升页面加载的速率。这虽然大大提高了资源管理、分解的复杂度，但是对于追求极限而言，本人认为这样才是真正的最佳实践。&lt;/p&gt;&#xD;
&lt;p&gt;最后，本文完全没有&lt;em&gt;全部否定&lt;/em&gt;减少HTTP请求数这一优化原则的意思，只是希望从另外一个角度看问题，保持着一定的怀疑的心态来重新审视这一原则，拒绝不经过思考的无意义的遵循，从而寻找到在特定环境下，最适合、最优化的方案。&lt;/p&gt;&#xD;
&#xD;
&lt;p style="text-align: right;"&gt;本文永久地址：&lt;a href="http://www.otakustay.com/doubt-on-fewer-http-requests/"&gt;http://www.otakustay.com/doubt-on-fewer-http-requests/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/2051012.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/05/19/doubt-on-fewer-http-requests.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/04/20/object-lookup-in-javascript.html</id><title type="text">javascript中的对象查找</title><summary type="text">近期群里常有人提一些简单的问题，比如发一段代码乱七八糟的代码，然后说里面某个变量是什么，为此，我觉得自己作为一个虽然不成熟的前端，对于一些自己力所能及的事情，还是应该传道授业解惑的。所以，这篇文章，计划从非常肤浅的层面上，来解释一下javascript中的对象查找是如何进行的。</summary><published>2011-04-20T09:04:00Z</published><updated>2011-04-20T09:04:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/04/20/object-lookup-in-javascript.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/04/20/object-lookup-in-javascript.html"/><content type="html">&lt;p&gt;近期群里常有人提一些简单的问题，比如发一段代码乱七八糟的代码，然后说里面某个变量是什么，比如这里就有个很好的例子：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;function fn(arg) {&#xD;
    alert(this.arg);&#xD;
    alert(this);&#xD;
}&#xD;
fn(123);&#xD;
var o = { fn: fn };&#xD;
o.fn(123);&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;然后就可能有这样的问题：&lt;/p&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&#xD;
&lt;p&gt;为什么this.arg是undefined？为什么2次调用fn的this是不一样的？&lt;/p&gt;&#xD;
&lt;/blockquote&gt;&#xD;
&lt;p&gt;为此，我觉得自己作为一个虽然不成熟的前端，对于一些自己力所能及的事情，还是应该&lt;em&gt;传道授业解惑&lt;/em&gt;的。所以，这篇文章，计划从非常肤浅的层面上，来解释一下javascript中的对象查找是如何进行的。&lt;/p&gt;&#xD;
&lt;p&gt;注意，本篇文章只是从表象上来介绍对象查找这一行为的过程，文章中的观点&lt;em&gt;并不全正确&lt;/em&gt;，甚至存在着&lt;em&gt;一些谬误&lt;/em&gt;，但是这也是为了让初学者更好地理解对象查找这一过程。相信如果说得太过抽象、深入，反而会引起一些负面效果。如果有一天，你再回过来，发现这个文章说得并不那么正确，那么恭喜你，那个时候的你已经可以找到正确前进的道路，这篇中的错误也不会再对你有任何影响。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;对象的分类&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;所谓对象查找，即在一段可执行代码的作用域内，找到一个当前需要的对象。在javascript中，需要进行查找的对象大致可以分为3种类型：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;变量查找，如&lt;code&gt;foo++;&lt;/code&gt;，这里就会去查找一个叫作foo的变量。&lt;/li&gt;&#xD;
&lt;li&gt;属性查找，如&lt;code&gt;foo.bar++;&lt;/code&gt;，这里会去查找foo这个变量下的一个叫作bar的属性。&lt;/li&gt;&#xD;
&lt;li&gt;this查找，即针对this关键字的处理。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;区分这3种类型的对象查找是首先要完成的任务，你可以基于以下原则进行判断：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;变量仅由变量名组成，即单独的foo、bar等。&lt;/li&gt;&#xD;
&lt;li&gt;属性永远由2种形式去访问，即&lt;code&gt;foo.bar&lt;/code&gt;和&lt;code&gt;foo['bar']&lt;/code&gt;，因此看到有&amp;ldquo;.&amp;rdquo;或&amp;ldquo;[]&amp;rdquo;即可当成是属性查找。&lt;/li&gt;&#xD;
&lt;li&gt;this就不用说了，好好的关键字。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;看一下这段代码：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;var foo = this;&#xD;
foo.bar();&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;这2行的代码就体现了3种对象查找，分别为：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;查找this对象，并赋值给foo变量。&lt;/li&gt;&#xD;
&lt;li&gt;查看foo变量。&lt;/li&gt;&#xD;
&lt;li&gt;查找foo变量下的bar属性，并将之作为一个函数进行调用。&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;&lt;strong&gt;变量的查找&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;当确定一个对象的查找为变量查找后，可以按照变量查找的规则来查看。&lt;/p&gt;&#xD;
&lt;p&gt;变量查找，即在&lt;em&gt;作用域链&lt;/em&gt;上进行查找，作用域链是javascript非常著名的2条链之一，以下代码体现一个标准的作用域链：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;var foo = 1;&#xD;
function a()　{&#xD;
    var bar = 2;&#xD;
    function b() {&#xD;
        foo = 3;&#xD;
        function c() {&#xD;
            alert(foo + ',' + bar); // 注意这一行&#xD;
        }&#xD;
        c();&#xD;
    }&#xD;
    b();&#xD;
}&#xD;
a();&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;在c函数中，一共进行了2个变量的查找，分别为foo和bar。&lt;/p&gt;&#xD;
&lt;p&gt;变量的查找可以简单地遵守&amp;ldquo;从下向上&amp;rdquo;的原则，即：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;在函数c的范围内查找foo和bar，显然在c里面并没有foo和bar的声明，查找失败。&lt;/li&gt;&#xD;
&lt;li&gt;在包含c的函数，即函数b的范围内查找foo和bar，可以看到b里面只有对foo的赋值，并没有声明，查找失败。&lt;/li&gt;&#xD;
&lt;li&gt;在包括b的函数，即函数a的范围内查找foo和bar，可以找到bar的声明，因此确定bar为2。&lt;/li&gt;&#xD;
&lt;li&gt;由于a不被任何函数包含，那么就在全局作用域内查找foo，发现有foo的声明，因此确定foo的值为1。但是由于在函数b中，对这个foo有赋值，所以foo的值被修改为3。&lt;/li&gt;&#xD;
&lt;li&gt;完确定foo的值为3，bar的值为2，因此输出&lt;code&gt;"3,2"&lt;/code&gt;。&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;总结一下，变量的查找是&lt;em&gt;延着作用域链进行的&lt;/em&gt;，作用域链可以简单地看成&lt;em&gt;函数间的包含关系&lt;/em&gt;，被包含的函数中不存在某个变量时，在包含他的函数中查找，直到&lt;em&gt;全局作用域&lt;/em&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;属性的查找&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;当确定一个对象的查找为属性查找后，可以按照属性查找的规则来查看。&lt;/p&gt;&#xD;
&lt;p&gt;属性查找，即在&lt;em&gt;原型链&lt;/em&gt;上进行查找，原型链是javascript双链的另一条，以下可以表示出一个原型链：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;var a = function() {};&#xD;
var b = function() {};&#xD;
var c = function() {};&#xD;
b.prototype = new a();&#xD;
c.prototype = new b();&#xD;
a.prototype.foo = 1;&#xD;
b.prototype.bar = 2;&#xD;
c.prototype.foo = 3;&#xD;
var o = new c();&#xD;
alert(o.foo + ',' + o.bar); // 这一行进行查找&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;属性查找是一个不断寻找prototype的过程，即：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;查找&lt;code&gt;c.prototype&lt;/code&gt;中，有没有显示定义foo和和bar，发现定义了foo，其值为3。&lt;/li&gt;&#xD;
&lt;li&gt;发现&lt;code&gt;c.prototype&lt;/code&gt;就是&lt;code&gt;new b()&lt;/code&gt;得到的对象，那么查找b.prototype中，有没有显示定义bar，发现定义了，其值为2。&lt;/li&gt;&#xD;
&lt;li&gt;因此确定foo的值为3，bar的值为2，输出&lt;code&gt;"3,2"&lt;/code&gt;。&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;总结一下，属性查找是&lt;em&gt;延着原型链进行的&lt;/em&gt;，原型链的具体知识这里不作详细解释，可以另找文章进行参考。所有的对象，其原型链最终会是&lt;em&gt;&lt;code&gt;Object.prototype&lt;/code&gt;&lt;/em&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;this的查找&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;this的查找是很多人迷茫的一点，也似乎有很多人抱有&lt;q&gt;this不稳定&lt;/q&gt;这样的看法，实在令人无语。this的查找可以说是3种对象查找中最为简单的，因为其实this对象的确定根&#xD;
本没有一个&amp;ldquo;查找&amp;rdquo;的过程。&lt;/p&gt;&#xD;
&lt;p&gt;首先，this对象只会在一个函数中需要确定，如果是在全局域下，this永远为Global对象，在浏览器中通常就是window对象。而在javascript中，函数的调用一共有4种方式：&lt;/p&gt;&#xD;
&lt;dl&gt;&#xD;
    &lt;dt&gt;Function Invocation Pattern&lt;/dt&gt;&#xD;
    &lt;dd&gt;&#xD;
&lt;p&gt;诸如`foo()`的调用形式被称为Function Invocation Pattern，是函数最直接的使用形式，注意这里的foo是作为单独的变量出现，而不是属性。&lt;/p&gt;&#xD;
&lt;p&gt;在这种模式下，foo函数体中的this永远为Global对象，在浏览器中就是window对象。&lt;/p&gt;&#xD;
&lt;/dd&gt;&#xD;
    &lt;dt&gt;Method Invocation Pattern&lt;/dt&gt;&#xD;
    &lt;dd&gt;&#xD;
&lt;p&gt;诸如`foo.bar()`的调用形式被称为Method Invocation Pattern，注意其特点是被调用的函数作为一个对象的属性出现，必然会有&amp;ldquo;.&amp;rdquo;或者&amp;ldquo;[]&amp;rdquo;这样的关键符号。&lt;/p&gt;&#xD;
&lt;p&gt;在这种模式下，bar函数体中的this永远为&amp;ldquo;.&amp;rdquo;或&amp;ldquo;[&amp;rdquo;前的那个对象，如上例中就一定是foo对象。&lt;/p&gt;&#xD;
&lt;/dd&gt;&#xD;
    &lt;dt&gt;Constructor Pattern&lt;/dt&gt;&#xD;
    &lt;dd&gt;&#xD;
&lt;p&gt;`new foo()`这种形式的调用被称为Constructor Pattern，其关键字`new`就很能说明问题，非常容易识别。&lt;/p&gt;&#xD;
&lt;p&gt;在这种模式下，foo函数内部的this永远是new foo()返回的对象。&lt;/p&gt;&#xD;
&lt;/dd&gt;&#xD;
    &lt;dt&gt;Apply Pattern&lt;/dt&gt;&#xD;
    &lt;dd&gt;&#xD;
&lt;p&gt;`foo.call(thisObject)`和`foo.apply(thisObject)`的形式被称为Apply Pattern，使用了内置的`call`和`apply`函数。&lt;/p&gt;&#xD;
&lt;p&gt;在这种模式下，`call`和`apply`的第一个参数就是foo函数体内的this，如果thisObject是`null`或`undefined`，那么会变成Global对象。&lt;/p&gt;&#xD;
&lt;/dd&gt;&#xD;
&lt;/dl&gt;&#xD;
&lt;p&gt;应用以上4种方式，确定一个函数是使用什么样的Pattern进行调用的，就能很容易确定this是什么。&lt;/p&gt;&#xD;
&lt;p&gt;另外，this是永远不会延作用域链或原型链出现一个&amp;ldquo;查找&amp;rdquo;的过程的，只会在函数调用时就完全确认。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;对于一个对象的查找：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;&#xD;
确定是变量查找、属性查找还是this查找。&lt;/li&gt;&#xD;
&lt;li&gt;&#xD;
如果是变量查找，则延作用域链找，找不到就是ReferenceError。&lt;/li&gt;&#xD;
&lt;li&gt;&#xD;
如果是属性查找，就延原型链找，找歪以就是undefined。&lt;/li&gt;&#xD;
&lt;li&gt;&#xD;
如果是this查找，去找调用函数的代码，根据调用的形式来确定this是什么。&lt;/li&gt;&#xD;
&lt;li&gt;&#xD;
&lt;p&gt;注意把一次查找过程拆分开来，比如&lt;code&gt;this.foo.bar.yahoo()&lt;/code&gt;，可以拆分成这样的代码，就能更清楚了：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;var o = this; // this查找&#xD;
var foo = o.this; // 属性查找&#xD;
var bar = foo.bar; // 属性查找&#xD;
bar.yahoo(); // 属性查找，加Method Invocation Pattern&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;最后，如果有一天你可以了解这些东西，这篇文章对你用户也就不大了：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;为什么延作用域查找不到会有ReferenceError。&lt;/li&gt;&#xD;
&lt;li&gt;其实变量也是属性，一个特殊对象的属性。&lt;/li&gt;&#xD;
&lt;li&gt;this也许不是Global，也许会是undefined。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p style="text-align: right;"&gt;本文永久地址：&lt;a href="http://www.otakustay.com/object-lookup-in-javascript/"&gt;http://www.otakustay.com/object-lookup-in-javascript/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/2022503.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/04/20/object-lookup-in-javascript.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/04/18/animation-and-requestanimationframe.html</id><title type="text">新动画函数requestAnimationFrame</title><summary type="text">介绍一个新的requestAnimationFrame函数，以及不太一样的javascript动画方案</summary><published>2011-04-18T09:03:00Z</published><updated>2011-04-18T09:03:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/04/18/animation-and-requestanimationframe.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/04/18/animation-and-requestanimationframe.html"/><content type="html">&lt;p&gt;&lt;strong&gt;经典动画&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;话不多说，首先来个经典的动画函数：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;function animate(element, name, from, to, time) {&#xD;
    time = time || 800; //默认0.8秒&#xD;
&#xD;
    var style = element.style,&#xD;
        latency = 60, // 每60ms一次变化&#xD;
        count = time / latency, //变化的次数&#xD;
        step = Math.round((to - from) / count), //每一步的变化量&#xD;
        now = from;&#xD;
&#xD;
    function go() {&#xD;
        count--;&#xD;
        now = count ? now + step : to;&#xD;
        style[name] = now + 'px';&#xD;
&#xD;
        if (count) {&#xD;
            setTimeout(go, latency);&#xD;
        }&#xD;
    }&#xD;
&#xD;
    style[name] = from + 'px';&#xD;
    setTimeout(go, latency);&#xD;
}&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;姑且不论这个函数的设计存在局限性，如只能对以px为单位的样式进行修改。仅从函数的实现上来看，这可以是一个非常经典的动画理念，其基本逻辑由以下部分组成：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;获取起点值&lt;code&gt;from&lt;/code&gt;和终点值&lt;code&gt;to&lt;/code&gt;，通过动画需要进行的时间&lt;code&gt;time&lt;/code&gt;，以及每侦间隔&lt;code&gt;latency&lt;/code&gt;的要求，计算出值的改变次数&lt;code&gt;count&lt;/code&gt;和每次改变的量&lt;code&gt;step&lt;/code&gt;。 &lt;/li&gt;&#xD;
&lt;li&gt;开启&lt;code&gt;setTimeout(fn, latency);&lt;/code&gt;来步进到下一侦。 &lt;/li&gt;&#xD;
&lt;li&gt;在下一侦中，设置属性步进一次，如果动画还没结束，再回到第2步继续下一侦。 &lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;这个函数工作得很好，服务了千千万万的站点和系统，事实上jQuery的animate函数的核心也无非是setInterval函数。&lt;/p&gt;&#xD;
&lt;p&gt;但是，随着现在系统复杂度的稳步上升，动画效果也越来越多，同时对动画的流畅度也有了更多的重视，这导致上面的函数会出现一些问题。例如同时打开100个动画效果，根据上面的函数，很明显会有100个定时器在同时运行，这些定时器之间的调度会对性能有轻微的影响。虽然在正常的环境中，这些许的影响并不会有什么关系，但是在动画这种对流畅度有很高要求的环境下，任何细微的影响都可能产生出不好的用户体验。&lt;/p&gt;&#xD;
&lt;p&gt;在这样的情况下，有一些开发者就发明了一种基于统一帧管理的动画框架，他使用一个定时器触发动画帧，不同的动画来注册这些帧，在每一帧上处理多个动画的属性变化。这样的好处是减少了定时器调度的开销，但是对于动画框架的开发者来说，统一帧管理、提供监听帧的API等，都是需要开发和维护的。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;浏览器的直接支持&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;最终，浏览器厂商们发现这件事其实可以由他们来做，并且基于浏览器层面，还可以有更多的优化，比如：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;对于一个侦中对DOM的所有操作，只进行一次Layout和Paint。 &lt;/li&gt;&#xD;
&lt;li&gt;如果发生动画的元素被隐藏了，那么就不再去Paint。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;于是，浏览器开始推出一个API，叫做&lt;code&gt;requestAnimationFrame&lt;/code&gt;，关于这个函数，&lt;a href="https://developer.mozilla.org/en/DOM/window.mozRequestAnimationFrame" title="requestAnimationFrame - MDC Doc Center"&gt;MDC的相关页面&lt;/a&gt;有比较详细的介绍，简单来说，这个函数有2种使用方法：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;调用&lt;code&gt;requestAnimationFrame&lt;/code&gt;函数，传递一个callback参数，则在下一个动画帧时，会调用callback。 &lt;/li&gt;&#xD;
&lt;li&gt;不传递参数地直接调用该函数，启动动画帧，下一个帧触发时，会同时触发&lt;code&gt;window.onmozbeforepaint&lt;/code&gt;事件，可以通过注册该事件来进行动画。 &lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;第2种方法由于依赖于Firefox自己的事件，且&lt;code&gt;beforepaint&lt;/code&gt;事件还没进入到标准中，所以不推荐使用，还是使用第1种方式比较好。此时，我们的动画逻辑可以变成这样：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;记录当前时间&lt;code&gt;startTime&lt;/code&gt;，作为动画开始的时间。 &lt;/li&gt;&#xD;
&lt;li&gt;请求下一帧，带上回调函数。 &lt;/li&gt;&#xD;
&lt;li&gt;下一帧触发时，回调函数的第一个参数为当前的时间，再与&lt;code&gt;startTime&lt;/code&gt;进行比较，确定时间间隔&lt;code&gt;ellapseTime&lt;/code&gt;。 &lt;/li&gt;&#xD;
&lt;li&gt;判断&lt;code&gt;ellapseTime&lt;/code&gt;是否已经超过事先设定的动画时间&lt;code&gt;time&lt;/code&gt;，如果超过，则结束动画。 &lt;/li&gt;&#xD;
&lt;li&gt;计算动画属性变化的差值&lt;code&gt;differ = to - from&lt;/code&gt;，再确定在&lt;code&gt;ellapseTime&lt;/code&gt;的时候应该变化多少&lt;code&gt;step = differ / time * ellapseTime&lt;/code&gt;。 &lt;/li&gt;&#xD;
&lt;li&gt;计算出现在应该变化到的位置&lt;code&gt;Math.round(from + step)&lt;/code&gt;，并重新对样式赋值。 &lt;/li&gt;&#xD;
&lt;li&gt;继续请求下一帧。 &lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;&lt;strong&gt;新的动画函数&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;下面就是一个全新的动画函数：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;function animate(element, name, from, to, time) {&#xD;
    time = time || 800; // 默认0.8秒&#xD;
    var style = element.style,&#xD;
        startTime = new Date;&#xD;
&#xD;
    function go(timestamp) {&#xD;
        var progress = timestamp - startTime;&#xD;
        if (progress &amp;gt;= duration) {&#xD;
            style[name] = to + 'px';&#xD;
            return;&#xD;
        }&#xD;
&#xD;
        var now = (to - from) * (progress / duration);&#xD;
        style[name] = now.toFixed() + 'px';&#xD;
        requestAnimationFrame(go);&#xD;
    }&#xD;
&#xD;
    style[name] = from + 'px';&#xD;
&#xD;
    requestAnimationFrame(go);&#xD;
}&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;到这一步，还剩一个问题，那就是并不是每个浏览器都支持&lt;code&gt;requestAnimationFrame&lt;/code&gt;函数的，所以再做一个简单的修正。&lt;/p&gt;&#xD;
&lt;p&gt;根据Firefox的特性来看，其&lt;code&gt;mozRequestAnimationFrame&lt;/code&gt;提供的最高FPS为60，并且会根据每一帧的计算的耗时来进行调整，比如每一帧计算用了1s，那他只会提供1FPS的动画效果。&lt;/p&gt;&#xD;
&lt;p&gt;而Chrome的高版本同样也实现了这个函数，叫&lt;code&gt;webkitRequestAnimationFrame&lt;/code&gt;，可以预见未来还会有Opera的&lt;code&gt;oRequestAnimationFrame&lt;/code&gt;和IE的&lt;code&gt;msRequestAnimationFrame&lt;/code&gt;，所以这里一并做一个简单的兼容处理：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;requestAnimationFrame = window.requestAnimationFrame ||&#xD;
    window.mozRequestAnimationFrame ||&#xD;
    window.webkitRequestAnimationFrame ||&#xD;
    window.msRequestAnimationFrame || &#xD;
    window.oRequestAnimationFrame ||&#xD;
    function(callback) { setTimeout(callback, 1000 / 60); };&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;strong&gt;参考资料&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en/DOM/window.mozRequestAnimationFrame" title="requestAnimationFrame - MDC Doc Center"&gt;MDC的相关文档&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;&lt;a href="http://paulirish.com/2011/requestanimationframe-for-smart-animating/" title="requestAnimationFrame for smart animating"&gt;requestAnimationFrame for smart animating&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;&lt;a href="https://github.com/jquery/jquery/pull/298" title="#8101 - lrbabe request animation frame (some adjustments and style edits) by timmywil for jquery's jquery - Pull Request - GitHub"&gt;jQuery的Pull Request&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p style="text-align: right;"&gt;本文永久地址：&lt;a href="http://www.otakustay.com/animation-and-requestanimationframe/"&gt;http://www.otakustay.com/animation-and-requestanimationframe/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/2020026.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/04/18/animation-and-requestanimationframe.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/04/11/learning-html5-charset.html</id><title type="text">HTML5标准学习 - 编码</title><summary type="text">浏览器与编码不得不说的故事。编码声明的演变和进化。</summary><published>2011-04-11T09:38:00Z</published><updated>2011-04-11T09:38:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/04/11/learning-html5-charset.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/04/11/learning-html5-charset.html"/><content type="html">&lt;p&gt;相信每一个前端工程师都或多或少遇上过&amp;ldquo;乱码&amp;rdquo;这位仁兄，无论你的基础有多么扎实，在生产的过程中都免不了偶尔和&amp;ldquo;乱码&amp;rdquo;兄弟喝上几杯茶吧。作为一个前端工程师，你是如何指定一个页面的编码的呢？你知道浏览器是怎么识别编码的吗？&lt;/p&gt;&#xD;
&lt;p&gt;首先，一个很简单的例子，用遇简的HTML页面来看看各浏览器下有什么不同：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;最简HTML，&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;和&lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;都没有内容，服务器也不给出具体的编码声明，直接从本地打开，各个浏览器下查看页面的编码：&lt;/p&gt;&#xD;
&lt;table&gt;&#xD;
&lt;thead&gt;&#xD;
    &#xD;
&lt;tr&gt;&#xD;
&lt;th&gt;浏览器&lt;/th&gt;&#xD;
      &lt;th&gt;显示编码&lt;/th&gt;&#xD;
      &lt;th&gt;备注&lt;/th&gt;&#xD;
    &#xD;
&lt;/tr&gt;&#xD;
&lt;/thead&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td&gt;IE6&lt;/td&gt;&#xD;
&lt;td&gt;UTF-8&lt;/td&gt;&#xD;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td&gt;IE8&lt;/td&gt;&#xD;
&lt;td&gt;UTF-8&lt;/td&gt;&#xD;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td&gt;IE9&lt;/td&gt;&#xD;
&lt;td&gt;GB2312&lt;/td&gt;&#xD;
&lt;td&gt;系统默认字符集&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td&gt;Firefox3.5&lt;/td&gt;&#xD;
&lt;td&gt;GBK2312&lt;/td&gt;&#xD;
&lt;td&gt;系统默认字符集&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td&gt;Firefox4.0&lt;/td&gt;&#xD;
&lt;td&gt;ISO-8859-1&lt;/td&gt;&#xD;
&lt;td&gt;西欧语言，英语默认编码&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td&gt;Chrome&lt;/td&gt;&#xD;
&lt;td&gt;GBK&lt;/td&gt;&#xD;
&lt;td&gt;系统默认字符集&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td&gt;Opera&lt;/td&gt;&#xD;
&lt;td&gt;中文-自动检测&lt;/td&gt;&#xD;
&lt;td&gt;应该也是GB2312&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;p&gt;从表格中可以看出，对于没有使用任何手段声明编码的页面，各浏览器有着不同的解析。当然在最简的页面中，无论用什么编码（当然前提是ASCII的超集）都没有影响，但足以表现出正确设置编码的重要性。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;编码声明&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;HTML4和HTML5分别采用了一个章节来阐述编码声明的方法，可以&lt;a title="5.2.2 Specifying the character encoding" href="http://www.w3.org/TR/1999/REC-html401-19991224/charset.html#h-5.2.2"&gt;点击这里查看HTML4的相关章节&lt;/a&gt;或&lt;a title="4.2.5.5 Specifying the document's character encoding" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#charset"&gt;点击这里查看HTML5的相关章节&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;首先，何为编码？编码即是通过一定的方式，指定浏览器（或称用户代理）以一种特殊的算法来解析字节流，以得到真正正确的内容。在HTML的标准中，编码可以使用别名来表示。编码的别名来自于&lt;a title="CHARACTER SETS" href="http://www.iana.org/assignments/character-sets"&gt;IANA的定义&lt;/a&gt;，只有在该列表中出现的编码才可以被浏览器识别。因此如果把UTF-8写成UTF8，浏览器就有可能完全不予理睬。另外，编码别名是&lt;em&gt;大小写不敏感&lt;/em&gt;的。&lt;/p&gt;&#xD;
&lt;p&gt;在HTML4中，提出有3种方法指定页面的编码，根据优先级高低依次是：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;HTTP头里的Content-Type字段后跟随字符集。 &lt;/li&gt;&#xD;
&lt;li&gt;使用&lt;code&gt;&amp;lt;meta http-equiv="Content-Type"&amp;gt;&lt;/code&gt;标签来声明。 &lt;/li&gt;&#xD;
&lt;li&gt;对于部分外部资源，如&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签加载的js文件，可以通过标签上的charset属性声明。 &lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;这个自然没有什么疑问，需要注意的是，通过&lt;code&gt;&amp;lt;meta http-equiv="Content-Type"&amp;gt;&lt;/code&gt;标签来声明页面的话，当浏览器遇上该标签时，如果发现自己使用的编码与标签声明的不符，是会回到头里重新解析页面的。这会导致页面的一部分被重新解析，因此如果试图使用标签的方式声明编码的话，建议将标签尽可能地写在前面。一个最佳实践是写在&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;标签之后，任何其他标签之前。关于这一点，&lt;a title="Rendering - Specify a character set" href="http://code.google.com/intl/zh-CN/speed/page-speed/docs/rendering.html#SpecifyCharsetEarly"&gt;Google PageSpeed也有相应的介绍&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;时代演进&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;但是随着时间的推移，开发者渐渐发现了一件事。就如同DOCTYPE的最简声明一样，其实浏览器在读取&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;标签的编码的时候，并不是严格地按照标准进行的。总而言之，由于在HTML的解析阶段，基于在Tokenizer阶段之前就必须确定好页面的编码，因此浏览器不可能像分析DOM树一样，在DOM树构建的时候再分解&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;标签的结构，取出其中的&lt;code&gt;http-equiv&lt;/code&gt;和&lt;code&gt;content&lt;/code&gt;属性，再确定编码。&lt;/p&gt;&#xD;
&lt;p&gt;现实中，浏览器做了一件非常简单的事，来读取&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;标签定义的编码：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;确定这是一个&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;标签，这根据HTML解析的状态机，由"&amp;lt;"字符加上"meta"字符串就能确定。 &lt;/li&gt;&#xD;
&lt;li&gt;查找该字符串（此处还没有标签的概念，只是个字符串），找到一个子字符串"charset"。 &lt;/li&gt;&#xD;
&lt;li&gt;再向后读，忽略掉所有的空格字符，找到第一个有意义的字符c。 &#xD;
    &#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;如果c不是"="这个字符，则回到第2步继续找。 &lt;/li&gt;&#xD;
&lt;li&gt;如果c是"="这个字符，继续向下走。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;/li&gt;&#xD;
&lt;li&gt;再跳掉所有的空格字符和单引号、双引号等，向后扫描，直到遇上单引号、双引号、空格字符、结束标签等不应该出现的字符为上，截取其中扫描得出的字符串s。 &lt;/li&gt;&#xD;
&lt;li&gt;分析s，得到编码别名。 &lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;从上面的算法，不难发现，下面几种写法，其实都能让浏览器正确地识别出编码：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;&lt;code&gt;&amp;lt;meta http-equiv="Cotnent-Type" content="text/html; charset=utf-8" /&amp;gt;&lt;/code&gt; &lt;/li&gt;&#xD;
&lt;li&gt;&lt;code&gt;&amp;lt;meta charset="utf-8" /&amp;gt;&lt;/code&gt;&lt;/li&gt;&#xD;
&lt;li&gt;&lt;code&gt;&amp;lt;meta charset=utf-8 /&amp;gt;&lt;/code&gt; &lt;/li&gt;&#xD;
&lt;li&gt;&amp;hellip;&amp;hellip;以及其他很多古怪的写法。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;于是，随着历史的推进，终于有一天，各浏览器厂商们坐在了一起，开始讨论这个问题&amp;hellip;&amp;hellip;最终他们惊奇地发现各自的实现非常相似（也许根本就是相互借鉴），所以他们决定将这种方式变成一个标准&amp;hellip;&amp;hellip;最后，再经过漫长的讨论，HTML5中广为人爱的编码声明方式就诞生了。在HTML5中，称其为&amp;ldquo;meta charset元素&amp;rdquo;，其最简形式如下：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta charset=utf-8&amp;gt;&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;当然这是HTML的语法，如果遵从XHTML并觉得XHTML更加亲切地话，写成&lt;code&gt;&amp;lt;meta charset="utf-8" /&amp;gt;&lt;/code&gt;也是没问题的。&lt;/p&gt;&#xD;
&lt;p&gt;而前文所述的具体获取编码的算法也被详细地记录在案，&lt;a title="2.7.4 Extracting encodings from meta elements" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/urls.html#extracting-encodings-from-meta-elements"&gt;可以在这里看到&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;到了HTML5时代，标准再次对编码的声明方式做了修正和细化，总得来说有以下的区别：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;HTML5允许使用BOM来决定编码，但仅支持UTF-16的BOM（即U+FEFF），且没有说明BOM指定编码的优先级如何。 &lt;/li&gt;&#xD;
&lt;li&gt;HTML5添加了&lt;code&gt;meta charset&lt;/code&gt;标签。 &lt;/li&gt;&#xD;
&lt;li&gt;HTML5规定如果一个页面没有指定编码，则使用ASCII作为其编码，而HTML4则规定浏览器可以根据所处的环境自行选择。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;其他杂项&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;除了编码的基本声明方式外，标准中还有不少需要注意的细节：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;如果使用&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;标签声明编码的话，该编码只能是ASCII的超集编码。可以简单地认为ASCII超集就是支持ASCII的256个字符的编码。 &lt;/li&gt;&#xD;
&lt;li&gt;HTML5非常推荐使用UTF-8编码。 &lt;/li&gt;&#xD;
&lt;li&gt;标准中提出不要使用UTF-32、JIS_C6226-1983、JIS_X0212-1990、HZ-GB-2312、JOHAB等字符集，并禁止使用CESU-8、UTF-7、BOCU-1和SCSU字符集。但事实上浏览器却至少能识别UTF-7。 &lt;/li&gt;&#xD;
&lt;li&gt;对于想要严格遵守XHTML的开发者，应当使用XML声明来指定编码，即&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8" standalone="no" ?&amp;gt;&lt;/code&gt;。但是这个在IE6下会影响到DOCTYPE，所以开发者也不得在这一点上给予妥协，乖乖地去用HTML的声明方式。 &lt;/li&gt;&#xD;
&lt;li&gt;关于现实中各编码声明方式的优先级，以及一些其他需要注意的细节，&lt;a title="最近那个BOM及浏览器charset识别" href="http://hi.baidu.com/d4rkwind/blog/item/6d88cc37a68b5498a71e125a.html"&gt;这篇文章&lt;/a&gt;值得一读。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;尽可能使用HTTP头指定编码。 &lt;/li&gt;&#xD;
&lt;li&gt;尽可能使用UTF-8，或者至少全站所有资源使用统一编码。 &lt;/li&gt;&#xD;
&lt;li&gt;如果想使用UTF-16，就给文件加上BOM，以确定是Little Endian还是Big Endian的。 &lt;/li&gt;&#xD;
&lt;li&gt;如果使用&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;标签指定编码，可以不使用http-equiv的形式，但尽可能让标签出现在前面，至少保证在任何非ASCII字符之前。 &lt;/li&gt;&#xD;
&lt;li&gt;链接外部的脚本，如果无法确定编码相同的话，加上charset属性。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&#xD;
&lt;p style="text-align: right;"&gt;本文永久地址：&lt;a href="http://www.otakustay.com/learning-html5-charset/"&gt;http://www.otakustay.com/learning-html5-charset/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/2012732.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/04/11/learning-html5-charset.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/03/31/learning-html5-doctype.html</id><title type="text">HTML5标准学习 – DOCTYPE</title><summary type="text">这次主要讲述DOCTYPE的种种，意义不大，重在解释清楚标准。</summary><published>2011-03-31T10:34:00Z</published><updated>2011-03-31T10:34:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/31/learning-html5-doctype.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/31/learning-html5-doctype.html"/><content type="html">&lt;p&gt;&lt;a title="HTML5标准学习 &amp;ndash; 文档结构" href="http://www.otakustay.com/learning-html5-structure/"&gt;上一篇文章&lt;/a&gt;主要讲述了HTML文档的构成，同时肤浅地接触了&amp;ldquo;标签省略&amp;rdquo;这一概念，本文会从概念上介绍HTML文档中第一个出现的重要元素 - DOCTYPE。&lt;/p&gt;&#xD;
&lt;p&gt;所谓DOCTYPE，最初是XML的概念，即通过一种特定的语法，作为一种元数据，来描述XML文档中允许出现的元素，以及各元素的组成、嵌套规则等。具体的概念可以&lt;a title="文件类型描述 - 维基百科，自由的百科全书" href="http://zh.wikipedia.org/wiki/DOCTYPE"&gt;在WIKI中&lt;/a&gt;中得到一个更详细的结果。&lt;/p&gt;&#xD;
&lt;p&gt;但是在HTML中，DOCTYPE又有着一些不同的效果，其中之一就是著名的触发浏览器标准模式的功能。即如果没有DOCTYPE，浏览器会进入一种被称为Quirks模式的怪异状态，在该模式下，浏览器的盒模型、样式解析、布局等都与标准规定的存在差异。&lt;/p&gt;&#xD;
&lt;p&gt;需要注意的是，所谓的HTML标准、DOM标准等，只规定了在标准模式下的概念和行为，正如文档构成中提到的，DOCTYPE是一个HTML文档绝对不可以省略的部分，因此就根本不存在&amp;ldquo;Quirks模式&amp;rdquo;这样的概念。也正是因为标准中没有对Quirks模式做出任何的规定，因此不同浏览器在Quirks模式下的处理也是不同的，应用Quirks模式可谓难上加难。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;HTML4的DOCTYPE&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在HTML4的标准中，DOCTYPE被归属于&amp;ldquo;&lt;a title="7.2 HTML version information" href="http://www.w3.org/TR/1999/REC-html401-19991224/struct/global.html#h-7.2"&gt;HTML版本信息&lt;/a&gt;&amp;rdquo;一章中。在该章节中，标准指定了3种DOCTYPE：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;严格模式：&lt;code&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&amp;gt;&lt;/code&gt;。 &lt;/li&gt;&#xD;
&lt;li&gt;过渡模式：&lt;code&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&amp;gt;&lt;/code&gt;。 &lt;/li&gt;&#xD;
&lt;li&gt;框架模式：&lt;code&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"&amp;gt;&lt;/code&gt;。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;在HTML4的标准中，每一个DOCTYPE对应的dtd文件都是有合法的URL指定的，可以通过互联网进行下载。浏览器可以根据URL获得到dtd的具体内容，并根据内容的规定来解析文档。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;现实是不同的&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;HTML4如同XML一样，是一个相当理想化的标准。但是，现实往往并没有这么理想，试想下面的HTML文档：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" &#xD;
                      "http://www.w3.org/TR/html4/frameset.dtd"&amp;gt;&#xD;
&amp;lt;html&amp;gt;&#xD;
    &amp;lt;head&amp;gt;&#xD;
        &amp;lt;title&amp;gt;I'm not a frameset&amp;lt;/title&amp;gt;&#xD;
    &amp;lt;/head&amp;gt;&#xD;
    &amp;lt;body&amp;gt;&#xD;
        &amp;lt;p&amp;gt;So what?&amp;lt;/p&amp;gt;&#xD;
    &amp;lt;/body&amp;gt;&#xD;
&amp;lt;/html&amp;gt;&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;这个文档采用了一个框架模式的DOCTYPE，但其正文确没有使用任何&lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt;元素，相对应地使用了应该由严格模式或者过渡模式指定的，标准的HTML结构。那么在这种情况下，浏览器能做什么呢？&lt;/p&gt;&#xD;
&lt;p&gt;拒绝渲染该页面？不，浏览器不敢这么做，在激烈的市场竞争之中，如果因此而导致部分页面无法渲染的话，就只能眼睁睁看着市场份额注入别家田了。所以浏览器顶多弱弱地报一个警告以示抗议，却依旧得乖乖地解析出这个文档并正确渲染。&lt;/p&gt;&#xD;
&lt;p&gt;这就是所谓的浏览器的&amp;ldquo;容错性&amp;rdquo;，事实上无论你的DOCTYPE是什么，浏览器都会以最大的兼容能力去解析一个文档，并以最大的努力让这个文档显示得符合开发者的预期。而浏览器的这一特性，也逐渐让标准制定者开始意识到，DOCTYPE似乎真的不怎么重要。因此，在HTML5中，DOCTYPE发生了一次重大的变化&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;HTML5的DOCTYPE&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;到了HTML5了，这一变化相信多数人已经知道，就是HTML5将DOCTYPE的声明简化了，只需要&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/code&gt;即可。&lt;/p&gt;&#xD;
&lt;p&gt;正好前文所述，在HTML4时代，标准制定者已经认识到，DOCTYPE对浏览器的渲染并没有太大的帮助，除了给无聊的w3c验证器看以外，DOCTYPE似乎只有触发浏览器兼容模式的作用。于是标准工作组采取了非常实际的态度，测试了所有课程顺触发标准模式的最简DOCTYPE，最终得出了这一结论。&lt;/p&gt;&#xD;
&lt;p&gt;但是故事不会这么简单，标准工作组也不是完成这么简单的动作就撒手不管的无赖分子，事实上他们还是很尽责任地考虑到了向后兼容性、可扩展性等一系列的事情，最后将DOCTYPE一章用了大量文字来进行描述，得到一个非常详实的结果。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a title="HTML syntax - HTML5" href="http://dev.w3.org/html5/markup/syntax.html#doctype-syntax"&gt;HTML5的参考手册相关章节&lt;/a&gt;中，将DOCTYPE分为3类：&lt;/p&gt;&#xD;
&lt;dl&gt;&lt;dt&gt;普通DOCTYPE - normal doctype &lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;普通DOCTYPE就是我们所见的最简形式，即&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/code&gt;，他的真正组成是这样的：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;一段文本，即&lt;code&gt;&amp;lt;!DOCTYPE&lt;/code&gt;，大小写不敏感。 &lt;/li&gt;&#xD;
&lt;li&gt;1个或多个空格，关于空格的定义请参照&lt;a title="HTML5标准学习 &amp;ndash; 简介" href="http://www.otakustay.com/learning-html5-intro/"&gt;简介&lt;/a&gt;中的解释。 &lt;/li&gt;&#xD;
&lt;li&gt;字符&lt;code&gt;HTML&lt;/code&gt;，同样大小写不敏感。 &lt;/li&gt;&#xD;
&lt;li&gt;1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;结束标记，即&lt;code&gt;&amp;gt;&lt;/code&gt;。 &#xD;
        &lt;/li&gt;&#xD;
&lt;/ol&gt;&lt;/dd&gt;&lt;dt&gt;不再推荐的DOCTYPE - deprecated doctype &lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;即所谓HTML4时代的几个DOCTYPE，其组成如下：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;一段文本，即&lt;code&gt;&amp;lt;!DOCTYPE&lt;/code&gt;，大小写不敏感。 &lt;/li&gt;&#xD;
&lt;li&gt;1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;字符&lt;code&gt;HTML&lt;/code&gt;，同样大小写不敏感。 &lt;/li&gt;&#xD;
&lt;li&gt;1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;字符&lt;code&gt;PUBLIC&lt;/code&gt;，大小写不敏感。 &lt;/li&gt;&#xD;
&lt;li&gt;继续1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;一对引号或单引号（必须前后匹配），引号中放一个Public ID。 &lt;/li&gt;&#xD;
&lt;li&gt;可选内容： &#xD;
                &lt;ol&gt;&#xD;
&lt;li&gt;1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;一对引号或单引号（必须前后匹配），引号中放一个与前面的Public ID对应的System ID。 &lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
              &lt;/li&gt;&#xD;
&lt;li&gt;1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;结束标记，即&lt;code&gt;&amp;gt;&lt;/code&gt;。 &lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;在标准中，Public ID和System ID是有严格的对应关系的，如果规定的System ID不能有Public ID，则上面的第8项可选内容也就不能存在。HTML5彻底放弃了HTML4中的过渡型和框架型的DOCTYPE，同时整合了XHTML的DOCTYPE声明，得出以下6种组合方式：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"&amp;gt;&lt;/code&gt; &lt;/li&gt;&#xD;
&lt;li&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"&amp;gt;&lt;/code&gt; &lt;/li&gt;&#xD;
&lt;li&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"&amp;gt;&lt;/code&gt; &lt;/li&gt;&#xD;
&lt;li&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&amp;gt;&lt;/code&gt; &lt;/li&gt;&#xD;
&lt;li&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&amp;gt;&lt;/code&gt; &lt;/li&gt;&#xD;
&lt;li&gt;&lt;code&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&amp;gt;&lt;/code&gt; &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;/dd&gt;&lt;dt&gt;遗留工具DOCTYPE - leagacy tool compatible doctype &lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;顾名思义，完全是为了兼容久远时代的历史遗产而准备的DOCTYPE，甚至都已经没办法考证什么样的&amp;ldquo;工具&amp;rdquo;会搞出这种DOCTYPE来&amp;hellip;&amp;hellip;遗留工具型的DOCTYPE的组成如下：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;一段文本，即&lt;code&gt;&amp;lt;!DOCTYPE&lt;/code&gt;，大小写不敏感。 &lt;/li&gt;&#xD;
&lt;li&gt;1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;字符&lt;code&gt;HTML&lt;/code&gt;，同样大小写不敏感。 &lt;/li&gt;&#xD;
&lt;li&gt;1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;字符&lt;code&gt;SYSTEM&lt;/code&gt;，大小写不敏感。 &lt;/li&gt;&#xD;
&lt;li&gt;继续1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;一对引号或单引号（必须前后匹配），引号中放一段文本&lt;code&gt;about:legacy-compat&lt;/code&gt;，注意这段文本是大小写&lt;em&gt;敏感&lt;/em&gt;的。 &lt;/li&gt;&#xD;
&lt;li&gt;1个或多个空格。 &lt;/li&gt;&#xD;
&lt;li&gt;结束标记，即&lt;code&gt;&amp;gt;&lt;/code&gt;。 &lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;比如这样的DOCTYPE就属于此类：&lt;code&gt;&amp;lt;!doctype HTML system "about:legacy-compat"&amp;gt;&lt;/code&gt;，基本上除了大小写，没有什么值得改变的。&lt;/p&gt;&#xD;
&lt;/dd&gt;&#xD;
      &#xD;
    &#xD;
  &lt;/dl&gt;&#xD;
&lt;p&gt;&lt;strong&gt;现实的细节&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;对于DOCTYPE的作用，在真正的浏览中，仅仅起到触发浏览器的标准模式的作用。虽然根据标准，一个HTML文档中，DOCTYPE前可以有其他的元素，如一个U+FFEF的BOM，几个注释，一点空格，但是在当前的状态下，并没有这么理想：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;对于IE6-9，如果DOCTYPE前存在注释，会进入Quirks模式。 &lt;/li&gt;&#xD;
&lt;li&gt;对于IE6，如果DOCTYPE前存在一个XML声明，会进入Quirks模式。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;写完才发现，问题又全出在IE下&amp;hellip;&amp;hellip;关于DOCTYPE的问题纠结至此，下一章主要讲述编码声明的问题。&lt;/p&gt;&#xD;
&#xD;
&lt;p style="text-align: right;"&gt;本文永久地址：&lt;a href="http://www.otakustay.com/learning-html5-doctype/"&gt;http://www.otakustay.com/learning-html5-doctype/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/2001397.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/03/31/learning-html5-doctype.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/03/28/learning-html5-structure.html</id><title type="text">HTML5标准学习 - 文档结构</title><summary type="text">系列第二篇，先从HTML的文档结构开始熟悉。到底一个文档需要哪些，又有哪些可以省略，怎么样的文档才属于真正规范的文档？一切从标准开始</summary><published>2011-03-28T08:27:00Z</published><updated>2011-03-28T08:27:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/28/learning-html5-structure.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/28/learning-html5-structure.html"/><content type="html">&lt;p&gt;说起HTML的结构，很多人都能说得头头是道，一般来说答案可能是这样的：&lt;/p&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&#xD;
&lt;p&gt;一个DOCTYPE，一个html，里面有head和body元素。&lt;/p&gt;&#xD;
&lt;/blockquote&gt;&#xD;
&lt;p&gt;这当然不能说是不正确的，但是如果问到一个最小的HTML源文件必须有哪一些东西的话，恐怕很少有人能正确地做出回答。&lt;/p&gt;&#xD;
&lt;p&gt;先来回答一下这个问题，一个最简的HTML5源码文件需要的内容如下：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;是的，就这样，一个字符不多，一个字符不少，除了大小写可任意变化外，其他的任何内容都是不能变动的。&lt;/p&gt;&#xD;
&lt;p&gt;那么究竟是怎么样的规则，导致一个最简的源码文件必须有doctype声明呢？根据标准，一个HTML文档有如下内容组成（严格按照顺序）：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;一个BOM标记，且这个BOM标记必须为U+FEFF。&lt;/li&gt;&#xD;
&lt;li&gt;0-n个空格或注释。&lt;/li&gt;&#xD;
&lt;li&gt;DOCTYPE声明。&lt;/li&gt;&#xD;
&lt;li&gt;0-n个空格或注释。&lt;/li&gt;&#xD;
&lt;li&gt;一个HTML元素。&lt;/li&gt;&#xD;
&lt;li&gt;0-n个空格或注释。&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;这里存在着一些和HTML4的不同，一个HTML4的最简源码文件是这样的：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&#xD;
                      "http://www.w3.org/TR/html4/loose.dtd"&amp;gt;&#xD;
&amp;lt;title&amp;gt;这里是标题&amp;lt;/title&amp;gt;&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;两者的区别是显而易见的：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;HTML5把DOCTYPE修改为更简单的&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/code&gt;，这个已经众所周知了。&lt;/li&gt;&#xD;
&lt;li&gt;在HTML4中多了一个&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;标签。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;这里的重点就是&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;标签了，关于这个标签，在&lt;a title="The global structure of an HTML document" href="http://www.w3.org/TR/1999/REC-html401-19991224/struct/global.html#h-7.2"&gt;HTML4.01标准&lt;/a&gt;中是这么说的：&lt;/p&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&#xD;
&lt;p&gt;Every HTML document must have a TITLE element in the HEAD section.&lt;/p&gt;&#xD;
&lt;/blockquote&gt;&#xD;
&lt;p&gt;也即是说，HTML4要求&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;标签是必须存在的。&lt;/p&gt;&#xD;
&lt;p&gt;而在&lt;a title="4 The elements of HTML &amp;mdash; HTML Standard" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#the-title-element-0"&gt;HTML5的标准&lt;/a&gt;中，又是这么说的：&lt;/p&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&#xD;
&lt;p&gt;There must be no more than one title element per document.&lt;/p&gt;&#xD;
&lt;/blockquote&gt;&#xD;
&lt;p&gt;HTML5中只设定了&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;标签数量的上限，却没有指明下限，也就是说，没有&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;的文档已经被视为一个合法的文档了。&lt;/p&gt;&#xD;
&lt;p&gt;对于DOCTYPE，HTML4中设定了6种DOCTYPE，HTML5中将DOCTYPE分为3种，这个在以后的章节中再具体说明。&lt;/p&gt;&#xD;
&lt;p&gt;再回过来看一下文档组成，除去&lt;q&gt;0-n个空格或注释&lt;/q&gt;这样并没有多大意义的元素之外，组成的列表中还说明有一个HTML元素，但是最简的源码中却没有这东西。这是因为在HTML的规范中，一直存在&amp;ldquo;隐式标签&amp;rdquo;这样的概念，关于隐式标签，大致可以这么解释：&lt;/p&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&#xD;
&lt;p&gt;一部分元素，当满足特定的前提条件时，其开始标签或结束标签可以在源码中省略。在这种情况下，被省略的标签称为&amp;ldquo;隐式标签&amp;rdquo;。&lt;/p&gt;&#xD;
&lt;/blockquote&gt;&#xD;
&lt;p&gt;需要注意的是，此处的省略指的是&lt;em&gt;在源码中省略&lt;/em&gt;，而在最终成型的DOM树中，这个标签是存在的，因此才称为&lt;em&gt;隐式&lt;/em&gt;标签。因此上面最简的源码结构，在生成DOM树后，其真正的结构是这样的：&lt;/p&gt;&#xD;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&#xD;
&amp;lt;html&amp;gt;&#xD;
    &amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&#xD;
    &amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&#xD;
&amp;lt;/html&amp;gt;&#xD;
&lt;/code&gt;&lt;/pre&gt;&#xD;
&lt;p&gt;最后，再总结一下XHTML中的一些规范：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;因为是XML，所以为了表示这是一个HTML文档，必须有一个命名空间，其值为&lt;code&gt;http://www.w3.org/1999/xhtml&lt;/code&gt;。&lt;/li&gt;&#xD;
&lt;li&gt;因为是XML，所以MIME type不能是&lt;code&gt;text/html&lt;/code&gt;了，&lt;code&gt;text/xml&lt;/code&gt;、&lt;code&gt;application/xml&lt;/code&gt;、&lt;code&gt;application/xml+html&lt;/code&gt;都是比较好的选择。&lt;/li&gt;&#xD;
&lt;li&gt;因为是XML，必须有根元素，根元素为&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;，即&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;的开始和结束标签不能省略了。&lt;/li&gt;&#xD;
&lt;li&gt;因为是XML，所有元素只要有了开始标签，就不能没有结束标签，或者自闭合。&lt;/li&gt;&#xD;
&lt;li&gt;因为是XML，所有元素都得严格遵守大小写，元素名称必须为小写。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;因为是XML，文档变得严格了很多，也因为是XML，其可读性和规范性提高了不少。但最终，我们始终要在HTML的宽容性和XML的规范性之间找到最佳的平衡点，一味地追求极端始终是一个错误。&lt;/p&gt;&#xD;
&lt;p style="text-align: right;"&gt;本文永久地址：&lt;a href="http://www.otakustay.com/learning-html5-structure/"&gt;http://www.otakustay.com/learning-html5-structure/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/1997848.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/03/28/learning-html5-structure.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/03/25/learning-html5-intro.html</id><title type="text">HTML5标准学习 – 简介</title><summary type="text">HTML5标准学习系列第一章，一些名词的解释，其实HTML中到处都是陷阱哦~</summary><published>2011-03-25T04:40:00Z</published><updated>2011-03-25T04:40:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/25/learning-html5-intro.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/25/learning-html5-intro.html"/><content type="html">&lt;p&gt;最近前端的群都蛮热闹的，但我发现多数讨论的是javascript和css相关的问题，仿佛大家在努力创建各种交互、样式的时候，忘却了这一切的基础 - HTML。&lt;/p&gt;&#xD;
&lt;p&gt;其实我很喜欢HTML，觉得这个语言远比XML来得有趣，其灵活、轻便远非极端规范的XML可以比拟。同时又因为HTML的作用范围极小，规定的标签有限等说不上优点还是缺点的特色，使得HTML有着自己的确定性。&lt;/p&gt;&#xD;
&lt;p&gt;本系列的前面很大一部分会以非常短小的篇幅，介绍HTML5中的一些基本概念，并且：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;只关心HTML这个语言，其他的javascript或者css完全不会涉及。 &lt;/li&gt;&#xD;
&lt;li&gt;只关注HTML，对XHTML会简要带过，但不会详细说明，这源于XHTML有着比HTML更严格的规范，对浏览器的解析而言，可以认为是HTML的一个子集。 &lt;/li&gt;&#xD;
&lt;li&gt;主要参考了&lt;a title="HTML5: The Markup Language Reference" href="http://dev.w3.org/html5/markup"&gt;whatwg的官方文档&lt;/a&gt;，并对现有主流浏览器的兼容性进行了评估。 &lt;/li&gt;&#xD;
&lt;li&gt;介绍的全是基本的概念，不会涉及文档解析、DOM树构建、脚本执行之类的实现细节。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;在这之后，可能会提取部分与浏览器的运行相关的技术细节，如脚本的解析、执行等话题来进行更深入的探讨。&lt;/p&gt;&#xD;
&lt;p&gt;如果你觉得HTML不爽，因为开始标签和结束标签可以随便省略太不规范，那么这个系列会告诉你，HTML的标签省略是有非常严格的规定的&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;如果你觉得HTML简单，大不了照着XML来写，那么这个系列会告诉你，HTML里充满着陷阱，即便不考虑各浏览器的实现，你也不见得能安全走过这片雷区&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;在开始这个系列以前，有几个非常重要的概念需要介绍。HTML中有着很多很多的概念，具体可以参见&lt;a title="HTML5: The Markup Language Reference - Terminology" href="http://dev.w3.org/html5/markup/terminology.html"&gt;Terminology&lt;/a&gt;一章，但有一部分并不会在参考手册中被广泛引用，只有其中的一小块有着了解的意义：&lt;/p&gt;&#xD;
&lt;dl&gt;&lt;dt&gt;浏览上下文 - browsing context &lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;就是我们通常见到的页面，确切地主，浏览上下文是一个环境，在这个环境中，HTML会被解析、构建，CSS样式会被计算、应用，javascript脚本会被加载、执行，最终展现出终端用户可以看到的内容。&lt;/p&gt;&#xD;
&lt;p&gt;通常来说，我们最常接触的上下文有这么几种：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;一个浏览器窗口，比如一个IE6窗口。 &lt;/li&gt;&#xD;
&lt;li&gt;多标签浏览器里的一个标签，Firefox、Chrome等浏览器都是这么做的。 &lt;/li&gt;&#xD;
&lt;li&gt;一个&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;元素也是一个独立的浏览上下文。 &lt;/li&gt;&#xD;
&lt;li&gt;在frameset中，一个&lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt;元素也会形成一个独立的浏览上下文。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;/dd&gt;&lt;dt&gt;换行 - new line &lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;和计算机基础课程所学的一样，HTML定义了3种类型的换行符：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;字符U+000D，即\r字符。 &lt;/li&gt;&#xD;
&lt;li&gt;字符U+000A，即\n字符。 &lt;/li&gt;&#xD;
&lt;li&gt;字符U+000D后跟着U+000A，即\r\n字符串。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;不同的系统有其默认的换行输出，HTML的规定保证浏览器能正确解析现在主流的3种换行方式。&lt;/p&gt;&#xD;
&lt;/dd&gt;&lt;dt&gt;空格 - space &lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;空格在HTML中有着非常重要的作用，如一个元素的各属性之间可以通过空格分隔。正常情况下，开发人员会按一下空格键来输入一个空格，当属性很多的时候，可能使用回车来将属性更好地排列起来。但是大概很少有人会去思考，为什么回车的效果和空格是一样的，HTML还能解析哪一些字符作为空格呢？&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;字符U+0020，就是普通的空格。 &lt;/li&gt;&#xD;
&lt;li&gt;字符U+0009，就是\t水平制表符。 &lt;/li&gt;&#xD;
&lt;li&gt;字符U+000A，就是\n换行符。 &lt;/li&gt;&#xD;
&lt;li&gt;字符U+000D，就是\r回车符。 &lt;/li&gt;&#xD;
&lt;li&gt;字符U+000C，这个是分页符，很少见到。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;关于空格的问题，其中jQuery也犯过这个问题，甚至至今都存在这个问题。在&lt;a title="jQuery 1.5.1 source" href="http://code.jquery.com/jquery-1.5.1.js"&gt;jQuery 1.5.1&lt;/a&gt;的第1738行定义了一个正则表达式&lt;code&gt;/[\n\t\r]/g&lt;/code&gt;，用于通过空格来分隔元素的class属性。在1.4.4版本中，这个正则是&lt;code&gt;/[\n\t]/g&lt;/code&gt;很显然漏掉了\r，&lt;a title="HASCLASS, REMOVECLASS DON'T WORK IN IE IF ATTRIBUTE CONTAINS \R CHARACTER" href="http://bugs.jquery.com/ticket/7673"&gt;这个BUG项&lt;/a&gt;就说明了这个问题。当然很遗憾，1.5.1版本依旧没有照顾到标准中的所有空格字符，即没有对U+000C分页符的处理。&lt;/p&gt;&#xD;
&#xD;
&lt;p style="text-align: right;"&gt;本文永久地址：&lt;a href="http://www.otakustay.com/learning-html5-intro/"&gt;http://www.otakustay.com/learning-html5-intro/&lt;/a&gt;&lt;/p&gt;&#xD;
&lt;/dd&gt;&lt;/dl&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/1995280.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/03/25/learning-html5-intro.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/03/08/browser-strategy-loading-external-resource.html</id><title type="text">各浏览器对页面外部资源加载的策略</title><summary type="text">本篇文章就使用几种流行的浏览器，针对同一个页面的外部资源加载过程进行分析，推测各浏览器加载外部资源的策略、特征，并最后给予一定的比较和总结。</summary><published>2011-03-08T09:33:00Z</published><updated>2011-03-08T09:33:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/08/browser-strategy-loading-external-resource.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/08/browser-strategy-loading-external-resource.html"/><content type="html">&lt;p&gt;这个总结来源于一次优化的请求，最初某个页面的加载十分缓慢，load事件迟迟无法触发，因此希望可以通过对静态文件分域名等方式对页面的外部资源进行优化，拿得load事件尽可能早地触发。&lt;/p&gt;&#xD;
&lt;p&gt;于是我查看了页面的源码，并对外部资源进行了整理，基于下面2个理念画出了一个推测的瀑布图：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;浏览器对同一个域只能并发2个HTTP请求 - 网上盛传已久。 &lt;/li&gt;&#xD;
&lt;li&gt;javascript文件的加载会阻塞浏览器其他资源的加载 - 同样网上盛传已久。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;然而，当我看到各浏览器中实际的瀑布图时，我知道自己又犯了一个简单的错误：太过相信所谓的权威和大众的声音，而没有更早地进行实践来检验理论的正确性&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;本篇文章就使用几种流行的浏览器，针对同一个页面的外部资源加载过程进行分析，推测各浏览器加载外部资源的策略、特征，并最后给予一定的比较和总结。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;测试样例&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;测试的页面结构如下：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;head &#xD;
    &#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;1.css + 1.js &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;/li&gt;&#xD;
&lt;li&gt;body &#xD;
    &#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;1.jpg + 2.jpg + 2.js + 2.css + 3.jpg + 4.jpg + 3.css + 3.js + 5.jpg + 6.jpg &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;共12个外部资源，加上页面本身，一次完整的加载一共有13次HTTP GET请求。&lt;/p&gt;&#xD;
&lt;p&gt;针对每一个外部资源，服务器首先会休眠5秒的时间，随后再返回相应的内容，以方便查看整个外部资源的加载过程。&lt;/p&gt;&#xD;
&lt;p&gt;测试的浏览器如下：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;IE6 &lt;/li&gt;&#xD;
&lt;li&gt;IE8 &lt;/li&gt;&#xD;
&lt;li&gt;Firefox3.6 &lt;/li&gt;&#xD;
&lt;li&gt;Firefox4.0 beta12 &lt;/li&gt;&#xD;
&lt;li&gt;Chrome 8 &lt;/li&gt;&#xD;
&lt;li&gt;Opera 11 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;IE6&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-ie6.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;IE6的瀑布图非常传统，其特征有：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;各资源按照在HTML中出现的顺序进行加载。 &lt;/li&gt;&#xD;
&lt;li&gt;javascript文件会阻塞其后所有资源的加载。 &lt;/li&gt;&#xD;
&lt;li&gt;最大并发HTTP连接数为2个。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;可见网上盛传的2个&amp;ldquo;误区&amp;rdquo;都来自IE6统治浏览器市场的时代，针对IE6的优化太多太多，大家也就习惯性地将这些结论作为公理来使用了。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;IE8&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-ie8.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;和IE6完全不同的瀑布图，其特点有：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;最大并发HTTP连接数为6个。 &lt;/li&gt;&#xD;
&lt;li&gt;javascript文件已经不会阻塞其他资源的加载，甚至多个javascript文件可以一起加载，并且会保证执行的顺序。 &lt;/li&gt;&#xD;
&lt;li&gt;会分析HTML结构，优先下载script和link标签定义的外部资源。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Firefox3.6&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-firefox3.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;和IE8的几乎完全一样：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;最大并发HTTP连接数为6个（可在about:config中修改）。 &lt;/li&gt;&#xD;
&lt;li&gt;javascript文件不会阻塞其他资源的加载，多个javascript文件可以一起加载。 &lt;/li&gt;&#xD;
&lt;li&gt;会分析HTML结构，优先下载script和link标签定义的外部资源。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Firefox4 beta12&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-firefox4.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;不知是因为设计理念上的不同，还是因为beta版未照顾到这一块，Firefox4反而退化了，和Firefox3.6的区别主要体现在对资源类型的处理上，Firefox4不再严格地优先下载script和link标签定义的外部资源，而是按照HTML结构中出现的顺序来进行加载。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Chrome8&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-chrome.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Chrome自带的工具不能很清楚地表示各请求的开始时间，所以使用了Fiddler的瀑布图，从图上可以看出，Chrome也是比较特立独行的一位，其特点有：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;最大并发HTTP连接数为6。 &lt;/li&gt;&#xD;
&lt;li&gt;head部分的资源会单独下载，且阻塞body中的其他资源的加载。 &lt;/li&gt;&#xD;
&lt;li&gt;会优先加载script和link标签定义的资源。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Opera11&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="http://www.otakustay.com/wp-content/uploads/2011/03/timeline-opera.png" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;先报怨一下，Dragonfly不怎么好用来着&amp;hellip;&amp;hellip;Opera的资源加载也比较有特色，而且很难看出规律，只能大致总结一下：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;&lt;del&gt;最大并发HTTP连接数为5（网上有说原先版本是4）。&lt;/del&gt;&lt;ins&gt;经过网友的指正，Opera的最大并发HTTP连接数默认为16，可在&lt;code&gt;opera:config - Performance - Max Connections Server&lt;/code&gt;查看和修改。&lt;/ins&gt; &lt;/li&gt;&#xD;
&lt;li&gt;javascript文件的加载会阻塞其他script和link标签定义的外部资源的加载，如图中的2.js。但不会阻塞图片等其他资源的加载，如图中的3.js。 &lt;/li&gt;&#xD;
&lt;li&gt;会一定程度上对资源的优先级进行优化，但由于javascript文件要阻止后续部分资源的加载，又为了充分利用最大HTTP连接数，因此不能严格先加载所有的script和link标签定义的资源，导致瀑布图上各类型资源有相互穿插，难寻规律。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;抛开IE6不论的话，除非是在线相册之类外部资源非常多的页面，不然没必要去追求静态资源的分域名优化。 &lt;/li&gt;&#xD;
&lt;li&gt;针对IE6进行静态资源分域名优化时，要严格注意javascript文件对后续资源的阻塞，进行精确计算和设计后保证资源最完美地分域名存储，以提供最大并行度。 &lt;/li&gt;&#xD;
&lt;li&gt;鉴于Chrome对head部分的资源会独立加载，当head部分用不满6个HTTP并发数时，是否可以将资源移到body中呢？在body中的资源又会引起其他的问题，需要谨慎考虑。 &lt;/li&gt;&#xD;
&lt;li&gt;Opera的行为比较怪异，似乎主动设计了一个很麻烦的算法，不过考虑到其占有率，就先放在一边吧&amp;hellip;&amp;hellip;而且号称最快的浏览器的Opera，在加载javascript文件时竟然如此笨拙&amp;hellip;&amp;hellip; &lt;/li&gt;&#xD;
&lt;li&gt;Firefox4 beta12的行为让人无法理解，看来要追踪RC版是否还存在这个问题，如果存在的话可以考虑找Mozilla报个问题了。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;对各浏览器加载外部资源的策略的掌握，是&lt;abbr title="Web Performance Optimize"&gt;WPO&lt;/abbr&gt;的基本元素，虽然一直想当一个WPO的专家，却在这方面迟迟不愿实践，实在有愧于自己的理想&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;最后，如果有哪位朋友了解Opera对资源加载的具体策略的，还请提供一下，以便有更清晰地认知，谢谢~！&lt;/p&gt;&#xD;
&lt;p style="text-align: right;"&gt;本文永久地址：&lt;a href="http://www.otakustay.com/browser-strategy-loading-external-resource/"&gt;http://www.otakustay.com/browser-strategy-loading-external-resource/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/1977480.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/03/08/browser-strategy-loading-external-resource.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/GrayZhang/archive/2011/03/02/mario-project-management.html</id><title type="text">超级玛丽奥项目管理</title><summary type="text">超级玛丽奥，一个无比经典的游戏，在红白机上的受欢迎程度无出其右，游戏的设计必有其出色之处，才导致那么多人的痴迷。本篇文章试图将超级玛丽的游戏设计的部分理念和细节转换为项目管理的方案，使用游戏的方式去管理项目，找寻一条快乐的管理之道。</summary><published>2011-03-02T07:07:00Z</published><updated>2011-03-02T07:07:00Z</updated><author><name>Gray Zhang</name><uri>http://www.cnblogs.com/GrayZhang/</uri></author><link rel="alternate" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/02/mario-project-management.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/GrayZhang/archive/2011/03/02/mario-project-management.html"/><content type="html">&lt;p&gt;超级玛丽奥，一个无比经典的游戏，在红白机上的受欢迎程度无出其右，游戏的设计必有其出色之处，才导致那么多人的痴迷。本篇文章试图将超级玛丽的游戏设计的部分理念和细节转换为项目管理的方案，使用游戏的方式去管理项目，找寻一条快乐的管理之道。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;游戏的组成&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;超级玛丽的游戏组成非常简单，只有几个必要的概念，但是可以玩出无数的花样：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;主角&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;一个水管工，名叫玛丽奥，某天他的公主被邪恶的大魔王抓走了，于是开始了拯救公主的征途&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;在项目中，主角无疑是整个团队，首先保证整个团队的一致性和不可分割性，使其成为一个单独的个体，而非若干个个体组合起来的松散的组织。当团队拿到项目的这一刻，就如同站在屏幕左边的玛丽奥，一段征程就此开始。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;关卡&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;游戏的最基本组成是一个一个的关卡，每一个关卡最后都有已知的惊喜（不是一个城堡就是一只公主），正因为关卡这个概念的存在，才造就了游戏的多样化和挑战性。&lt;/p&gt;&#xD;
&lt;p&gt;相对项目来说，一个关卡可以变成一个里程碑，每一个里程碑最后也都有着预先准备的&amp;ldquo;惊喜&amp;rdquo;。每一个里程碑成功交付之时，作为管理者，必须让所有成员意识到我们又突破了一个关卡，这是值得庆祝的事，并且要让每一个成员都认可这是团队一起努力后应得的结果。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;坑&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;出于游戏中怪物智商普遍低于平均线，&amp;ldquo;坑&amp;rdquo;这一事物反而成了游戏中的一大障碍。跑着跳着欢快着，一不小心掉进个坑里，是多么没有面子的事情。&lt;/p&gt;&#xD;
&lt;p&gt;对于项目，所谓的坑，自然是一个又一个的困难。在一个里程碑开始之初，就应当规划好整个头上的&amp;ldquo;场景&amp;rdquo;，整个团队有权利也有义务知道，按照小小主人公的奔跑速度，在什么时候会遇上坑，是一个怎么样的坑，以便团队事先做好应对的措施。&lt;/p&gt;&#xD;
&lt;p&gt;而坑也是游戏中极富多样性的一个元素，也许玩的时候并没有仔细地分析，坑的各类其实是很多的：&lt;/p&gt;&#xD;
&lt;dl&gt;&lt;dt&gt;标准坑 &lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;大量存在于各个关卡之中，只要按标准的速度&amp;ldquo;走&amp;rdquo;过去，在适当的里面起跳就可以轻松地跃过。但是永远也不要小看这样的坑，当地形变得复杂，一个又一个的标准坑连在一起，中间只剩一个人的容身之所，就会让游戏的难度大大增加。&lt;/p&gt;&#xD;
&lt;p&gt;同样，在项目中，最常遇上的困难也就是如此简单的标准坑，只要团队按着计划的步调前进，在适当的时候给予一次小小的冲刺，就可以安全地度过。但是当这类不大不小的困难连续出现，在解决一个问题之后又紧接着出现另一个问题，之间只留下勉强喘息的时间之时，就是对项目组的一个考验。如果在项目开始之初就对关卡的地形了如指掌，事先做好全面的心理准备，在通过的过程中调整好自己的步调，相信绝大多数的&amp;ldquo;玩家&amp;rdquo;还是不会败在这种环境之下的。&lt;/p&gt;&#xD;
&lt;/dd&gt;&lt;dt&gt;大坑 &lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;大坑的跨度之大，足以让没有充分助跑就随意起跳的玛丽奥同志坠入无限的深渊。在初代的游戏中，最大的一个坑甚至需要足够的助跑，在平地的边缘起跑，才可以勉强地落到对岸。&lt;/p&gt;&#xD;
&lt;p&gt;大坑对项目来说绝对是一种挑战，在这段时间内，项目组将不可避免地出现火力全开的情况，甚至要为此加班加点。但即便如此，如果没有之前的助跑，无论你的弹跳力多么超群，在大坑面前都无法避免跌入地狱的结局。因此作为项目进度制定者，对于大坑必须有明确的标识，提前一定的时间知会整个项目组。此时项目组需要开始调整自己的节奏，为即将到来的攻坚战作好准备，以最大的冲刺速度突前，直到坑的边缘，决然地起跳。&lt;/p&gt;&#xD;
&lt;p&gt;每一次跃过大坑，都会给玩家带来成就和喜悦之感，往往项目也正是通过对困难的征服所带来的成就感，才得以保持整个团队的士气，一直向着最后的终点冲刺。&lt;/p&gt;&#xD;
&lt;/dd&gt;&lt;dt&gt;碎坑 &lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;碎坑是一种很特殊的坑，他由非常多但非常窄的坑组成，每2个坑之间仅容下一个身位的立足之所。&lt;/p&gt;&#xD;
&lt;p&gt;在项目的进行过程中，遇上这样的情况也是不可避免的。小小的麻烦总是不断地骚扰，当一个函数出现了点问题、当客户来电说需要有一个小小的修改、当有同仁身体不适需要休息&amp;hellip;&amp;hellip;而当这些细碎的问题撞在一起时，一个典型的&amp;ldquo;碎坑地形&amp;rdquo;就出现了。&lt;/p&gt;&#xD;
&lt;p&gt;那么如何去应对矿坑地带呢？玩过游戏的人都知道，面对这样的地形，与其小心翼翼地从每一个坑上跳过、屏住呼吸随时注意自己的下一个落点、提心掉胆有惊无险地通过，不如在不远处开始加速，以飞奔的速度从上面通过，碎坑是可以直接跑过去的，而不需要起跳这样笨拙的动作。&lt;/p&gt;&#xD;
&lt;p&gt;同样映射到项目之中，当面对一个碎坑地形的时候，如果管理者可以及早地发现问题，并通告整个团队。那么团队只需要一鼓作气，加快自己的节奏，哪怕无可避免地有一些加班加点的情况，但只要拥有足够的速度，碎坑将如同平地，无法给项目的进度造成任何的阻挠。&lt;/p&gt;&#xD;
&lt;/dd&gt;&lt;/dl&gt;&#xD;
&lt;p&gt;&lt;strong&gt;砖&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;砖也是游戏场景中无处不在的重要元素，主角可以拿他那比金子还硬的拳头（绝对不明脑袋）去敲一下砖头，至于砖头里有什么，那就另当别论了。当然可以肯定的是，不会敲出一个BOSS来：）&lt;/p&gt;&#xD;
&lt;p&gt;在项目中，砖可以是一些起眼或者不起眼的小细节，而是不是去敲这个砖，并不会影响到项目整体，即便一个砖都不敲，项目最终也是可以交付的。只是砖作为一种额外的收益，如果花些心思去敲了，往往能得到一点什么。&lt;/p&gt;&#xD;
&lt;p&gt;同样的，砖也有很多种：&lt;/p&gt;&#xD;
&lt;dl&gt;&lt;dt&gt;普通砖&lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;普通砖遍地都是，稍微敲一下就会碎裂，但往往不会给出什么东西。当然也存在极少数的情况，会出来一个蘑菇或者一朵鲜花，当然也有敲不完的金币。总得来说，普通砖里充满了机遇，但过分追求的结果往往是失望。&lt;/p&gt;&#xD;
&lt;p&gt;对于项目，我们也经常能看到这样的现象，一个小小的元件摆放在那边，从各个角度看都有让人重构的冲动。但是对于管理者来说，这样的重构是不是值得，敲下这块砖会不会出现自己需要的收益，却是一个非常需要关注的事情。在大多数的情况下，我们并不反对去敲每一块砖；但是如果希望项目在绝对最短的时间内完成交付，是不是也同样可以选择忽略那些平凡的&amp;ldquo;砖&amp;rdquo;，用这一跳的时间去做更值得关注的事？&lt;/p&gt;&#xD;
&lt;/dd&gt;&lt;dt&gt;方砖&lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;如果不是因为这东西不能拿去砸怪物的话，我很乐意称之为&amp;ldquo;板砖&amp;rdquo;。这是何等坑爹的一种砖，他就是那么一个广场，无论你怎么敲他，他都不会碎裂，也不会挤出哪怕是那么一丝的分数给你。&lt;/p&gt;&#xD;
&lt;p&gt;当然项目中这样的情况也不少，当你从一开始就走在一条错误的道路上尝试，无论怎么努力也得不到回报。但是请不要气馁不要绝望，正是因为有这样无数的尝试，你的团队才能确定这块砖是不是真正的方砖，是不是绝对不会产出任何的收效，这样团队才可以在日后遇上类似情况时避免进入一个无谓的圈套去挣扎不休，而是直接忽略那个看起来充满诱惑的炸弹，直接冲向目标。要相信项目中每一分投入都会有相应的回报，每一次努力都有其应有的价值。&lt;/p&gt;&#xD;
&lt;/dd&gt;&lt;dt&gt;问号砖&lt;/dt&gt;&lt;dd&gt;&#xD;
&lt;p&gt;问号砖太显眼了，除了上面有个大大的问号，还会不断地闪烁着光芒提示你来敲。而且，问号砖是不会不给你东西的，少则一只蘑菇，甚至有可能是一个无敌的星星。唯一的遗憾是，问号砖的数量还是比较稀少的。&lt;/p&gt;&#xD;
&lt;p&gt;项目中，把握好每一个问号砖是非常关键的，一个砖带来的收益往往能起到决定性的作用，正如一个无敌星星能让你在以后很长的一段路程中平安无事（前提是别傻到掉坑里）。一但发现有一个如此闪亮的砖头，即刻组织一定的人力物力去敲掉，收益绝对能大于成本。这样的砖往往是一个全新进入视野的第三方组件、或者一位有意合作的资深人士、或者一款制作精良的工具，他将为项目接下去的进度提供足够的推力，不仅仅是对项目执行的帮助，也是整个团队士气振奋的关键所在。&lt;/p&gt;&#xD;
&lt;/dd&gt;&lt;/dl&gt;&#xD;
&lt;p&gt;&lt;strong&gt;最终BOSS&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;最后的BOSS，抢走我们（一点也不）可爱的公主的大魔王，库巴老乌龟，他始终那么耐心地将公主绑起来放在自己的房间里，然后站在一座桥上面等着主人公的来到。从剧情的角度看，库巴实在是无比可爱，无论是哪一个关卡，都会充满耐心地迎接剧情&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;对项目来说的最终BOSS，那无疑就是项目的交付了，虽然说交付往往并不是终点，但是对于项目来说，至少是很大很大的一步，称其为BOSS也没有任何的过错。&lt;/p&gt;&#xD;
&lt;p&gt;玩游戏的都知道，库巴有2种打法，其一是慢慢地虐死他，其二是跳到他身后碰一下那个斧头，然后咔嚓一下库巴就掉火里去了&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;相对的，项目的交付也有两种方法：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;其一，简单地完成项目的需求，给予一个最低限度可运行的成果，完成基本的交付工作，获得项目的相应报酬。这算是一种皆大欢喜的结果，项目组使用了最合理的人力物力，完成了项目的需求；客户也拿到了其所希望的产品，并为此支付合理的报酬。 &lt;/li&gt;&#xD;
&lt;li&gt;其二，深入地理解客户的需求，不断挖掘出潜在的需求，完成良好的设计，构架起精美的实现，最后交付一个超出客户期望的产品。也许对于项目组来说，投入了更多的人力物力，却最终只得到合同中协议的报酬，就好像费劲力气折腾死了库巴，最后也就和（一点也不）可爱的公主说上一句话。但是玩游戏的人能从中体会到更大的乐趣，项目也是一样，在收获合同规定的报酬之余，项目组也获得了客户更好的认同，以及客户的忠诚度，为未来的发展打下了一个基础，在将来的某一时机，会得到应有的回报。 &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;项目过程&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在&amp;ldquo;超级玛丽式项目管理&amp;rdquo;中，我们将游戏的元素运用到项目，让所有成员可以更直观（相信多数人玩过超级玛丽，也知道这些概念）地理解项目的整个过程，以达到更紧密的团队合作。&lt;/p&gt;&#xD;
&lt;p&gt;项目启动之初，项目经理必须能够根据项目的进度安排，与技术经理一起制作出&amp;ldquo;关卡&amp;rdquo;和&amp;ldquo;地图&amp;rdquo;，一个关卡即一个里程碑，在每个关卡的地图中，要明确标识出&amp;ldquo;坑&amp;rdquo;的位置以及大小。&lt;/p&gt;&#xD;
&lt;p&gt;随着项目的启动，我们的主角玛丽奥先生将会出现在地图之上。随着项目的推进，先生也不断前进着。从地图上可以轻松地看出，项目是不是快要遇上&amp;ldquo;坑&amp;rdquo;了，会是一个怎么样的坑，全团队都可以直观地对项目的现阶段进展进行理解，当有&amp;ldquo;大坑&amp;rdquo;出现时，提前作好冲刺准备，当&amp;ldquo;碎坑地形&amp;rdquo;出现时，鼓足干劲通往直前&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;当然，项目并不是游戏那样有着最初静态的设定的。项目必须随着时间的推移不断地拥抱变化，地图也需要实时地进行修改，甚至加入一些通过下水道进入的特殊关卡，甚至是下水、上天等特殊情况。相信熟练玩游戏的开发者们，不会面对游戏的变化产生任何害怕的情绪，相反，也许会激起他们的好胜之心，更加投入地去解决困难。&lt;/p&gt;&#xD;
&lt;p&gt;最后，当我们的主角站在桥上，面对着最后的大魔王库巴老好人的时候，团队作出最后的努力，战胜BOSS，解救公主，然后是盛大的庆祝，一个项目圆满落幕&amp;hellip;&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;如何让成员更有干劲&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;至此，一个项目的元素基本齐全，项目可以稳步地进行。但是过于稳定的环境，会逐渐让项目成员产生疲倦的心态，效率也会按一定的曲线开始下降。这对于管理者来说，自然是不愿意看到的现象，因此，必须有一定的激励机制，让成员可以随时处在一个较好的心理状态。在游戏中也有一些这样额外的元素，可以引申到项目之中，对成员士气的激励有着非常好的效果：&lt;/p&gt;&#xD;
&lt;dl&gt;&lt;dt&gt;生命蘑菇 &lt;/dt&gt;&lt;dd&gt;游戏中最充满传奇色彩的元素，怎么看都不能吃的绿色蘑菇，却有着生命+1的效果。生命蘑菇永远出现在不可见的地方，当你在特定的位置停下脚本，轻轻地起跳，突然就出现了一块砖，一只漂亮的蘑菇落在你的脚边&amp;hellip;&amp;hellip;对于项目来说，生命蘑菇就是对成员额外的努力的奖励，每一个成员都有完成本职工作的义务，但是在义务之外，如果对项目有着卓越的贡献，自然需要给予相应的奖赏，以至最后形成一个良好的循环，每一个成员都会自发地从项目的角度寻找突破口，并自觉地给予更大的产出。生命蘑菇的哲理是&amp;ldquo;你永远看不到他，但他就在那儿&amp;rdquo;，只要自身足够努力，他不会离你太远。 &lt;/dd&gt;&lt;dt&gt;怪物（们） &lt;/dt&gt;&lt;dd&gt;怪物在游戏中几乎以弱智的形态出现，对玩家没有太大的威胁，但是当一脚踩扁一只蘑菇，一下踢飞一只乌龟，这种快感也是游戏吸引人的重要因素。因此在项目中，作为项目正常进行之余，定期进行总结、交流，提出一些更具挑战性的&amp;ldquo;子项目&amp;rdquo;来&amp;ldquo;玩玩&amp;rdquo;，确实有助于保持住团队成员的状态。同时，怪物是多样性的，技术的攻关也要具备多元化的特点，让不同职位、不同专长的人都可以平均地得到表现的机会。 &lt;/dd&gt;&lt;/dl&gt;&#xD;
&lt;p&gt;&lt;strong&gt;其他元素&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;游戏中还有很多其他的细节，每一样都能映射到项目中的某个方面，比如：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;超级玛丽的任何一关，只要按住奔跑键和向前键，在适当的地方起跳，绝对可以毫不停留的冲到终点。但是一但有一处的停留，后面的进程反而会遇到种种麻烦，不是躲避满身是刺的怪物，就是各种大坑需要计算距离来助跑&amp;hellip;&amp;hellip;&lt;/li&gt;&#xD;
&lt;li&gt;除了库巴老同志外，每一关卡的最后都有一根旗杆。其实拿到旗杆的满分非常容易，但是无数玩家为了能跑过杆子而孜孜不倦地努力着。但其实这根杆子是跳不过去的&amp;hellip;&amp;hellip;玩家只是看到了这么一丝希望，他们就会不断去努力。&lt;/li&gt;&#xD;
&lt;li&gt;初代的超级玛丽不够刺激，变因不够多，用作充满变化的项目不适合？那么看看&lt;a title="超级变态搞笑超级玛丽" href="http://www.aipai.com/c1/PT86OSYkbiFoJCY.html" rel="external"&gt;这个变态版的超级玛丽&lt;/a&gt;，想办法再用到项目中去？&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&#xD;
&lt;p style="text-align: right;"&gt;本文永久地址：&lt;a href="http://www.otakustay.com/mario-project-management/"&gt;http://www.otakustay.com/mario-project-management/&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/GrayZhang/aggbug/1968942.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/GrayZhang/archive/2011/03/02/mario-project-management.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
