<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_雨中无伞-----WEB前端开发</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/38146/rss</id><updated>2012-02-09T01:36:04Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/38146/rss"/><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/02/09/2343503.html</id><title type="text">HTML5语音输入（淘宝语音搜索）方法</title><summary type="text">谷歌的网站是时逛时新啊，今天在他们首页发现了HTML5的新玩法——语音搜索。可惜的是只有webkit核心的浏览器才能使用。用法很简单 只需要在input添加属性x-webkit-speech即可，例子如下： &amp;lt;input type=&amp;quot;text&amp;quot; x-webkit-speech /&amp;gt; 这样你的输入框右边里就多了个「小话筒」，点击的时候就会提示 这时说出来识别后就可以了，我测试下来，中文英语的识别率还挺高的。 语音输入其他属性： lang 这玩意可以强制输入框里面的语音的语言种类，例如 &amp;lt;input type=&amp;quot;text&amp;quot; x-webkit</summary><published>2012-02-09T01:36:00Z</published><updated>2012-02-09T01:36:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/09/2343503.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/09/2343503.html"/><content type="html">&lt;div&gt;&lt;div id="MyContent"&gt; &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;谷歌的网站是时逛时新啊，今天在他们首页发现了HTML5的新玩法&amp;#8212;&amp;#8212;语音搜索。可惜的是只有webkit核心的浏览器才能使用。用法很简单&lt;br style="margin: 10px" /&gt; 只需要在input添加属性&lt;code style="color: #000000; font-family: Consolas, Monaco, Courier, monospace, fixed; background-color: #eeeeee; background-origin: initial; background-clip: initial"&gt;x-webkit-speech&lt;/code&gt;即可，例子如下：&lt;/p&gt; &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;&lt;br style="margin: 10px" /&gt; &amp;lt;&lt;code style="color: #000000; font-family: Consolas, Monaco, Courier, monospace, fixed; background-color: #eeeeee; background-origin: initial; background-clip: initial"&gt;input type="text" x-webkit-speech /&lt;/code&gt;&amp;gt;&lt;/p&gt; &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;&lt;br style="margin: 10px" /&gt; 这样你的输入框右边里就多了个「小话筒」，点击的时候就会提示&lt;/p&gt; &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;&lt;a href="http://liumiao.me/UploadFiles/2012-01/admin/20121196980438340.png" target="_blank"&gt;&lt;img src="http://liumiao.me/UploadFiles/2012-01/admin/20121196980438340.png" alt="大图" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;&lt;/p&gt; &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;这时说出来识别后就可以了，我测试下来，中文英语的识别率还挺高的。&lt;/p&gt; &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;语音输入其他属性：&lt;/p&gt; &lt;ul style="margin: 5px 0px"&gt;&lt;li style="margin: 5px 0px; line-height: 1.5em"&gt;     &lt;p&gt;&lt;strong&gt;&lt;code style="color: #000000; font-family: Consolas, Monaco, Courier, monospace, fixed; background-color: #eeeeee; background-origin: initial; background-clip: initial"&gt;lang&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;     &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;这玩意可以强制输入框里面的语音的语言种类，例如&lt;br style="margin: 10px" /&gt;     &amp;lt;&lt;code style="color: #000000; font-family: Consolas, Monaco, Courier, monospace, fixed; background-color: #eeeeee; background-origin: initial; background-clip: initial"&gt;input type="text" x-webkit-speech lang="zh-CN"/&lt;/code&gt;&amp;gt;&lt;/p&gt;     &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;&lt;/p&gt;     &lt;/li&gt;&lt;li style="margin: 5px 0px; line-height: 1.5em"&gt;     &lt;p&gt;&lt;strong&gt;语音事件&lt;/strong&gt;&lt;/p&gt;     &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;目前已知的只有&lt;code style="color: #000000; font-family: Consolas, Monaco, Courier, monospace, fixed; background-color: #eeeeee; background-origin: initial; background-clip: initial"&gt;onwebkitspeechchange&lt;/code&gt;，顾名思义，就是语音发生变化时触发的事件，一般可以作为提交&lt;br style="margin: 10px" /&gt;     &amp;lt;&lt;code style="color: #000000; font-family: Consolas, Monaco, Courier, monospace, fixed; background-color: #eeeeee; background-origin: initial; background-clip: initial"&gt;input type="text" x-webkit-speech onwebkitspeechchange="$(this).cloest('form').submit()"/&lt;/code&gt;&amp;gt;&lt;br style="margin: 10px" /&gt;     这样说完以后就自动搜索了&lt;/p&gt;     &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;&lt;/p&gt;     &lt;/li&gt;&lt;li style="margin: 5px 0px; line-height: 1.5em"&gt;     &lt;p&gt;&lt;strong&gt;x-webkit-grammar&lt;/strong&gt;&lt;/p&gt;     &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;这个不是语音搜索用的属性，但是可以控制这个输入的语法，例如在做搜索框的话就可以用&lt;br style="margin: 10px" /&gt;     &amp;lt;&lt;code style="color: #000000; font-family: Consolas, Monaco, Courier, monospace, fixed; background-color: #eeeeee; background-origin: initial; background-clip: initial"&gt;input type="text" x-webkit-speech x-webkit-grammar="b&lt;a href="http://liumiao.me/html/list_567.html" target="_blank"&gt;UI&lt;/a&gt;ltin:search" /&lt;/code&gt;&amp;gt;&lt;/p&gt;     &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;&lt;br style="margin: 10px" /&gt;     使得语音输入的内容尽量靠近搜索内容，去除多余的字符，例如「的」&lt;/p&gt;     &lt;/li&gt;&lt;/ul&gt; &lt;p style="margin: 0px 0px 7px; line-height: 1.5em; letter-spacing: 1px"&gt;这个功能相当有趣，实用就不敢恭维了，主要是因为适用范围太小啊，所以只能希望HTML5尽快统一并应用了。&lt;/p&gt; &lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2343503.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/09/2343503.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/02/08/2342349.html</id><title type="text">Flowplayer-一款免费的WEB视频播放器</title><summary type="text">Flowplayer 是一个开源（GPL 3的）WEB视频播放器。您可以将该播放器嵌入您的网页中，如果您是开发人员，您还可以自由定制和配置播放器相关参数以达到您要的播放效果。本文主要介绍Flowplayer的使用。 Flowplayer支持播放flv、swf等流媒体以及图片文件，能够非常流畅的播放视频文件，支持自定义配置和扩展。 1、加载flowplayer.js 在要播放视频的页面的head之间加入flowplayer.js。 &amp;lt;scripttype=&amp;quot;text/javascript&amp;quot;src=&amp;quot;js/flowplayer-3.2.6.min.js&amp;quot</summary><published>2012-02-08T02:47:00Z</published><updated>2012-02-08T02:47:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/08/2342349.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/08/2342349.html"/><content type="html">&lt;div&gt;&lt;div&gt;           &lt;p&gt;Flowplayer 是一个开源（GPL 3的）WEB视频播放器。您可以将该播放器嵌入您的网页中，如果您是开发人员，您还可以自由定制和配置播放器相关参数以达到您要的播放效果。本文主要介绍Flowplayer的使用。&lt;/p&gt; &lt;p&gt;&lt;img alt="" src="http://www.helloweba.com/attachments/fck/flowplayer.jpg" height="260" width="650" /&gt;&lt;/p&gt; &lt;div&gt;&lt;a target="_blank" href="http://www.helloweba.com/demo/flowplayer/"&gt;&lt;img alt="查看演示DEMO" src="http://www.helloweba.com/images/demo_btn.jpg" /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;Flowplayer支持播放flv、swf等流媒体以及图片文件，能够非常流畅的播放视频文件，支持自定义配置和扩展。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1、加载flowplayer.js&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在要播放视频的页面的head之间加入flowplayer.js。&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;nbsp;type="text/javascript"&amp;nbsp;src="js/flowplayer-3.2.6.min.js"&amp;gt;&amp;lt;/script&amp;gt;&amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;您可以到flowplyer官网上下载最新版本：&lt;a target="_blank" href="http://flowplayer.org/download/index.html"&gt;http://flowplayer.org/download/index.html&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;2、XHTML&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在需要加入播放器的地方加入如下一段代码：&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;a&amp;nbsp;href="flowplayer.flv"&amp;nbsp;style="display:block;width:520px;height:330px"&amp;nbsp;id="player"&amp;gt;&amp;lt;/a&amp;gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;将a标签的href属性指向要播放的视频地址，然后设置样式，宽度和高度，以及设置display:block，当然关键的是还要给a标签指定一个id，以便于JS调用。&lt;/p&gt; &lt;p&gt;当然你也可以只在html中指定一个DIV，然后由Javascript来控制播放地址，如：&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;nbsp;id="player2"&amp;nbsp;style="width:520px;&amp;nbsp;height:330px"&amp;gt;&amp;nbsp;&amp;lt;/div&amp;gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;&lt;strong&gt;3、Javascript&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在页面底部计入javascript脚本调用播放器：&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;nbsp;type="text/javascript"&amp;gt;&amp;nbsp;&lt;br /&gt;flowplayer("player",&amp;nbsp;"flowplayer-3.2.7.swf");&amp;nbsp;&lt;br /&gt;&amp;lt;/script&amp;gt;&amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;使用flowplayer()函数调用播放器，第一个参数是播放器的id，第二个参数是播放器的路径，它是一个flash文件，一定要保证播放器的路径正确。&lt;/p&gt; &lt;p&gt;如果不是使用a标签调用视频文件，而是使用DIV来调用，则代码如下：&lt;/p&gt; &lt;pre&gt;&lt;code&gt;flowplayer(&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"player2",&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"flowplayer-3.2.7.swf",{&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;clip:&amp;nbsp;{&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;url:&amp;nbsp;"flowplayer.flv",&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;autoPlay:&amp;nbsp;false,&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;autoBuffering:&amp;nbsp;true&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&lt;br /&gt;);&amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;flowplayer()函数的第三个参数是可以进行多项设置的，其实就是高级设置。clip方法里的url：表示视频文件的真实地 址，autoPlay：表示是否自动播放，默认是true，autoBuffering：表示是否自动缓冲，即当视频文件设置为不自动播放时，播放器仍然 预先下载视频文件内容。&lt;/p&gt; &lt;p&gt;flowplayer还支持播放列表，以及皮肤设置等多项高级设置，具体设置方法，感兴趣的同学可以请访问：&lt;a target="_blank" href="http://flowplayer.org/documentation/configuration/index.html"&gt;http://flowplayer.org/documentation/configuration/index.html&lt;/a&gt;。&lt;/p&gt;        &lt;/div&gt;&lt;/div&gt;&lt;p&gt;【http://www.helloweba.com/demo/flowplayer/】&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;转载：&lt;div&gt;http://www.helloweba.com/view-blog-154.html&lt;/div&gt; &lt;br /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2342349.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/08/2342349.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/02/08/2342331.html</id><title type="text">jQuery+CSS实现的图片滚动效果</title><summary type="text">本例实现了一系列图片滚动的效果，适合用在需要展示图片库或合作伙伴友情链接图片之类的网站首页。可以控制图片滚动的速度、自动/手动滚动图片、支持鼠标悬停等。 XHTML &amp;lt;divclass=&amp;quot;bx_wrap&amp;quot;&amp;gt;&amp;lt;divclass=&amp;quot;bx_container&amp;quot;&amp;gt;&amp;lt;ulid=&amp;quot;demo1&amp;quot;&amp;gt;&amp;lt;li&amp;gt;&amp;lt;imgalt=&amp;quot;#&amp;quot;width=&amp;quot;140&amp;quot;height=&amp;quot;94&amp;quot;src=&amp;quot;img/s1.jpg&amp;quot;&amp;gt;&amp;lt;</summary><published>2012-02-08T02:38:00Z</published><updated>2012-02-08T02:38:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/08/2342331.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/08/2342331.html"/><content type="html">&lt;div&gt;&lt;div&gt;           &lt;p&gt;本例实现了一系列图片滚动的效果，适合用在需要展示图片库或合作伙伴友情链接图片之类的网站首页。可以控制图片滚动的速度、自动/手动滚动图片、支持鼠标悬停等。&lt;/p&gt; &lt;p&gt;&lt;img alt="" src="http://www.helloweba.com/attachments/fck/image_marquee.jpg" height="280" width="650" /&gt;&lt;/p&gt; &lt;div&gt;&lt;a target="_blank" href="http://www.helloweba.com/demo/images-marquee/"&gt;&lt;img alt="查看演示DEMO" src="http://www.helloweba.com/images/demo_btn.jpg" /&gt;&lt;/a&gt; &lt;a href="http://www.helloweba.com/demo/download/images-marquee.rar"&gt;&lt;img alt="下载源码" src="http://www.helloweba.com/images/download_btn.jpg" /&gt;&lt;/a&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;XHTML&lt;/strong&gt;&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;nbsp;class="bx_wrap"&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;div&amp;nbsp;class="bx_container"&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ul&amp;nbsp;id="demo1"&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;li&amp;gt;&amp;lt;img&amp;nbsp;&amp;nbsp;alt="#"&amp;nbsp;width="140"&amp;nbsp;height="94"&amp;nbsp;src="img/s1.jpg"&amp;gt;&amp;lt;/li&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;!--更多图片--&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/ul&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/div&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;lt;/div&amp;gt;&amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;XHTML由一组图片构成的无序列表ul，给ul一个id属性用于JS调用，另外ul的父级div.bx_container和div.bx_wrap是必需的。此外记得在head间载入jquery库和滚动所需的js插件：&lt;/p&gt; &lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;nbsp;type="text/javascript"&amp;nbsp;src="js/jquery.js"&amp;gt;&amp;nbsp;&amp;lt;/script&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;lt;script&amp;nbsp;type="text/javascript"&amp;nbsp;src="js/bxCarousel.js"&amp;gt;&amp;nbsp;&amp;lt;/script&amp;gt;&amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;&lt;strong&gt;CSS&lt;/strong&gt;&lt;/p&gt; &lt;pre&gt;&lt;code&gt;.bx_wrap&amp;nbsp;{margin-left:&amp;nbsp;10px;&amp;nbsp;margin-top:10px}&amp;nbsp;&lt;br /&gt;.bx_wrap&amp;nbsp;ul&amp;nbsp;img&amp;nbsp;{&amp;nbsp;border:&amp;nbsp;2px&amp;nbsp;solid&amp;nbsp;#ddd;&amp;nbsp;}&amp;nbsp;&lt;br /&gt;.bx_wrap&amp;nbsp;ul&amp;nbsp;li{text-align:center}&amp;nbsp;&lt;br /&gt;.bx_wrap&amp;nbsp;ul&amp;nbsp;li&amp;nbsp;a:hover{text-decoration:none;&amp;nbsp;color:#f30}&amp;nbsp;&lt;br /&gt;.bx_wrap&amp;nbsp;a.prev&amp;nbsp;{width:20px;height:24px;line-height:24px;outline-style:none;outline-width:&amp;nbsp;0;&amp;nbsp;&lt;br /&gt;position:absolute;&amp;nbsp;top:45px;&amp;nbsp;left:-2px;&amp;nbsp;text-indent:-999em;&amp;nbsp;background:&amp;nbsp;url(img/arr_left.gif)&amp;nbsp;&amp;nbsp;&lt;br /&gt;no-repeat;}&amp;nbsp;&lt;br /&gt;.bx_wrap&amp;nbsp;a.next&amp;nbsp;{width:20px;height:24px;line-height:24px;&amp;nbsp;left:626px;position:&amp;nbsp;absolute;&amp;nbsp;&lt;br /&gt;top:45px;&amp;nbsp;text-indent:-999em;&amp;nbsp;background:url(img/arr_right.gif)&amp;nbsp;no-repeat;}&amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;如果要使用方向按钮导航，则需要设置.bx_wrap a.prev和.bx_wrap a.next的样式。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;jQuery&lt;/strong&gt;&lt;/p&gt; &lt;pre&gt;&lt;code&gt;$("#demo1").bxCarousel({&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;display_num:&amp;nbsp;4,&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;move:&amp;nbsp;1,&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto:&amp;nbsp;true,&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;controls:&amp;nbsp;false,&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;margin:&amp;nbsp;10&amp;nbsp;&lt;br /&gt;});&amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;bxCarousel参数说明：&lt;/p&gt; &lt;p&gt;move：每次滚动移动图片的数量，默认为4。&lt;/p&gt; &lt;p&gt;display_num：展示图片的数量，默认为4。&lt;/p&gt; &lt;p&gt;speed：图片滚动速度，默认为500毫秒。&lt;/p&gt; &lt;p&gt;margin：图片间的间距，默认为0。&lt;/p&gt; &lt;p&gt;auto：是否自动滚动，默认为false。&lt;/p&gt; &lt;p&gt;auto_interval：当设为自动滚动时，每次滚动的时间间隔（毫秒），默认为2000毫秒即2秒。&lt;/p&gt; &lt;p&gt;auto_dir：自动滚动的方向，默认为next，你可以试下prev。&lt;/p&gt; &lt;p&gt;next_image：向右滚方向按钮图片，可以用CSS设置。&lt;/p&gt; &lt;p&gt;prev_image：向左滚方向按钮图片，可以用CSS设置。&lt;/p&gt; &lt;p&gt;auto_hover：滚动时，鼠标滑上图片时是否停止滚动，默认为false。&lt;/p&gt; &lt;p&gt;controls：是否显示左右滚动按钮图片，默认为true。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;转载：&lt;div&gt;http://www.helloweba.com/view-blog-139.html&lt;/div&gt; &lt;br /&gt;&lt;/p&gt;        &lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2342331.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/08/2342331.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/02/06/2340004.html</id><title type="text">现代浏览器的工作原理</title><summary type="text">简介 浏览器可以被认为是使用最广泛的软件，本文将介绍浏览器的工 作原理，我们将看到，从你在地址栏输入google.com到你看到google主页过程中都发生了什么。 将讨论的浏览器 今天，有五种主流浏览器——IE、Firefox、Safari、Chrome及Opera。 本文将基于一些开源浏览器的例子——Firefox、 Chrome及Safari，Safari是部分开源的。 根据W3C（World Wide Web Consortium 万维网联盟）的浏览器统计数据，当前（2011年9月），Firefox、Safari及Chrome的市场占有率综合已快接近50％。（原文为2009年10月，数</summary><published>2012-02-06T05:41:00Z</published><updated>2012-02-06T05:41:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/06/2340004.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/06/2340004.html"/><content type="html">&lt;div&gt;&lt;p&gt;&lt;strong&gt;简介&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;浏览器可以被认为是使用最广泛的软件，本文将介绍浏览器的工 作原理，我们将看到，从你在地址栏输入google.com到你看到google主页过程中都发生了什么。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;将讨论的浏览器&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;今天，有五种主流浏览器&amp;#8212;&amp;#8212;IE、Firefox、Safari、Chrome及Opera。&lt;/p&gt; &lt;p&gt;本文将基于一些开源浏览器的例子&amp;#8212;&amp;#8212;Firefox、 Chrome及Safari，Safari是部分开源的。&lt;/p&gt; &lt;p&gt;根据W3C（World Wide Web Consortium 万维网联盟）的浏览器统计数据，当前（&lt;a href="http://www.iefans.net/9yue-shichangfene-ie6-jisu-xiahua/" target="_blank" rel="nofollow"&gt;2011年9月&lt;/a&gt;），Firefox、Safari及Chrome的市场占有率综合已快接近50％。（原文为2009年10月，数据没有太大变化）因此，可以说开源浏览器将近占据了浏览器市场的半壁江山。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;浏览器的主要功能&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;浏览器的主要功能是将用户选择得web资源呈现出来，它需要从服务器请求资源，并将其显示在浏览器窗口中，资源的格式通常是HTML，也包括 PDF、image及其他格式。用户用URI（Uniform Resource Identifier  统一资源标识符）来指定所请求资源的位置，在网络一章有更多讨论。&lt;/p&gt; &lt;p&gt;HTML和CSS规范中规定了浏览器解释html文档的方式，由 W3C组织对这些规范进行维护，W3C是负责制定web标准的组织。&lt;/p&gt; &lt;p&gt;HTML规范的最新版本是HTML4(http://www.w3.org/TR/html401/)，HTML5还在制定中（译注：两年前），最 新的CSS规范版本是2（http://www.w3.org/TR/CSS2），CSS3也还正在制定中（译注：同样两年前）。&lt;/p&gt; &lt;p&gt;这些年来，浏览器厂商纷纷开发自己的扩展，对规范的遵循并不完善，这为web开发者带来了严重的兼容性问题。&lt;/p&gt; &lt;p&gt;但是，浏览器的用户界面则差不多，常见的用户界面元素包括：&lt;/p&gt; &lt;p&gt;&amp;#9650;用来输入URI的地址栏&lt;/p&gt; &lt;p&gt;&amp;#9650;前进、后退按钮&lt;/p&gt; &lt;p&gt;&amp;#9650;书签选项&lt;/p&gt; &lt;p&gt;&amp;#9650;用于刷新及暂停当前加载文档的刷新、暂停按钮&lt;/p&gt; &lt;p&gt;&amp;#9650;用于到达主页的主页按钮&lt;/p&gt; &lt;p&gt;奇怪的是，并没有哪个正式公布的规范对用户界面做出规定，这些是多年来各浏览器厂商之间相互模仿和不断改进得结果。&lt;/p&gt; &lt;p&gt;HTML5并没有规定浏览器必须具有的UI元素，但列出了一些常用元素，包括地址栏、状态栏及工具栏。还有一些浏览器有自己专有得功能，比如Firefox得下载管理。更多相关内容将在后面讨论用户界面时介绍。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;浏览器的主要构成High Level Structure&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;浏览器的主要组件包括：&lt;/p&gt; &lt;p&gt;&amp;#9650;用户界面－ 包括地址栏、后退/前进按钮、书签目录等，也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分&lt;/p&gt; &lt;p&gt;&amp;#9650;浏览器引擎－ 用来查询及操作渲染引擎的接口&lt;/p&gt; &lt;p&gt;&amp;#9650;渲染引擎－ 用来显示请求的内容，例如，如果请求内容为html，它负责解析html及css，并将解析后的结果显示出来&lt;/p&gt; &lt;p&gt;&amp;#9650;网络－ 用来完成网络调用，例如http请求，它具有平台无关的接口，可以在不同平台上工作&lt;br /&gt; UI 后端－ 用来绘制类似组合选择框及对话框等基本组件，具有不特定于某个平台的通用接口，底层使用操作系统的用户接口&lt;/p&gt; &lt;p&gt;&amp;#9650;JS解释器－ 用来解释执行JS代码&lt;/p&gt; &lt;p&gt;&amp;#9650;数据存储－ 属于持久层，浏览器需要在硬盘中保存类似cookie的各种数据，HTML5定义了web database技术，这是一种轻量级完整的客户端存储技术&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work1.png"&gt;&lt;img size-full=""  wp-image-12750"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work1.png" alt="现代浏览器的工作原理" width="500" height="339" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图1：浏览器主要组件&lt;/p&gt; &lt;p&gt;需要注意的是，不同于大部分浏览器，Chrome为每个Tab分配了各自的渲染引擎实例，每个Tab就是一个独立的进程。&lt;/p&gt; &lt;p&gt;对于构成浏览器的这些组件，后面会逐一详细讨论。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;组件间的通信 Communication between the components&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Firefox和Chrome都开发了一个特殊的通信结构，后面将有专门的一章进行讨论。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;渲染引擎 The rendering engine&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;渲染引擎的职责就是渲染，即在浏览器窗口中显示所请求的内容。&lt;/p&gt; &lt;p&gt;默认情况下，渲染引擎可以显示html、xml文档及图片，它也可以借助插件（一种浏览器扩展）显示其他类型数据，例如使用PDF阅读器插件，可以显示PDF格式，将由专门一章讲解插件及扩展，这里只讨论渲染引擎最主要的用途&amp;#8212;&amp;#8212;显示应用了CSS之后的html及图片。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;渲染引擎 Rendering engines&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;本文所讨论得浏览器&amp;#8212;&amp;#8212;Firefox、Chrome和Safari是基于两种渲染引擎构建的，Firefox使用Geoko&amp;#8212;&amp;#8212;Mozilla自主研发的渲染引擎，Safari和Chrome都使用webkit。&lt;/p&gt; &lt;p&gt;Webkit是一款开源渲染引擎，它本来是为linux平台研发的，后来由Apple移植到Mac及Windows上，相关内容请参考&lt;a href="http://webkit.org/" rel="nofollow" target="_blank"&gt;http://webkit.org&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;主流程 The main flow&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;渲染引擎首先通过网络获得所请求文档的内容，通常以8K分块的方式完成。&lt;/p&gt; &lt;p&gt;下面是渲染引擎在取得内容之后的基本流程：&lt;/p&gt; &lt;p&gt;解析html以构建dom树-&amp;gt;构建render树-&amp;gt;布局render树-&amp;gt;绘制render树&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work2.png"&gt;&lt;img size-full=""  wp-image-12751"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work2.png" alt="现代浏览器的工作原理" width="600" height="66" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图2：渲染引擎基本流程&lt;/p&gt; &lt;p&gt;渲染引擎开始解析html，并将标签转化为内容树中的dom节点。接着，它解析外部CSS文件及style标签中的样式信息。这些样式信息以及html中的可见性指令将被用来构建另一棵树&amp;#8212;&amp;#8212;render树。&lt;/p&gt; &lt;p&gt;Render树由一些包含有颜色和大小等属性的矩形组成，它们将被按照正确的顺序显示到屏幕上。&lt;/p&gt; &lt;p&gt;Render树构建好了之后，将会执行布局过程，它将确定每个节点在屏幕上的确切坐标。再下一步就是绘制，即遍历render树，并使用UI后端层绘制每个节点。&lt;/p&gt; &lt;p&gt;值得注意的是，这个过程是逐步完成的，为了更好的用户体验，渲染引擎将会尽可能早的将内容呈现到屏幕上，并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容，同时，可能还在通过网络下载其余内容。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work3.png"&gt;&lt;img size-full=""  wp-image-12752"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work3.png" alt="现代浏览器的工作原理" width="562" height="260" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图3：webkit主流程&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work4.jpg"&gt;&lt;img size-full=""  wp-image-12753"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work4.jpg" alt="现代浏览器的工作原理" width="562" height="261" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图4：Mozilla的Geoko 渲染引擎主流程&lt;/p&gt; &lt;p&gt;从图3和4中可以看出，尽管webkit和Gecko使用的术语稍有不同，他们的主要流程基本相同。Gecko称可见的格式化元素组成的树为 frame树，每个元素都是一个frame，webkit则使用render树这个名词来命名由渲染对象组成的树。Webkit中元素的定位称为布局，而 Gecko中称为回流。Webkit称利用dom节点及样式信息去构建render树的过程为attachment，Gecko在html和dom树之间 附加了一层，这层称为内容接收器，相当制造dom元素的工厂。下面将讨论流程中的各个阶段。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;解析 Parsing－general&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;既然解析是渲染引擎中一个非常重要的过程，我们将稍微深入的研究它。首先简要介绍一下解析。&lt;/p&gt; &lt;p&gt;解析一个文档即将其转换为具有一定意义的结构&amp;#8212;&amp;#8212;编码可以理解和使用的东西。解析的结果通常是表达文档结构的节点树，称为解析树或语法树。&lt;/p&gt; &lt;p&gt;例如，解析&amp;#8220;2＋3－1&amp;#8221;这个表达式，可能返回这样一棵树。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work5.png"&gt;&lt;img size-full=""  wp-image-12754"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work5.png" alt="现代浏览器的工作原理" width="400" height="155" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图5：数学表达式树节点&lt;/p&gt; &lt;p&gt;&lt;strong&gt;文法 Grammars&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;解析基于文档依据的语法规则&amp;#8212;&amp;#8212;文档的语言或格式。每种可被解析的格式必须具有由词汇及语法规则组成的特定的文法，称为上下文无关文法。人类语言不具有这一特性，因此不能被一般的解析技术所解析。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;解析器－词法分析器 Parser－Lexer combination&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;解析可以分为两个子过程&amp;#8212;&amp;#8212;语法分析及词法分析&lt;/p&gt; &lt;p&gt;词法分析就是将输入分解为符号，符号是语言的词汇表&amp;#8212;&amp;#8212;基本有效单元的集合。对于人类语言来说，它相当于我们字典中出现的所有单词。&lt;/p&gt; &lt;p&gt;语法分析指对语言应用语法规则。&lt;/p&gt; &lt;p&gt;解析器一般将工作分配给两个组件&amp;#8212;&amp;#8212;词法分析器（有时也叫分词器）负责将输入分解为合法的符号，解析器则根据语言的语法规则分析文档结构，从而构建解析树，词法分析器知道怎么跳过空白和换行之类的无关字符。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work6.png"&gt;&lt;img size-full=""  wp-image-12755"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work6.png" alt="现代浏览器的工作原理" width="101" height="300" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图6：从源文档到解析树&lt;/p&gt; &lt;p&gt;解析过程是迭代的，解析器从词法分析器处取道一个新的符号，并试着用这个符号匹配一条语法规则，  如果匹配了一条规则，这个符号对应的节点将被添加到解析树上，然后解析器请求另一个符号。如果没有匹配到规则，解析器将在内部保存该符号，并从词法分析器  取下一个符号，直到所有内部保存的符号能够匹配一项语法规则。如果最终没有找到匹配的规则，解析器将抛出一个异常，这意味着文档无效或是包含语法错误。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;转换 Translation&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;很多时候，解析树并不是最终结果。解析一般在转换中使用&amp;#8212;&amp;#8212;将输入文档转换为另一种格式。编译就是个例子，编译器在将一段源码编译为机器码的时候，先将源码解析为解析树，然后将该树转换为一个机器码文档。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work7.png"&gt;&lt;img size-full=""  wp-image-12756"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work7.png" alt="现代浏览器的工作原理" width="104" height="400" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图7：编译流程&lt;/p&gt; &lt;p&gt;&lt;strong&gt;解析实例 Parsing example&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;图5中，我们从一个数学表达式构建了一个解析树，这里定义一个简单的数学语言来看下解析过程。&lt;/p&gt; &lt;p&gt;词汇表：我们的语言包括整数、加号及减号。&lt;/p&gt; &lt;p&gt;语法：&lt;/p&gt; &lt;p&gt;1. 该语言的语法基本单元包括表达式、term及操作符&lt;/p&gt; &lt;p&gt;2. 该语言可以包括多个表达式&lt;/p&gt; &lt;p&gt;3. 一个表达式定义为两个term通过一个操作符连接&lt;/p&gt; &lt;p&gt;4. 操作符可以是加号或减号&lt;/p&gt; &lt;p&gt;5. term可以是一个整数或一个表达式&lt;/p&gt; &lt;p&gt;现在来分析一下&amp;#8220;2＋3－1&amp;#8221;这个输入&lt;/p&gt; &lt;p&gt;第一个匹配规则的子字符串是&amp;#8220;2&amp;#8221;，根据规则5，它是一个term，第二个匹配的是&amp;#8220;2＋3&amp;#8221;，它符合第2条规则&amp;#8212;&amp;#8212;一个操作符连接两个term， 下一次匹配发生在输入的结束处。&amp;#8220;2＋3－1&amp;#8221;是一个表达式，因为我们已经知道&amp;#8220;2＋3&amp;#8221;是一个term，所以我们有了一个term紧跟着一个操作符及另 一个term。&amp;#8220;2＋＋&amp;#8221;将不会匹配任何规则，因此是一个无效输入。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;词汇表及语法的定义&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;词汇表通常利用正则表达式来定义。&lt;/p&gt; &lt;p&gt;例如上面的语言可以定义为：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_761517" notranslate=""  xslt"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;INTEGER：0｜［1－9］［0－9］＊&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;PLUS：＋&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;MINUS：－&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;正如看到的，这里用正则表达式定义整数。&lt;/p&gt; &lt;p&gt;语法通常用BNF格式定义，我们的语言可以定义为：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_514406" notranslate=""  c"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;expression :＝ term operation term&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;operation := PLUS | MINUS&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;term := INTEGER | expression&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;如果一个语言的文法是上下文无关的，则它可以用正则解析器来解析。对上下文无关文法的一个直观的定义是，该文法可以用BNF来完整的表达。可查看&lt;a href="http://en.wikipedia.org/wiki/Context-free_grammar" rel="nofollow" target="_blank"&gt;http://en.wikipedia.org/wiki/Context-free_grammar&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;解析器类型 Types of parsers&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;自顶向下解析器从最高层规则开始&amp;#8212;&amp;#8212;它先识别出&amp;#8220;2＋3&amp;#8220;，将其视为一个表达式，然后识别出&amp;#8221;2＋3－1&amp;#8220;为一个表达式（识别表达式的过程中匹配了其他规则，但出发点是最高层规则）。&lt;/p&gt; &lt;p&gt;自底向上解析会扫描输入直到匹配了一条规则，然后用该规则取代匹配的输入，直到解析完所有输入。部分匹配的表达式被放置在解析堆栈中。&lt;/p&gt; &lt;table border="1" cellpadding="0" cellspacing="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td width="147"&gt; &lt;p align="center"&gt;&lt;strong&gt;Stack&lt;/strong&gt;&lt;/p&gt; &lt;/td&gt; &lt;td width="65"&gt; &lt;p align="center"&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;/p&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td width="147"&gt;&lt;br /&gt;&lt;/td&gt; &lt;td width="65"&gt;2 + 3 &amp;#8211; 1&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td width="147"&gt;term&lt;/td&gt; &lt;td width="65"&gt;+ 3 &amp;#8211; 1&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td width="147"&gt;term operation&lt;/td&gt; &lt;td width="65"&gt;3 &amp;#8211; 1&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td width="147"&gt;expression&lt;/td&gt; &lt;td width="65"&gt;- 1&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td width="147"&gt;expression operation&lt;/td&gt; &lt;td width="65"&gt;1&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td width="147"&gt;expression&lt;/td&gt; &lt;td width="65"&gt;&lt;br /&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;自底向上解析器称为shift reduce 解析器，因为输入向右移动（想象一个指针首先指向输入开始处，并向右移动），并逐渐简化为语法规则。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;自动化解析 Generating parse&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;解析器生成器这个工具可以自动生成解析器，只需要指定语言的文法&amp;#8212;&amp;#8212;词汇表及语法规则，它就可以生成一个解析器。创建一个解析器需要对解析有深入的 理解，而且手动的创建一个由较好性能的解析器并不容易，所以解析生成器很有用。Webkit使用两个知名的解析生成器&amp;#8212;&amp;#8212;用于创建语法分析器的Flex及 创建解析器的Bison（你可能接触过Lex和Yacc）。Flex的输入是一个包含了符号定义的正则表达式，Bison的输入是用BNF格式表示的语法 规则。rs automatically&lt;/p&gt; &lt;p&gt;&lt;strong&gt;HTML解析器 HTML Parser&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;HTML解析器的工作是将html标识解析为解析树。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;HTML文法定义 The HTML grammar definition&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;W3C组织制定规范定义了HTML的词汇表和语法。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;非上下文无关文法 Not a context free grammar&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;正如在解析简介中提到的，上下文无关文法的语法可以用类似BNF的格式来定义。&lt;/p&gt; &lt;p&gt;不幸的是，所有的传统解析方式都不适用于html（当然我提出它们并不只是因为好玩，它们将用来解析css和js），html不能简单的用解析所需的上下文无关文法来定义。&lt;/p&gt; &lt;p&gt;Html 有一个正式的格式定义&amp;#8212;&amp;#8212;DTD（Document Type Definition  文档类型定义）&amp;#8212;&amp;#8212;但它并不是上下文无关文法，html更接近于xml，现在有很多可用的xml解析器，html有个xml的变体&amp;#8212;&amp;#8212;xhtml，它们间 的不同在于，html更宽容，它允许忽略一些特定标签，有时可以省略开始或结束标签。总的来说，它是一种soft语法，不像xml呆板、固执。&lt;/p&gt; &lt;p&gt;显然，这个看起来很小的差异却带来了很大的不同。一方面，这是html流行的原因&amp;#8212;&amp;#8212;它的宽容使web开发人员的工作更加轻松，但另一方面，这也使很难去写一个格式化的文法。所以，html的解析并不简单，它既不能用传统的解析器解析，也不能用xml解析器解析。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;HTML DTD&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Html适用DTD格式进行定义，这一格式是用于定义SGML家族的语言，包括了对所有允许元素及它们的属性和层次关系的定义。正如前面提到的，html DTD并没有生成一种上下文无关文法。&lt;/p&gt; &lt;p&gt;DTD有一些变种，标准模式只遵守规范，而其他模式则包含了对浏览器过去所使用标签的支持，这么做是为了兼容以前内容。最新的标准DTD在&lt;a href="http://www.w3.org/TR/html4/strict.dtd" rel="nofollow" target="_blank"&gt;http://www.w3.org/TR/html4/strict.dtd&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;DOM&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;输出的树，也就是解析树，是由DOM元素及属性节点组成的。DOM是文档对象模型的缩写，它是html文档的对象表示，作为html元素的外部接口供js等调用。&lt;/p&gt; &lt;p&gt;树的根是&amp;#8220;document&amp;#8221;对象。&lt;/p&gt; &lt;p&gt;DOM和标签基本是一一对应的关系，例如，如下的标签：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_195367" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;8&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;body&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;p&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;Hello DOM&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;p&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;div&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;img&lt;/code&gt; &lt;code plain"=""&gt;src=&amp;#8221;example.png&amp;#8221; /&amp;gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;div&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;body&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;将会被转换为下面的DOM树：&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work8.png"&gt;&lt;img size-full=""  wp-image-12757"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work8.png" alt="现代浏览器的工作原理" width="400" height="219" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图8：示例标签对应的DOM树&lt;/p&gt; &lt;p&gt;和html一样，DOM的规范也是由W3C组织制定的。访问&lt;a href="http://www.w3.org/DOM/DOMTR" rel="nofollow" target="_blank"&gt;http://www.w3.org/DOM/DOMTR&lt;/a&gt;，这是使用文档的一般规范。一个模型描述一种特定的html元素，可以在&lt;a href="http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.htm" rel="nofollow" target="_blank"&gt;http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.htm&lt;/a&gt;&amp;nbsp;查看html定义。&lt;/p&gt; &lt;p&gt;这里所谓的树包含了DOM节点是说树是由实现了DOM接口的元素构建而成的，浏览器使用已被浏览器内部使用的其他属性的具体实现。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;解析算法 The parsing algorithm&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;正如前面章节中讨论的，hmtl不能被一般的自顶向下或自底向上的解析器所解析。&lt;/p&gt; &lt;p&gt;原因是：&lt;/p&gt; &lt;p&gt;1. 这门语言本身的宽容特性&lt;/p&gt; &lt;p&gt;2. 浏览器对一些常见的非法html有容错机制&lt;/p&gt; &lt;p&gt;3. 解析过程是往复的，通常源码不会在解析过程中发生改变，但在html中，脚本标签包含的&amp;#8220;document.write &amp;#8221;可能添加标签，这说明在解析过程中实际上修改了输入&lt;/p&gt; &lt;p&gt;不能使用正则解析技术，浏览器为html定制了专属的解析器。&lt;/p&gt; &lt;p&gt;Html5规范中描述了这个解析算法，算法包括两个阶段&amp;#8212;&amp;#8212;符号化及构建树。&lt;/p&gt; &lt;p&gt;符号化是词法分析的过程，将输入解析为符号，html的符号包括开始标签、结束标签、属性名及属性值。&lt;/p&gt; &lt;p&gt;符号识别器识别出符号后，将其传递给树构建器，并读取下一个字符，以识别下一个符号，这样直到处理完所有输入。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work9.png"&gt;&lt;img size-full=""  wp-image-12758"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work9.png" alt="现代浏览器的工作原理" width="308" height="400" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图9：HTML解析流程&lt;/p&gt; &lt;p&gt;&lt;strong&gt;符号识别算法 The tokenization algorithm&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;算法输出html符号，该算法用状态机表示。每次读取输入流中的一个或多个字符，并根据这些字符转移到下一个状态，当前的符号状态及构建树状态共同影响结果，这意味着，读取同样的字符，可能因为当前状态的不同，得到不同的结果以进入下一个正确的状态。&lt;/p&gt; &lt;p&gt;这个算法很复杂，这里用一个简单的例子来解释这个原理。&lt;/p&gt; &lt;p&gt;基本示例&amp;#8212;&amp;#8212;符号化下面的html：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_700426" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;body&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;Hello world&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;body&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;初始状态为&amp;#8220;Data State&amp;#8221;，当遇到&amp;#8220;&amp;lt;&amp;#8221;字符，状态变为&amp;#8220;Tag open  state&amp;#8221;，读取一个a－z的字符将产生一个开始标签符号，状态相应变为&amp;#8220;Tag name  state&amp;#8221;，一直保持这个状态直到读取到&amp;#8220;&amp;gt;&amp;#8221;，每个字符都附加到这个符号名上，例子中创建的是一个html符号。&lt;/p&gt; &lt;p&gt;当读取到&amp;#8220;&amp;gt;&amp;#8221;，当前的符号就完成了，此时，状态回到&amp;#8220;Data  state&amp;#8221;，&amp;#8220;&amp;lt;body&amp;gt;&amp;#8221;重复这一处理过程。到这里，html和body标签都识别出来了。现在，回到&amp;#8220;Data  state&amp;#8221;，读取&amp;#8220;Hello world&amp;#8221;中的字符&amp;#8220;H&amp;#8221;将创建并识别出一个字符符号，这里会为&amp;#8220;Hello  world&amp;#8221;中的每个字符生成一个字符符号。&lt;/p&gt; &lt;p&gt;这样直到遇到&amp;#8220;&amp;lt;/body&amp;gt;&amp;#8221;中的&amp;#8220;&amp;lt;&amp;#8221;。现在，又回到了&amp;#8220;Tag open  state&amp;#8221;，读取下一个字符&amp;#8220;/&amp;#8221;将创建一个闭合标签符号，并且状态转移到&amp;#8220;Tag name  state&amp;#8221;，还是保持这一状态，直到遇到&amp;#8220;&amp;gt;&amp;#8221;。然后，产生一个新的标签符号并回到&amp;#8220;Data  state&amp;#8221;。后面的&amp;#8220;&amp;lt;/html&amp;gt;&amp;#8221;将和&amp;#8220;&amp;lt;/body&amp;gt;&amp;#8221;一样处理。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work10.png"&gt;&lt;img size-full=""  wp-image-12759"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work10.png" alt="现代浏览器的工作原理" width="627" height="387" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图10：符号化示例输入&lt;/p&gt; &lt;p&gt;&lt;strong&gt;树的构建算法 Tree construction algorithm&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在树的构建阶段，将修改以Document为根的DOM树，将元素附加到树上。每个由符号识别器识别生成的节点将会被树构造器进行处理，规范中定义 了每个符号相对应的Dom元素，对应的Dom元素将会被创建。这些元素除了会被添加到Dom树上，还将被添加到开放元素堆栈中。这个堆栈用来纠正嵌套的未 匹配和未闭合标签，这个算法也是用状态机来描述，所有的状态采用插入模式。&lt;/p&gt; &lt;p&gt;来看一下示例中树的创建过程：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_419169" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;body&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;Hello world&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;body&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;构建树这一阶段的输入是符号识别阶段生成的符号序列。&lt;/p&gt; &lt;p&gt;首先是&amp;#8220;initial mode&amp;#8221;，接收到html符号后将转换为&amp;#8220;before html&amp;#8221;模式，在这个模式中对这个符号进行再处理。此时，创建了一个HTMLHtmlElement元素，并将其附加到根Document对象上。&lt;/p&gt; &lt;p&gt;状态此时变为&amp;#8220;before head&amp;#8221;，接收到body符号时，即使这里没有head符号，也将自动创建一个HTMLHeadElement元素并附加到树上。&lt;/p&gt; &lt;p&gt;现在，转到&amp;#8220;in head&amp;#8221;模式，然后是&amp;#8220;after head&amp;#8221;。到这里，body符号会被再次处理，将创建一个HTMLBodyElement并插入到树中，同时，转移到&amp;#8220;in body&amp;#8221;模式。&lt;/p&gt; &lt;p&gt;然后，接收到字符串&amp;#8220;Hello world&amp;#8221;的字符符号，第一个字符将导致创建并插入一个text节点，其他字符将附加到该节点。&lt;/p&gt; &lt;p&gt;接收到body结束符号时，转移到&amp;#8220;after body&amp;#8221;模式，接着接收到html结束符号，这个符号意味着转移到了&amp;#8220;after after body&amp;#8221;模式，当接收到文件结束符时，整个解析过程结束。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work11.gif"&gt;&lt;img size-full=""  wp-image-12760"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work11.gif" alt="现代浏览器的工作原理" width="532" height="769" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图11：示例html树的构建过程&lt;/p&gt; &lt;p&gt;&lt;strong&gt;解析结束时的处理 Action when the parsing is finished&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在这个阶段，浏览器将文档标记为可交互的，并开始解析处于延时模式中的脚本&amp;#8212;&amp;#8212;这些脚本在文档解析后执行。&lt;/p&gt; &lt;p&gt;文档状态将被设置为完成，同时触发一个load事件。&lt;/p&gt; &lt;p&gt;Html5规范中有符号化及构建树的完整算法(http://www.w3.org/TR/html5/syntax.html#html-parser)。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;浏览器容错 Browsers error tolerance&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;你从来不会在一个html页面上看到&amp;#8220;无效语法&amp;#8221;这样的错误，浏览器修复了无效内容并继续工作。&lt;/p&gt; &lt;p&gt;以下面这段html为例：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_468807" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;8&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;9&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;mytag&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;mytag&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;div&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;p&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;div&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code plain"=""&gt;Really lousy HTML&lt;/code&gt;&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;p&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;这段html违反了很多规则（mytag不是合法的标签，p及div错误的嵌套等等），但是浏览器仍然可以没有任何怨言的继续显示，它在解析的过程中修复了html作者的错误。&lt;/p&gt; &lt;p&gt;浏览器都具有错误处理的能力，但是，另人惊讶的是，这并不是html最新规范的内容，就像书签及前进后退按钮一样，它只是浏览器长期发展的结果。一些比较知名的非法html结构，在许多站点中出现过，浏览器都试着以一种和其他浏览器一致的方式去修复。&lt;/p&gt; &lt;p&gt;Html5规范定义了这方面的需求，webkit在html解析类开始部分的注释中做了很好的总结。&lt;/p&gt; &lt;p&gt;解析器将符号化的输入解析为文档并创建文档，但不幸的是，我们必须处理很多没有很好格式化的html文档，至少要小心下面几种错误情况。&lt;/p&gt; &lt;p&gt;1. 在未闭合的标签中添加明确禁止的元素。这种情况下，应该先将前一标签闭合&lt;/p&gt; &lt;p&gt;2. 不能直接添加元素。有些人在写文档的时候会忘了中间一些标签（或者中间标签是可选的），比如HTML HEAD BODY TR TD LI等&lt;/p&gt; &lt;p&gt;3. 想在一个行内元素中添加块状元素。关闭所有的行内元素，直到下一个更高的块状元素&lt;/p&gt; &lt;p&gt;4. 如果这些都不行，就闭合当前标签直到可以添加该元素。&lt;/p&gt; &lt;p&gt;下面来看一些webkit容错的例子：&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_674489" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;br&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;替代&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;一些网站使用&amp;lt;/br&amp;gt;替代&amp;lt;br&amp;gt;，为了兼容IE和Firefox，webkit将其看作&amp;lt;br&amp;gt;。&lt;/p&gt; &lt;p&gt;代码：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_352265" notranslate=""  javascript"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code keyword"=""&gt;if&lt;/code&gt; &lt;code plain"=""&gt;(t-&amp;gt;isCloseTag(brTag) &amp;amp;&amp;amp; m_document-&amp;gt;inCompatMode()) {&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;reportError(MalformedBRError);&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;t-&amp;gt;beginTag = &lt;/code&gt;&lt;code keyword"=""&gt;true&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;Note－这里的错误处理在内部进行，用户看不到。&lt;/p&gt; &lt;p&gt;迷路的表格&lt;/p&gt; &lt;p&gt;这指一个表格嵌套在另一个表格中，但不在它的某个单元格内。&lt;/p&gt; &lt;p&gt;比如下面这个例子：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_72828" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;table&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;table&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;tr&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;td&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;inner table&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;td&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;tr&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;table&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;tr&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;td&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;outer table&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;td&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;tr&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;table&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;webkit将会将嵌套的表格变为两个兄弟表格：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_635314" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;table&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;tr&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;td&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;outer table&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;td&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;tr&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;table&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;table&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;tr&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;td&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;inner table&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;td&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;tr&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;table&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;代码：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_306938" notranslate=""  javascript"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code keyword"=""&gt;if&lt;/code&gt; &lt;code plain"=""&gt;(m_inStrayTableContent &amp;amp;&amp;amp; localName == tableTag)&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;popBlock(tableTag);&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;webkit使用堆栈存放当前的元素内容，它将从外部表格的堆栈中弹出内部的表格，则它们变为了兄弟表格。&lt;/p&gt; &lt;p&gt;嵌套的表单元素&lt;/p&gt; &lt;p&gt;用户将一个表单嵌套到另一个表单中，则第二个表单将被忽略。&lt;/p&gt; &lt;p&gt;代码：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_204245" notranslate=""  javascript"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code keyword"=""&gt;if&lt;/code&gt; &lt;code plain"=""&gt;(!m_currentFormElement) {&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;m_currentFormElement = &lt;/code&gt;&lt;code keyword"=""&gt;new&lt;/code&gt; &lt;code plain"=""&gt;HTMLFormElement(formTag, m_document);&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;太深的标签继承&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.liceo.edu.mx/" rel="nofollow" target="_blank"&gt;www.liceo.edu.mx&lt;/a&gt;是一个由嵌套层次的站点的例子，最多只允许20个相同类型的标签嵌套，多出来的将被忽略。&lt;/p&gt; &lt;p&gt;代码：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_961795" notranslate=""  cpp"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;8&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code color1=""  bold"=""&gt;bool&lt;/code&gt; &lt;code plain"=""&gt;HTMLParser::allowNestedRedundantTag(&lt;/code&gt;&lt;code keyword=""  bold"=""&gt;const&lt;/code&gt; &lt;code plain"=""&gt;AtomicString&amp;amp; tagName)&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;{&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;unsigned i = 0;&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;for&lt;/code&gt; &lt;code plain"=""&gt;(HTMLStackElem* curr = m_blockStack;&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;i &amp;lt; cMaxRedundantTagDepth &amp;amp;&amp;amp; curr &amp;amp;&amp;amp; curr-&amp;gt;tagName == tagName;&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;curr = curr-&amp;gt;next, i++) { }&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;return&lt;/code&gt; &lt;code plain"=""&gt;i != cMaxRedundantTagDepth;&lt;/code&gt;&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;放错了地方的html、body闭合标签&lt;/p&gt; &lt;p&gt;又一次不言自明。&lt;/p&gt; &lt;p&gt;支持不完整的html。我们从来不闭合body，因为一些愚蠢的网页总是在还未真正结束时就闭合它。我们依赖调用end方法去执行关闭的处理。&lt;/p&gt; &lt;p&gt;代码：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_213231" notranslate=""  cpp"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;if&lt;/code&gt; &lt;code plain"=""&gt;(t-&amp;gt;tagName == htmlTag || t-&amp;gt;tagName == bodyTag )&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;return&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;所以，web开发者要小心了，除非你想成为webkit容错代码的范例，否则还是写格式良好的html吧。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;CSS解析 CSS parsing&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;还记得简介中提到的解析的概念吗，不同于html，css属于上下文无关文法，可以用前面所描述的解析器来解析。Css规范定义了css的词法及语法文法。&lt;/p&gt; &lt;p&gt;看一些例子：&lt;/p&gt; &lt;p&gt;每个符号都由正则表达式定义了词法文法（词汇表）：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_278858" notranslate=""  javascript"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;comment &lt;/code&gt;&lt;code comments"=""&gt;///*[^*]*/*+([^/*][^*]*/*+)*//&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;num [0-9]+|[0-9]*&amp;#8221;.&amp;#8221;[0-9]+&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;nonascii [/200-/377]&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;nmstart [_a-z]|{nonascii}|{escape}&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;nmchar [_a-z0-9-]|{nonascii}|{escape}&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;name {nmchar}+&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code plain"=""&gt;ident {nmstart}{nmchar}*&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&amp;#8220;ident&amp;#8221;是识别器的缩写，相当于一个class名，&amp;#8220;name&amp;#8221;是一个元素id（用&amp;#8220;＃&amp;#8221;引用）。&lt;/p&gt; &lt;p&gt;语法用BNF进行描述：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_978612" notranslate=""  cpp"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;8&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;9&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;10&lt;/div&gt;&lt;div number11="" index10=""  alt2"=""&gt;11&lt;/div&gt;&lt;div number12="" index11=""  alt1"=""&gt;12&lt;/div&gt;&lt;div number13="" index12=""  alt2"=""&gt;13&lt;/div&gt;&lt;div number14="" index13=""  alt1"=""&gt;14&lt;/div&gt;&lt;div number15="" index14=""  alt2"=""&gt;15&lt;/div&gt;&lt;div number16="" index15=""  alt1"=""&gt;16&lt;/div&gt;&lt;div number17="" index16=""  alt2"=""&gt;17&lt;/div&gt;&lt;div number18="" index17=""  alt1"=""&gt;18&lt;/div&gt;&lt;div number19="" index18=""  alt2"=""&gt;19&lt;/div&gt;&lt;div number20="" index19=""  alt1"=""&gt;20&lt;/div&gt;&lt;div number21="" index20=""  alt2"=""&gt;21&lt;/div&gt;&lt;div number22="" index21=""  alt1"=""&gt;22&lt;/div&gt;&lt;div number23="" index22=""  alt2"=""&gt;23&lt;/div&gt;&lt;div number24="" index23=""  alt1"=""&gt;24&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;ruleset&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;: selector [ &lt;/code&gt;&lt;code string"=""&gt;','&lt;/code&gt; &lt;code plain"=""&gt;S* selector ]*&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;#8216;{&amp;#8217; S* declaration [ &lt;/code&gt;&lt;code string"=""&gt;';'&lt;/code&gt; &lt;code plain"=""&gt;S* declaration ]* &amp;#8216;}&amp;#8217; S*&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;selector&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;: simple_selector [ combinator selector | S+ [ combinator selector ] ]&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;&lt;code plain"=""&gt;simple_selector&lt;/code&gt;&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;&lt;code plain"=""&gt;: element_name [ HASH | &lt;/code&gt;&lt;code keyword=""  bold"=""&gt;class&lt;/code&gt; &lt;code plain"=""&gt;| attrib | pseudo ]*&lt;/code&gt;&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;&lt;code plain"=""&gt;| [ HASH | &lt;/code&gt;&lt;code keyword=""  bold"=""&gt;class&lt;/code&gt; &lt;code plain"=""&gt;| attrib | pseudo ]+&lt;/code&gt;&lt;/div&gt;&lt;div number11="" index10=""  alt2"=""&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number12="" index11=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;class&lt;/code&gt;&lt;/div&gt;&lt;div number13="" index12=""  alt2"=""&gt;&lt;code plain"=""&gt;: &amp;#8216;.&amp;#8217; IDENT&lt;/code&gt;&lt;/div&gt;&lt;div number14="" index13=""  alt1"=""&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number15="" index14=""  alt2"=""&gt;&lt;code plain"=""&gt;element_name&lt;/code&gt;&lt;/div&gt;&lt;div number16="" index15=""  alt1"=""&gt;&lt;code plain"=""&gt;: IDENT | &amp;#8216;*&amp;#8217;&lt;/code&gt;&lt;/div&gt;&lt;div number17="" index16=""  alt2"=""&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number18="" index17=""  alt1"=""&gt;&lt;code plain"=""&gt;attrib&lt;/code&gt;&lt;/div&gt;&lt;div number19="" index18=""  alt2"=""&gt;&lt;code plain"=""&gt;: &amp;#8216;[&lt;/code&gt;&lt;code string"=""&gt;' S* IDENT S* [ [ '&lt;/code&gt;&lt;code plain"=""&gt;=' | INCLUDES | DASHMATCH ] S*&lt;/code&gt;&lt;/div&gt;&lt;div number20="" index19=""  alt1"=""&gt;&lt;code plain"=""&gt;[ IDENT | STRING ] S* ] &amp;#8216;]&amp;#8217;&lt;/code&gt;&lt;/div&gt;&lt;div number21="" index20=""  alt2"=""&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number22="" index21=""  alt1"=""&gt;&lt;code plain"=""&gt;pseudo&lt;/code&gt;&lt;/div&gt;&lt;div number23="" index22=""  alt2"=""&gt;&lt;code plain"=""&gt;: &amp;#8216;:&amp;#8217; [ IDENT | FUNCTION S* [IDENT S*] &amp;#8216;)&amp;#8217; ]&lt;/code&gt;&lt;/div&gt;&lt;div number24="" index23=""  alt1"=""&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;说明：一个规则集合有这样的结构&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_995791" notranslate=""  css"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;div.error , a.error {&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code keyword"=""&gt;color&lt;/code&gt;&lt;code plain"=""&gt;:&lt;/code&gt;&lt;code value"=""&gt;red&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code keyword"=""&gt;font-weight&lt;/code&gt;&lt;code plain"=""&gt;:&lt;/code&gt;&lt;code value"=""&gt;bold&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;div.error和a.error时选择器，大括号中的内容包含了这条规则集合中的规则，这个结构在下面的定义中正式的定义了：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_681172" notranslate=""  cpp"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;ruleset&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;: selector [ &lt;/code&gt;&lt;code string"=""&gt;','&lt;/code&gt; &lt;code plain"=""&gt;S* selector ]*&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;#8216;{&amp;#8217; S* declaration [ &lt;/code&gt;&lt;code string"=""&gt;';'&lt;/code&gt; &lt;code plain"=""&gt;S* declaration ]* &amp;#8216;}&amp;#8217; S*&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;这说明，一个规则集合具有一个或是可选个数的多个选择器，这些选择器以逗号和空格（S表示空格）进行分隔。每个规则集合包含大括号及大括号中的一条或多条以分号隔开的声明。声明和选择器在后面进行定义。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Webkit CSS 解析器 Webkit CSS parser&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Webkit使用Flex和Bison解析生成器从CSS语法文件中自动生成解析器。回忆一下解析器的介绍，Bison创建一个自底向上的解析 器，Firefox使用自顶向下解析器。它们都是将每个css文件解析为样式表对象，每个对象包含css规则，css规则对象包含选择器和声明对象，以及 其他一些符合css语法的对象。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work12.png"&gt;&lt;img size-full=""  wp-image-12762"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work12.png" alt="现代浏览器的工作原理" width="500" height="393" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图12：解析css&lt;/p&gt; &lt;p&gt;&lt;strong&gt;脚本解析 Parsing scripts&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;本章将介绍Javascript。&lt;/p&gt; &lt;p&gt;处理脚本及样式表的顺序 The order of processing scripts and style sheets&lt;/p&gt; &lt;p&gt;&lt;strong&gt;脚本&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;web的模式是同步的，开发者希望解析到一个script标签时立即解析执行脚本，并阻塞文档的解析直到脚本执行完。如果脚本是外引的，则网络必须 先请求到这个资源&amp;#8212;&amp;#8212;这个过程也是同步的，会阻塞文档的解析直到资源被请求到。这个模式保持了很多年，并且在html4及html5中都特别指定了。开发 者可以将脚本标识为defer，以使其不阻塞文档解析，并在文档解析结束后执行。Html5增加了标记脚本为异步的选项，以使脚本的解析执行使用另一个线 程。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;预解析 Speculative parsing&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Webkit和Firefox都做了这个优化，当执行脚本时，另一个线程解析剩下的文档，并加载后面需要通过网络加载的资源。这种方式可以使资源并 行加载从而使整体速度更快。需要注意的是，预解析并不改变Dom树，它将这个工作留给主解析过程，自己只解析外部资源的引用，比如外部脚本、样式表及图 片。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;样式表 Style sheets&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;样式表采用另一种不同的模式。理论上，既然样式表不改变Dom树，也就没有必要停下文档的解析等待它们，然而，存在一个问题，脚本可能在文档的解析 过程中请求样式信息，如果样式还没有加载和解析，脚本将得到错误的值，显然这将会导致很多问题，这看起来是个边缘情况，但确实很常见。Firefox在存 在样式表还在加载和解析时阻塞所有的脚本，而chrome只在当脚本试图访问某些可能被未加载的样式表所影响的特定的样式属性时才阻塞这些脚本。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;渲染树的构造 Render tree construction&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;当Dom树构建完成时，浏览器开始构建另一棵树&amp;#8212;&amp;#8212;渲染树。渲染树由元素显示序列中的可见元素组成，它是文档的可视化表示，构建这棵树是为了以正确的顺序绘制文档内容。&lt;/p&gt; &lt;p&gt;Firefox将渲染树中的元素称为frames，webkit则用renderer或渲染对象来描述这些元素。&lt;/p&gt; &lt;p&gt;一个渲染对象直到怎么布局及绘制自己及它的children。&lt;/p&gt; &lt;p&gt;RenderObject是Webkit的渲染对象基类，它的定义如下：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_920711" notranslate=""  cpp"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;8&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;class&lt;/code&gt; &lt;code plain"=""&gt;RenderObject{&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;virtual&lt;/code&gt; &lt;code keyword=""  bold"=""&gt;void&lt;/code&gt; &lt;code plain"=""&gt;layout();&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;virtual&lt;/code&gt; &lt;code keyword=""  bold"=""&gt;void&lt;/code&gt; &lt;code plain"=""&gt;paint(PaintInfo);&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;virtual&lt;/code&gt; &lt;code keyword=""  bold"=""&gt;void&lt;/code&gt; &lt;code plain"=""&gt;rect repaintRect();&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;Node* node; &lt;/code&gt;&lt;code comments"=""&gt;//the DOM node&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;RenderStyle* style; &lt;/code&gt;&lt;code comments"=""&gt;// the computed style&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code plain"=""&gt;RenderLayer* containgLayer; &lt;/code&gt;&lt;code comments"=""&gt;//the containing z-index layer&lt;/code&gt;&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;每个渲染对象用一个和该节点的css盒模型相对应的矩形区域来表示，正如css2所描述的那样，它包含诸如宽、高和位置之类的几何信息。盒模型的类 型受该节点相关的display样式属性的影响（参考样式计算章节）。下面的webkit代码说明了如何根据display属性决定某个节点创建何种类型 的渲染对象。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_834182" notranslate=""  cpp"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;8&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;9&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;10&lt;/div&gt;&lt;div number11="" index10=""  alt2"=""&gt;11&lt;/div&gt;&lt;div number12="" index11=""  alt1"=""&gt;12&lt;/div&gt;&lt;div number13="" index12=""  alt2"=""&gt;13&lt;/div&gt;&lt;div number14="" index13=""  alt1"=""&gt;14&lt;/div&gt;&lt;div number15="" index14=""  alt2"=""&gt;15&lt;/div&gt;&lt;div number16="" index15=""  alt1"=""&gt;16&lt;/div&gt;&lt;div number17="" index16=""  alt2"=""&gt;17&lt;/div&gt;&lt;div number18="" index17=""  alt1"=""&gt;18&lt;/div&gt;&lt;div number19="" index18=""  alt2"=""&gt;19&lt;/div&gt;&lt;div number20="" index19=""  alt1"=""&gt;20&lt;/div&gt;&lt;div number21="" index20=""  alt2"=""&gt;21&lt;/div&gt;&lt;div number22="" index21=""  alt1"=""&gt;22&lt;/div&gt;&lt;div number23="" index22=""  alt2"=""&gt;23&lt;/div&gt;&lt;div number24="" index23=""  alt1"=""&gt;24&lt;/div&gt;&lt;div number25="" index24=""  alt2"=""&gt;25&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;{&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;Document* doc = node-&amp;gt;document();&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;RenderArena* arena = doc-&amp;gt;renderArena();&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;#8230;&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;RenderObject* o = 0;&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;switch&lt;/code&gt; &lt;code plain"=""&gt;(style-&amp;gt;display()) {&lt;/code&gt;&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;case&lt;/code&gt; &lt;code plain"=""&gt;NONE:&lt;/code&gt;&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;break&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;case&lt;/code&gt; &lt;code plain"=""&gt;INLINE:&lt;/code&gt;&lt;/div&gt;&lt;div number11="" index10=""  alt2"=""&gt;&lt;code plain"=""&gt;o = &lt;/code&gt;&lt;code keyword=""  bold"=""&gt;new&lt;/code&gt; &lt;code plain"=""&gt;(arena) RenderInline(node);&lt;/code&gt;&lt;/div&gt;&lt;div number12="" index11=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;break&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number13="" index12=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;case&lt;/code&gt; &lt;code plain"=""&gt;BLOCK:&lt;/code&gt;&lt;/div&gt;&lt;div number14="" index13=""  alt1"=""&gt;&lt;code plain"=""&gt;o = &lt;/code&gt;&lt;code keyword=""  bold"=""&gt;new&lt;/code&gt; &lt;code plain"=""&gt;(arena) RenderBlock(node);&lt;/code&gt;&lt;/div&gt;&lt;div number15="" index14=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;break&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number16="" index15=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;case&lt;/code&gt; &lt;code plain"=""&gt;INLINE_BLOCK:&lt;/code&gt;&lt;/div&gt;&lt;div number17="" index16=""  alt2"=""&gt;&lt;code plain"=""&gt;o = &lt;/code&gt;&lt;code keyword=""  bold"=""&gt;new&lt;/code&gt; &lt;code plain"=""&gt;(arena) RenderBlock(node);&lt;/code&gt;&lt;/div&gt;&lt;div number18="" index17=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;break&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number19="" index18=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;case&lt;/code&gt; &lt;code plain"=""&gt;LIST_ITEM:&lt;/code&gt;&lt;/div&gt;&lt;div number20="" index19=""  alt1"=""&gt;&lt;code plain"=""&gt;o = &lt;/code&gt;&lt;code keyword=""  bold"=""&gt;new&lt;/code&gt; &lt;code plain"=""&gt;(arena) RenderListItem(node);&lt;/code&gt;&lt;/div&gt;&lt;div number21="" index20=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;break&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;/div&gt;&lt;div number22="" index21=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;#8230;&lt;/code&gt;&lt;/div&gt;&lt;div number23="" index22=""  alt2"=""&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;div number24="" index23=""  alt1"=""&gt;&lt;code keyword=""  bold"=""&gt;return&lt;/code&gt; &lt;code plain"=""&gt;o;&lt;/code&gt;&lt;/div&gt;&lt;div number25="" index24=""  alt2"=""&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;元素的类型也需要考虑，例如，表单控件和表格带有特殊的框架。&lt;/p&gt; &lt;p&gt;在webkit中，如果一个元素想创建一个特殊的渲染对象，它需要复写&amp;#8220;createRenderer&amp;#8221;方法，使渲染对象指向不包含几何信息的样式对象。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;渲染树和Dom树的关系 The render tree relation to the DOM tree&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;渲染对象和Dom元素相对应，但这种对应关系不是一对一的，不可见的Dom元素不会被插入渲染树，例如head元素。另外，display属性为none的元素也不会在渲染树中出现（visibility属性为hidden的元素将出现在渲染树中）。&lt;/p&gt; &lt;p&gt;还有一些Dom元素对应几个可见对象，它们一般是一些具有复杂结构的元素，无法用一个矩形来描述。例如，select元素有三个渲染对象&amp;#8212;&amp;#8212;一个显 示区域、一个下拉列表及一个按钮。同样，当文本因为宽度不够而折行时，新行将作为额外的渲染元素被添加。另一个多个渲染对象的例子是不规范的html，根 据css规范，一个行内元素只能仅包含行内元素或仅包含块状元素，在存在混合内容时，将会创建匿名的块状渲染对象包裹住行内元素。&lt;/p&gt; &lt;p&gt;一些渲染对象和所对应的Dom节点不在树上相同的位置，例如，浮动和绝对定位的元素在文本流之外，在两棵树上的位置不同，渲染树上标识出真实的结构，并用一个占位结构标识出它们原来的位置。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work13.png"&gt;&lt;img size-full=""  wp-image-12763"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work13.png" alt="现代浏览器的工作原理" width="585" height="317" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图12：渲染树及对应的Dom树&lt;/p&gt; &lt;p&gt;&lt;strong&gt;创建树的流程 The flow of constructing the tree&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Firefox中，表述为一个监听Dom更新的监听器，将frame的创建委派给Frame Constructor，这个构建器计算样式（参看样式计算）并创建一个frame。&lt;/p&gt; &lt;p&gt;Webkit中，计算样式并生成渲染对象的过程称为attachment，每个Dom节点有一个attach方法，attachment的过程是同步的，调用新节点的attach方法将节点插入到Dom树中。&lt;/p&gt; &lt;p&gt;处理html和body标签将构建渲染树的根，这个根渲染对象对应被css规范称为containing  block的元素&amp;#8212;&amp;#8212;包含了其他所有块元素的顶级块元素。它的大小就是viewport&amp;#8212;&amp;#8212;浏览器窗口的显示区域，Firefox称它为 viewPortFrame，webkit称为RenderView，这个就是文档所指向的渲染对象，树中其他的部分都将作为一个插入的Dom节点被创 建。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;样式计算 Style Computation&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;创建渲染树需要计算出每个渲染对象的可视属性，这可以通过计算每个元素的样式属性得到。&lt;/p&gt; &lt;p&gt;样式包括各种来源的样式表，行内样式元素及html中的可视化属性（例如bgcolor），可视化属性转化为css样式属性。&lt;/p&gt; &lt;p&gt;样式表来源于浏览器默认样式表，及页面作者和用户提供的样式表&amp;#8212;&amp;#8212;有些样式是浏览器用户提供的（浏览器允许用户定义喜欢的样式，例如，在Firefox中，可以通过在Firefox Profile目录下放置样式表实现）。&lt;/p&gt; &lt;p&gt;计算样式的一些困难：&lt;/p&gt; &lt;p&gt;1. 样式数据是非常大的结构，保存大量的样式属性会带来内存问题&lt;/p&gt; &lt;p&gt;2. 如果不进行优化，找到每个元素匹配的规则会导致性能问题，为每个元素查找匹配的规则都需要遍历整个规则表，这个过程有很大的工作量。选择符可能有复杂的结构，匹配过程如果沿着一条开始看似正确，后来却被证明是无用的路径，则必须去尝试另一条路径。&lt;/p&gt; &lt;p&gt;例如，下面这个复杂选择符&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_296183" notranslate=""  css"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;div div div div｛&amp;#8230;｝&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;这意味着规则应用到三个div的后代div元素，选择树上一条特定的路径去检查，这可能需要遍历节点树，最后却发现它只是两个div的后代，并不使用该规则，然后则需要沿着另一条路径去尝试&lt;/p&gt; &lt;p&gt;3. 应用规则涉及非常复杂的级联，它们定义了规则的层次&lt;/p&gt; &lt;p&gt;我们来看一下浏览器如何处理这些问题：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;共享样式数据&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;webkit节点引用样式对象（渲染样式），某些情况下，这些对象可以被节点间共享，这些节点需要是兄弟或是表兄弟节点，并且：&lt;/p&gt; &lt;p&gt;&amp;#9650;这些元素必须处于相同的鼠标状态（比如不能一个处于hover，而另一个不是）&lt;/p&gt; &lt;p&gt;&amp;#9650;不能有元素具有id&lt;/p&gt; &lt;p&gt;&amp;#9650;标签名必须匹配&lt;/p&gt; &lt;p&gt;&amp;#9650;class属性必须匹配&lt;/p&gt; &lt;p&gt;&amp;#9650;对应的属性必须相同&lt;/p&gt; &lt;p&gt;&amp;#9650;链接状态必须匹配&lt;/p&gt; &lt;p&gt;&amp;#9650;焦点状态必须匹配&lt;/p&gt; &lt;p&gt;&amp;#9650;不能有元素被属性选择器影响&lt;/p&gt; &lt;p&gt;&amp;#9650;元素不能有行内样式属性&lt;/p&gt; &lt;p&gt;&amp;#9650;不能有生效的兄弟选择器，webcore在任何兄弟选择器相遇时只是简单的抛出一个全局转换，并且在它们显示时使整个文档的样式共享失效，这些包括＋选择器和类似:first-child和:last-child这样的选择器。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Firefox规则树 Firefox rule tree&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Firefox用两个树用来简化样式计算－规则树和样式上下文树，webkit也有样式对象，但它们并没有存储在类似样式上下文树这样的树中，只是由Dom节点指向其相关的样式。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work14.png"&gt;&lt;img size-full=""  wp-image-12764"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work14.png" alt="现代浏览器的工作原理" width="640" height="407" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图14：Firefox样式上下文树&lt;/p&gt; &lt;p&gt;样式上下文包含最终值，这些值是通过以正确顺序应用所有匹配的规则，并将它们由逻辑值转换为具体的值，例如，如果逻辑值为屏幕的百分比，则通过计算将其转化为绝对单位。样式树的使用确实很巧妙，它使得在节点中共享的这些值不需要被多次计算，同时也节省了存储空间。&lt;/p&gt; &lt;p&gt;所有匹配的规则都存储在规则树中，一条路径中的底层节点拥有最高的优先级，这棵树包含了所找到的  所有规则匹配的路径（译注：可以取巧理解为每条路径对应一个节点，路径上包含了该节点所匹配的所有规则）。规则树并不是一开始就为所有节点进行计算，而是  在某个节点需要计算样式时，才进行相应的计算并将计算后的路径添加到树中。&lt;/p&gt; &lt;p&gt;我们将树上的路径看成辞典中的单词，假如已经计算出了如下的规则树：&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work15.png"&gt;&lt;img size-full=""  wp-image-12765"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work15.png" alt="现代浏览器的工作原理" width="400" height="261" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;假如需要为内容树中的另一个节点匹配规则，现在知道匹配的规则（以正确的顺序）为B-E-I，因为我们已经计算出了路径A-B-E-I-L，所以树上已经存在了这条路径，剩下的工作就很少了。&lt;/p&gt; &lt;p&gt;现在来看一下树如何保存。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;结构化&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;样式上下文按结构划分，这些结构包括类似border或color这样的特定分类的样式信息。一个结构中的所有特性不是继承的就是非继承的，对继承 的特性，除非元素自身有定义，否则就从它的parent继承。非继承的特性（称为reset特性）如果没有定义，则使用默认的值。&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;/p&gt; &lt;p&gt;如果最终没有找到该结构的任何规则定义，那么如果这个结构是继承型的，则找到其在内容树中的parent的结构，这种情况下，我们也成功的共享了结构；如果这个结构是reset型的，则使用默认的值。&lt;/p&gt; &lt;p&gt;如果特定的节点添加了值，那么需要做一些额外的计算以将其转换为实际值，然后在树上的节点缓存该值，使它的children可以使用。&lt;/p&gt; &lt;p&gt;当一个元素和它的一个兄弟元素指向同一个树节点时，完整的样式上下文可以被它们共享。&lt;/p&gt; &lt;p&gt;来看一个例子：假设有下面这段html&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_99469" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;8&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;9&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;10&lt;/div&gt;&lt;div number11="" index10=""  alt2"=""&gt;11&lt;/div&gt;&lt;div number12="" index11=""  alt1"=""&gt;12&lt;/div&gt;&lt;div number13="" index12=""  alt2"=""&gt;13&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;body&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;div&lt;/code&gt; &lt;code plain"=""&gt;class=&amp;#8221;err&amp;#8221; id=&amp;#8221;div1&amp;#8243;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;p&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;this is a&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;span&lt;/code&gt; &lt;code plain"=""&gt;class=&amp;#8221;big&amp;#8221;&amp;gt; big error &amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;span&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;this is also a&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;span&lt;/code&gt; &lt;code plain"=""&gt;class=&amp;#8221;big&amp;#8221;&amp;gt; very big error&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;span&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;&lt;code plain"=""&gt;error&lt;/code&gt;&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;p&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;div&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number11="" index10=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;div&lt;/code&gt; &lt;code plain"=""&gt;class=&amp;#8221;err&amp;#8221; id=&amp;#8221;div2&amp;#8243;&amp;gt;another error&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;div&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number12="" index11=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;body&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number13="" index12=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;html&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;以及下面这些规则&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_57360" notranslate=""  css"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code value"=""&gt;1&lt;/code&gt;&lt;code plain"=""&gt;. div {&lt;/code&gt;&lt;code keyword"=""&gt;margin&lt;/code&gt;&lt;code plain"=""&gt;:&lt;/code&gt;&lt;code value"=""&gt;5px&lt;/code&gt;&lt;code plain"=""&gt;;&lt;/code&gt;&lt;code keyword"=""&gt;color&lt;/code&gt;&lt;code plain"=""&gt;:&lt;/code&gt;&lt;code value"=""&gt;black&lt;/code&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code value"=""&gt;2&lt;/code&gt;&lt;code plain"=""&gt;. .err {&lt;/code&gt;&lt;code keyword"=""&gt;color&lt;/code&gt;&lt;code plain"=""&gt;:&lt;/code&gt;&lt;code value"=""&gt;red&lt;/code&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code value"=""&gt;3&lt;/code&gt;&lt;code plain"=""&gt;. .big {&lt;/code&gt;&lt;code keyword"=""&gt;margin-top&lt;/code&gt;&lt;code plain"=""&gt;:&lt;/code&gt;&lt;code value"=""&gt;3px&lt;/code&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code value"=""&gt;4&lt;/code&gt;&lt;code plain"=""&gt;. div span {&lt;/code&gt;&lt;code keyword"=""&gt;margin-bottom&lt;/code&gt;&lt;code plain"=""&gt;:&lt;/code&gt;&lt;code value"=""&gt;4px&lt;/code&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code value"=""&gt;5&lt;/code&gt;&lt;code plain"=""&gt;. #div&lt;/code&gt;&lt;code value"=""&gt;1&lt;/code&gt; &lt;code plain"=""&gt;{&lt;/code&gt;&lt;code keyword"=""&gt;color&lt;/code&gt;&lt;code plain"=""&gt;:&lt;/code&gt;&lt;code value"=""&gt;blue&lt;/code&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code value"=""&gt;6&lt;/code&gt;&lt;code plain"=""&gt;. #div&lt;/code&gt;&lt;code value"=""&gt;2&lt;/code&gt; &lt;code plain"=""&gt;{&lt;/code&gt;&lt;code keyword"=""&gt;color&lt;/code&gt;&lt;code plain"=""&gt;:&lt;/code&gt;&lt;code value"=""&gt;green&lt;/code&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;简化下问题，我们只填充两个结构&amp;#8212;&amp;#8212;color和margin，color结构只包含一个成员－颜色，margin结构包含四边。&lt;/p&gt; &lt;p&gt;生成的规则树如下（节点名：指向的规则）&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work16.png"&gt;&lt;img size-full=""  wp-image-12766"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work16.png" alt="现代浏览器的工作原理" width="500" height="294" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;上下文树如下（节点名：指向的规则节点）&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work17.png"&gt;&lt;img size-full=""  wp-image-12767"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work17.png" alt="现代浏览器的工作原理" width="400" height="305" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;假设我们解析html，遇到第二个div标签，我们需要为这个节点创建样式上下文，并填充它的样式结构。&lt;/p&gt; &lt;p&gt;我们进行规则匹配，找到这个div匹配的规则为1、2、6，我们发现规则树上已经存在了一条我们可以使用的路径1、2，我们只需为规则6新增一个节点添加到下面（就是规则树中的F）。&lt;/p&gt; &lt;p&gt;然后创建一个样式上下文并将其放到上下文树中，新的样式上下文将指向规则树中的节点F。&lt;/p&gt; &lt;p&gt;现在我们需要填充这个样式上下文，先从填充margin结构开始，既然最后一个规则节点没有添加margin结构，沿着路径向上，直到找到缓存的前 面插入节点计算出的结构，我们发现B是最近的指定margin值的节点。因为已经有了color结构的定义，所以不能使用缓存的结构，既然color只有 一个属性，也就不需要沿着路径向上填充其他属性。计算出最终值（将字符串转换为RGB等），并缓存计算后的结构。&lt;/p&gt; &lt;p&gt;第二个span元素更简单，进行规则匹配后发现它指向规则G，和前一个span一样，既然有兄弟节点指向同一个节点，就可以共享完整的样式上下文，只需指向前一个span的上下文。&lt;/p&gt; &lt;p&gt;因为结构中包含继承自parent的规则，上下文树做了缓存（color特性是继承来的，但Firefox将其视为reset并在规则树中缓存）。&lt;/p&gt; &lt;p&gt;例如，如果我们为一个paragraph的文字添加规则：&lt;/p&gt; &lt;p&gt;p {font-family:Verdana;font size:10px;font-weight:bold}&lt;/p&gt; &lt;p&gt;那么这个p在内容树中的子节点div，会共享和它parent一样的font结构，这种情况发生在没有为这个div指定font规则时。&lt;/p&gt; &lt;p&gt;Webkit中，并没有规则树，匹配的声明会被遍历四次，先是应用非important的高优先级属性（之所以先应用这些属性，是因为其他的依赖于 它们－比如display），其次是高优先级important的，接着是一般优先级非important的，最后是一般优先级important的规 则。这样，出现多次的属性将被按照正确的级联顺序进行处理，最后一个生效。&lt;/p&gt; &lt;p&gt;总结一下，共享样式对象（结构中完整或部分内容）解决了问题1和3，Firefox的规则树帮助以正确的顺序应用规则。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;对规则进行处理以简化匹配过程&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;样式规则有几个来源：&lt;/p&gt; &lt;p&gt;&amp;#183; 外部样式表或style标签内的css规则&lt;/p&gt; &lt;p&gt;&amp;#183; 行内样式属性&lt;/p&gt; &lt;p&gt;&amp;#183; html可视化属性（映射为相应的样式规则）&lt;/p&gt; &lt;p&gt;后面两个很容易匹配到元素，因为它们所拥有的样式属性和html属性可以将元素作为key进行映射。&lt;/p&gt; &lt;p&gt;就像前面问题2所提到的，css的规则匹配可能很狡猾，为了解决这个问题，可以先对规则进行处理，以使其更容易被访问。&lt;/p&gt; &lt;p&gt;解析完样式表之后，规则会根据选择符添加一些hash映射，映射可以是根据id、class、标签名或是任何不属于这些分类的综合映射。如果选择符为id，规则将被添加到id映射，如果是class，则被添加到class映射，等等。&lt;/p&gt; &lt;p&gt;这个处理是匹配规则更容易，不需要查看每个声明，我们能从映射中找到一个元素的相关规则，这个优化使在进行规则匹配时减少了95＋％的工作量。&lt;/p&gt; &lt;p&gt;来看下面的样式规则：&lt;/p&gt; &lt;p&gt;p.error {color:red}&lt;/p&gt; &lt;p&gt;#messageDiv {height:50px}&lt;/p&gt; &lt;p&gt;div {margin:5px}&lt;/p&gt; &lt;p&gt;第一条规则将被插入class映射，第二条插入id映射，第三条是标签映射。&lt;/p&gt; &lt;p&gt;下面这个html片段：&lt;/p&gt; &lt;p&gt;&amp;lt;p class=&amp;#8221;error&amp;#8221;&amp;gt;an error occurred &amp;lt;/p&amp;gt;&lt;/p&gt; &lt;p&gt;&amp;lt;div id=&amp;#8221; messageDiv&amp;#8221;&amp;gt;this is a message&amp;lt;/div&amp;gt;&lt;/p&gt; &lt;p&gt;我们首先找到p元素对应的规则，class映射将包含一个&amp;#8220;error&amp;#8221;的key，找到p.error的规则，div在id映射和标签映射中都有相关的规则，剩下的工作就是找出这些由key对应的规则中哪些确实是正确匹配的。&lt;/p&gt; &lt;p&gt;例如，如果div的规则是&lt;/p&gt; &lt;p&gt;table div {margin:5px}&lt;/p&gt; &lt;p&gt;这也是标签映射产生的，因为key是最右边的选择符，但它并不匹配这里的div元素，因为这里的div没有table祖先。&lt;/p&gt; &lt;p&gt;Webkit和Firefox都会做这个处理。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;以正确的级联顺序应用规则&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;样式对象拥有对应所有可见属性的属性，如果特性没有被任何匹配的规则所定义，那么一些特性可以从parent的样式对象中继承，另外一些使用默认值。&lt;/p&gt; &lt;p&gt;这个问题的产生是因为存在不止一处的定义，这里用级联顺序解决这个问题。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;样式表的级联顺序&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;一个样式属性的声明可能在几个样式表中出现，或是在一个样式表中出现多次，因此，应用规则的顺序至关重要，这个顺序就是级联顺序。根据css2的规范，级联顺序为（从低到高）：&lt;/p&gt; &lt;p&gt;1. 浏览器声明&lt;/p&gt; &lt;p&gt;2. 用户声明&lt;/p&gt; &lt;p&gt;3. 作者的一般声明&lt;/p&gt; &lt;p&gt;4. 作者的important声明&lt;/p&gt; &lt;p&gt;5. 用户important声明&lt;/p&gt; &lt;p&gt;浏览器声明是最不重要的，用户只有在声明被标记为important时才会覆盖作者的声明。具有同等级别的声明将根据specifity以及它们被定义时的顺序进行排序。Html可视化属性将被转换为匹配的css声明，它们被视为最低优先级的作者规则。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Specifity&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Css2规范中定义的选择符specifity如下：&lt;/p&gt; &lt;p&gt;&amp;#183; 如果声明来自style属性，而不是一个选择器的规则，则计1，否则计0（＝a）&lt;/p&gt; &lt;p&gt;&amp;#183; 计算选择器中id属性的数量（＝b）&lt;/p&gt; &lt;p&gt;&amp;#183; 计算选择器中class及伪类的数量（＝c）&lt;/p&gt; &lt;p&gt;&amp;#183; 计算选择器中元素名及伪元素的数量（＝d）&lt;/p&gt; &lt;p&gt;连接a－b－c－d四个数量（用一个大基数的计算系统）将得到specifity。这里使用的基数由分类中最高的基数定义。例如，如果a为14，可 以使用16进制。不同情况下，a为17时，则需要使用阿拉伯数字17作为基数，这种情况可能在这个选择符时发生html body div div  &amp;#8230;（选择符中有17个标签，一般不太可能）。&lt;/p&gt; &lt;p&gt;一些例子：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_268745" notranslate=""  cpp"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;8&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;9&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;10&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;* {} &lt;/code&gt;&lt;code comments"=""&gt;/* a=0 b=0 c=0 d=0 -&amp;gt; specificity = 0,0,0,0 */&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;li {} &lt;/code&gt;&lt;code comments"=""&gt;/* a=0 b=0 c=0 d=1 -&amp;gt; specificity = 0,0,0,1 */&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;li:first-line {} &lt;/code&gt;&lt;code comments"=""&gt;/* a=0 b=0 c=0 d=2 -&amp;gt; specificity = 0,0,0,2 */&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;ul li {} &lt;/code&gt;&lt;code comments"=""&gt;/* a=0 b=0 c=0 d=2 -&amp;gt; specificity = 0,0,0,2 */&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;ul ol+li {} &lt;/code&gt;&lt;code comments"=""&gt;/* a=0 b=0 c=0 d=3 -&amp;gt; specificity = 0,0,0,3 */&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;h1 + *[rel=up]{} &lt;/code&gt;&lt;code comments"=""&gt;/* a=0 b=0 c=1 d=1 -&amp;gt; specificity = 0,0,1,1 */&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code plain"=""&gt;ul ol li.red {} &lt;/code&gt;&lt;code comments"=""&gt;/* a=0 b=0 c=1 d=3 -&amp;gt; specificity = 0,0,1,3 */&lt;/code&gt;&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;&lt;code plain"=""&gt;li.red.level {} &lt;/code&gt;&lt;code comments"=""&gt;/* a=0 b=0 c=2 d=1 -&amp;gt; specificity = 0,0,2,1 */&lt;/code&gt;&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;&lt;code preprocessor"=""&gt;#x34y {} /* a=0 b=1 c=0 d=0 -&amp;gt; specificity = 0,1,0,0 */&lt;/code&gt;&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;&lt;code plain"=""&gt;style=&amp;#8221;" &lt;/code&gt;&lt;code comments"=""&gt;/* a=1 b=0 c=0 d=0 -&amp;gt; specificity = 1,0,0,0 */&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;规则排序&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;规则匹配后，需要根据级联顺序对规则进行排序，webkit先将小列表用冒泡排序，再将它们合并为一个大列表，webkit通过为规则复写&amp;#8220;&amp;gt;&amp;#8221;操作来执行排序：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_538187" notranslate=""  cpp"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;static&lt;/code&gt; &lt;code color1=""  bold"=""&gt;bool&lt;/code&gt; &lt;code plain"=""&gt;operator &amp;gt;(CSSRuleData&amp;amp; r1, CSSRuleData&amp;amp; r2)&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;{&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code color1=""  bold"=""&gt;int&lt;/code&gt; &lt;code plain"=""&gt;spec1 = r1.selector()-&amp;gt;specificity();&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code color1=""  bold"=""&gt;int&lt;/code&gt; &lt;code plain"=""&gt;spec2 = r2.selector()-&amp;gt;specificity();&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;return&lt;/code&gt; &lt;code plain"=""&gt;(spec1 == spec2) : r1.position() &amp;gt; r2.position() : spec1 &amp;gt; spec2;&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;逐步处理 Gradual process&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;webkit使用一个标志位标识所有顶层样式表都已加载，如果在attch时样式没有完全加载，则放置占位符，并在文档中标记，一旦样式表完成加载就重新进行计算。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;布局 Layout&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;当渲染对象被创建并添加到树中，它们并没有位置和大小，计算这些值的过程称为layout或reflow。&lt;/p&gt; &lt;p&gt;Html使用基于流的布局模型，意味着大部分时间，可以以单一的途径进行几何计算。流中靠后的元素并不会影响前面元素的几何特性，所以布局可以在文档中从右向左、自上而下的进行。也存在一些例外，比如html tables。&lt;/p&gt; &lt;p&gt;坐标系统相对于根frame，使用top和left坐标。&lt;/p&gt; &lt;p&gt;布局是一个递归的过程，由根渲染对象开始，它对应html文档元素，布局继续递归的通过一些或所有的frame层级，为每个需要几何信息的渲染对象进行计算。&lt;/p&gt; &lt;p&gt;根渲染对象的位置是0,0，它的大小是viewport－浏览器窗口的可见部分。&lt;/p&gt; &lt;p&gt;所有的渲染对象都有一个layout或reflow方法，每个渲染对象调用需要布局的children的layout方法。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Dirty bit 系统&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;为了不因为每个小变化都全部重新布局，浏览器使用一个dirty  bit系统，一个渲染对象发生了变化或是被添加了，就标记它及它的children为dirty－需要layout。存在两个标识－dirty及 children are dirty，children are  dirty说明即使这个渲染对象可能没问题，但它至少有一个child需要layout。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;全局和增量 layout&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;当layout在整棵渲染树触发时，称为全局layout，这可能在下面这些情况下发生：&lt;/p&gt; &lt;p&gt;1. 一个全局的样式改变影响所有的渲染对象，比如字号的改变&lt;/p&gt; &lt;p&gt;2. 窗口resize&lt;/p&gt; &lt;p&gt;layout也可以是增量的，这样只有标志为dirty的渲染对象会重新布局（也将导致一些额外的布局）。增量 layout会在渲染对象dirty时异步触发，例如，当网络接收到新的内容并添加到Dom树后，新的渲染对象会添加到渲染树中。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work18.png"&gt;&lt;img size-full=""  wp-image-12768"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work18.png" alt="现代浏览器的工作原理" width="326" height="341" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图20：增量 layout&lt;/p&gt; &lt;p&gt;&lt;strong&gt;异步和同步layout&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;增量layout的过程是异步的，Firefox为增量layout生成了reflow队列，以及一个调度执行这些批处理命令。Webkit也有一个计时器用来执行增量layout－遍历树，为dirty状态的渲染对象重新布局。&lt;/p&gt; &lt;p&gt;另外，当脚本请求样式信息时，例如&amp;#8220;offsetHeight&amp;#8221;，会同步的触发增量布局。&lt;/p&gt; &lt;p&gt;全局的layout一般都是同步触发。&lt;/p&gt; &lt;p&gt;有些时候，layout会被作为一个初始layout之后的回调，比如滑动条的滑动。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;优化&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;当一个layout因为resize或是渲染位置改变（并不是大小改变）而触发时，渲染对象的大小将会从缓存中读取，而不会重新计算。&lt;/p&gt; &lt;p&gt;一般情况下，如果只有子树发生改变，则layout并不从根开始。这种情况发生在，变化发生在元素自身并且不影响它周围元素，例如，将文本插入文本域（否则，每次击键都将触发从根开始的重排）。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;layout过程&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;layout一般有下面这几个部分：&lt;/p&gt; &lt;p&gt;1. parent渲染对象决定它的宽度&lt;/p&gt; &lt;p&gt;2. parent渲染对象读取chilidren，并：&lt;/p&gt; &lt;p&gt;1. 放置child渲染对象（设置它的x和y）&lt;/p&gt; &lt;p&gt;2. 在需要时（它们当前为dirty或是处于全局layout或者其他原因）调用child渲染对象的layout，这将计算child的高度&lt;/p&gt; &lt;p&gt;3. parent渲染对象使用child渲染对象的累积高度，以及margin和padding的高度来设置自己的高度－这将被parent渲染对象的parent使用&lt;/p&gt; &lt;p&gt;4. 将dirty标识设置为false&lt;/p&gt; &lt;p&gt;Firefox使用一个&amp;#8220;state&amp;#8221;对象（nsHTMLReflowState）做为参数去布局（firefox称为reflow），state包含parent的宽度及其他内容。&lt;/p&gt; &lt;p&gt;Firefox布局的输出是一个&amp;#8220;metrics&amp;#8221;对象（nsHTMLReflowMetrics）。它包括渲染对象计算出的高度。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;宽度计算&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;渲染对象的宽度使用容器的宽度、渲染对象样式中的宽度及margin、border进行计算。例如，下面这个div的宽度：&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_15626" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;div&lt;/code&gt; &lt;code plain"=""&gt;style=&amp;#8221;width:30%&amp;#8221;/&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;webkit中宽度的计算过程是（RenderBox类的calcWidth方法）：&lt;/p&gt; &lt;p&gt;&amp;#183;  容器的宽度是容器的可用宽度和0中的最大值，这里的可用宽度 为：contentWidth=clientWidth()-paddingLeft()-paddingRight()，clientWidth和 clientHeight代表一个对象内部的不包括border和滑动条的大小&lt;/p&gt; &lt;p&gt;&amp;#183; 元素的宽度指样式属性width的值，它可以通过计算容器的百分比得到一个绝对值&lt;/p&gt; &lt;p&gt;&amp;#183; 加上水平方向上的border和padding&lt;/p&gt; &lt;p&gt;到这里是最佳宽度的计算过程，现在计算宽度的最大值和最小值，如果最佳宽度大于最大宽度则使用最大宽度，如果小于最小宽度则使用最小宽度。最后缓存这个值，当需要layout但宽度未改变时使用。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Line breaking&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;当一个渲染对象在布局过程中需要折行时，则暂停并告诉它的parent它需要折行，parent将创建额外的渲染对象并调用它们的layout。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;绘制 Painting&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;绘制阶段，遍历渲染树并调用渲染对象的paint方法将它们的内容显示在屏幕上，绘制使用UI基础组件，这在UI的章节有更多的介绍。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;全局和增量&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;和布局一样，绘制也可以是全局的－绘制完整的树－或增量的。在增量的绘制过程中，一些渲染对象以不影响整棵树的方式改变，改变的渲染对象使其在屏幕 上的矩形区域失效，这将导致操作系统将其看作dirty区域，并产生一个paint事件，操作系统很巧妙的处理这个过程，并将多个区域合并为一个。 Chrome中，这个过程更复杂些，因为渲染对象在不同的进程中，而不是在主进程中。Chrome在一定程度上模拟操作系统的行为，表现为监听事件并派发 消息给渲染根，在树中查找到相关的渲染对象，重绘这个对象（往往还包括它的children）。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;绘制顺序&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;css2定义了绘制过程的顺序－&lt;a href="http://www.w3.org/TR/CSS21/zindex.html" rel="nofollow" target="_blank"&gt;http://www.w3.org/TR/CSS21/zindex.html&lt;/a&gt;。这个就是元素压入堆栈的顺序，这个顺序影响着绘制，堆栈从后向前进行绘制。&lt;/p&gt; &lt;p&gt;一个块渲染对象的堆栈顺序是：&lt;/p&gt; &lt;p&gt;1. 背景色&lt;/p&gt; &lt;p&gt;2. 背景图&lt;/p&gt; &lt;p&gt;3. border&lt;/p&gt; &lt;p&gt;4. children&lt;/p&gt; &lt;p&gt;5. outline&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Firefox显示列表&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Firefox读取渲染树并为绘制的矩形创建一个显示列表，该列表以正确的绘制顺序包含这个矩形相关的渲染对象。&lt;/p&gt; &lt;p&gt;用这样的方法，可以使重绘时只需查找一次树，而不需要多次查找&amp;#8212;&amp;#8212;绘制所有的背景、所有的图片、所有的border等等。&lt;/p&gt; &lt;p&gt;Firefox优化了这个过程，它不添加会被隐藏的元素，比如元素完全在其他不透明元素下面。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Webkit矩形存储&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;重绘前，webkit将旧的矩形保存为位图，然后只绘制新旧矩形的差集。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;动态变化&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;浏览器总是试着以最小的动作响应一个变化，所以一个元素颜色的变化将只导致该元素的重绘，元素位置的变化将大致元素的布局和重绘，添加一个Dom节 点，也会大致这个元素的布局和重绘。一些主要的变化，比如增加html元素的字号，将会导致缓存失效，从而引起整数的布局和重绘。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;渲染引擎的线程&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;渲染引擎是单线程的，除了网络操作以外，几乎所有的事情都在单一的线程中处理，在Firefox和Safari中，这是浏览器的主线程，Chrome中这是tab的主线程。&lt;/p&gt; &lt;p&gt;网络操作由几个并行线程执行，并行连接的个数是受限的（通常是2－6个）。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;事件循环&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;浏览器主线程是一个事件循环，它被设计为无限循环以保持执行过程的可用，等待事件（例如layout和paint事件）并执行它们。下面是Firefox的主要事件循环代码。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_479389" notranslate=""  cpp"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code keyword=""  bold"=""&gt;while&lt;/code&gt; &lt;code plain"=""&gt;(!mExiting)&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;NS_ProcessNextEvent(&lt;/code&gt;&lt;code keyword=""  bold"=""&gt;thread&lt;/code&gt;&lt;code plain"=""&gt;);&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;CSS2 可视模型 CSS2 visual module&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;画布 The Canvas&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;根据CSS2规范，术语canvas用来描述格式化的结构所渲染的空间&amp;#8212;&amp;#8212;浏览器绘制内容的地方。画布对每个维度空间都是无限大的，但浏览器基于viewport的大小选择了一个初始宽度。&lt;/p&gt; &lt;p&gt;根据&lt;a href="http://www.w3.org/TR/CSS2/zindex.html" rel="nofollow" target="_blank"&gt;http://www.w3.org/TR/CSS2/zindex.html&lt;/a&gt;的定义，画布如果是包含在其他画布内则是透明的，否则浏览器会指定一个颜色。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;CSS盒模型&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;CSS盒模型描述了矩形盒，这些矩形盒是为文档树中的元素生成的，并根据可视的格式化模型进行布局。每个box包括内容区域（如图片、文本等）及可选的四周padding、border和margin区域。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work19.jpg"&gt;&lt;img size-full=""  wp-image-12769"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work19.jpg" alt="现代浏览器的工作原理" width="509" height="348" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;每个节点生成0－n个这样的box。&lt;/p&gt; &lt;p&gt;所有的元素都有一个display属性，用来决定它们生成box的类型，例如：&lt;/p&gt; &lt;p&gt;block－生成块状box&lt;/p&gt; &lt;p&gt;inline－生成一个或多个行内box&lt;/p&gt; &lt;p&gt;none－不生成box&lt;/p&gt; &lt;p&gt;默认的是inline，但浏览器样式表设置了其他默认值，例如，div元素默认为block。可以访问&lt;a href="http://www.w3.org/TR/CSS2/sample.html" rel="nofollow" target="_blank"&gt;http://www.w3.org/TR/CSS2/sample.html&lt;/a&gt;查看更多的默认样式表示例。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;定位策略 Position scheme&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;这里有三种策略：&lt;/p&gt; &lt;p&gt;1. normal－对象根据它在文档的中位置定位，这意味着它在渲染树和在Dom树中位置一致，并根据它的盒模型和大小进行布局&lt;/p&gt; &lt;p&gt;2. float－对象先像普通流一样布局，然后尽可能的向左或是向右移动&lt;/p&gt; &lt;p&gt;3. absolute－对象在渲染树中的位置和Dom树中位置无关&lt;/p&gt; &lt;p&gt;static和relative是normal，absolute和fixed属于absolute。&lt;/p&gt; &lt;p&gt;在static定位中，不定义位置而使用默认的位置。其他策略中，作者指定位置&amp;#8212;&amp;#8212;top、bottom、left、right。&lt;/p&gt; &lt;p&gt;Box布局的方式由这几项决定：box的类型、box的大小、定位策略及扩展信息（比如图片大小和屏幕尺寸）。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Box类型&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Block box：构成一个块，即在浏览器窗口上有自己的矩形&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work20.png"&gt;&lt;img size-full=""  wp-image-12770"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work20.png" alt="现代浏览器的工作原理" width="150" height="127" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Inline box：并没有自己的块状区域，但包含在一个块状区域内&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work21.png"&gt;&lt;img size-full=""  wp-image-12771"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work21.png" alt="现代浏览器的工作原理" width="300" height="233" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;block一个挨着一个垂直格式化，inline则在水平方向上格式化。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work22.png"&gt;&lt;img size-full=""  wp-image-12772"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work22.png" alt="现代浏览器的工作原理" width="350" height="324" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Inline盒模型放置在行内或是line  box中，每行至少和最高的box一样高，当box以baseline对齐时&amp;#8212;&amp;#8212;即一个元素的底部和另一个box上除底部以外的某点对齐，行高可以比最高 的box高。当容器宽度不够时，行内元素将被放到多行中，这在一个p元素中经常发生。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work23.png"&gt;&lt;img size-full=""  wp-image-12773"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work23.png" alt="现代浏览器的工作原理" width="400" height="277" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;定位 Position&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Relative&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;相对定位&amp;#8212;&amp;#8212;先按照一般的定位，然后按所要求的差值移动。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work24.png"&gt;&lt;img size-full=""  wp-image-12774"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work24.png" alt="现代浏览器的工作原理" width="500" height="261" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Floats&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;一个浮动的box移动到一行的最左边或是最右边，其余的box围绕在它周围。下面这段html：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_135338" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;p&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;img&lt;/code&gt; &lt;code plain"=""&gt;style=&amp;#8221;float:right&amp;#8221; src=&amp;#8221;images/image.gif&amp;#8221; width=&amp;#8221;100&amp;#8243; height=&amp;#8221;100&amp;#8243;&amp;gt;Lorem ipsum dolor sit amet, consectetuer&amp;#8230;&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;p&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;将显示为：&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work25.png"&gt;&lt;img size-full=""  wp-image-12775"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work25.png" alt="现代浏览器的工作原理" width="444" height="203" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Absolute和Fixed&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;这种情况下的布局完全不顾普通的文档流，元素不属于文档流的一部分，大小取决于容器。Fixed时，容器为viewport（可视区域）。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work26.png"&gt;&lt;img size-full=""  wp-image-12776"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work26.png" alt="现代浏览器的工作原理" width="500" height="343" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;图17：fixed&lt;/p&gt; &lt;p&gt;注意－fixed即使在文档流滚动时也不会移动。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Layered representation&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;这个由CSS属性中的z-index指定，表示盒模型的第三个大小，即在z轴上的位置。Box分发到堆栈中（称为堆栈上下文），每个堆栈中靠后的元 素将被较早绘制，栈顶靠前的元素离用户最近，当发生交叠时，将隐藏靠后的元素。堆栈根据z-index属性排序，拥有z-index属性的box形成了一 个局部堆栈，viewport有外部堆栈，例如：&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;div&gt;&lt;div id="highlighter_745565" notranslate=""  html"=""&gt;&lt;table border="0" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div number1="" index0=""  alt2"=""&gt;1&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;2&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;3&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;4&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;5&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;6&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;7&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;8&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;9&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;10&lt;/div&gt;&lt;div number11="" index10=""  alt2"=""&gt;11&lt;/div&gt;&lt;div number12="" index11=""  alt1"=""&gt;12&lt;/div&gt;&lt;div number13="" index12=""  alt2"=""&gt;13&lt;/div&gt;&lt;div number14="" index13=""  alt1"=""&gt;14&lt;/div&gt;&lt;div number15="" index14=""  alt2"=""&gt;15&lt;/div&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div number1="" index0=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;STYLE&lt;/code&gt; &lt;code plain"=""&gt;type=&amp;#8221;text/css&amp;#8221;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number2="" index1=""  alt1"=""&gt;&lt;code plain"=""&gt;div {&lt;/code&gt;&lt;/div&gt;&lt;div number3="" index2=""  alt2"=""&gt;&lt;code plain"=""&gt;position: absolute;&lt;/code&gt;&lt;/div&gt;&lt;div number4="" index3=""  alt1"=""&gt;&lt;code plain"=""&gt;left: 2in;&lt;/code&gt;&lt;/div&gt;&lt;div number5="" index4=""  alt2"=""&gt;&lt;code plain"=""&gt;top: 2in;&lt;/code&gt;&lt;/div&gt;&lt;div number6="" index5=""  alt1"=""&gt;&lt;code plain"=""&gt;}&lt;/code&gt;&lt;/div&gt;&lt;div number7="" index6=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;STYLE&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number8="" index7=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;P&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number9="" index8=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;DIV&lt;/code&gt;&lt;/div&gt;&lt;div number10="" index9=""  alt1"=""&gt;&lt;code plain"=""&gt;style=&amp;#8221;z-index: 3;background-color:red; width: 1in; height: 1in; &amp;#8220;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number11="" index10=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;DIV&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number12="" index11=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;&lt;/code&gt;&lt;code keyword"=""&gt;DIV&lt;/code&gt;&lt;/div&gt;&lt;div number13="" index12=""  alt2"=""&gt;&lt;code plain"=""&gt;style=&amp;#8221;z-index: 1;background-color:green;width: 2in; height: 2in;&amp;#8221;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number14="" index13=""  alt1"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;DIV&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;div number15="" index14=""  alt2"=""&gt;&lt;code plain"=""&gt;&amp;lt;/&lt;/code&gt;&lt;code keyword"=""&gt;p&lt;/code&gt;&lt;code plain"=""&gt;&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;结果是：&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work28.png"&gt;&lt;img size-full=""  wp-image-12779"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work28.png" alt="现代浏览器的工作原理" width="254" height="227" /&gt;&lt;/a&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work27.png"&gt;&lt;br /&gt; &lt;/a&gt;&lt;/p&gt; &lt;p&gt;虽然绿色div排在红色div后面，可能在正常流中也已经被绘制在后面，但z-index有更高优先级，所以在根box的堆栈中更靠前。&lt;/p&gt; &lt;p&gt;国外也有网友根据浏览器的工作原理绘制了几张工作流程图，方便大家通过简易的图片来了解这个辛苦的过程：&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work28.jpg"&gt;&lt;img size-full=""  wp-image-12778"="" title="现代浏览器的工作原理" src="http://blog.jobbole.com/wp-content/uploads/2012/02/How-browsers-work28.jpg" alt="现代浏览器的工作原理" width="435" height="2513" /&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;原文：&lt;a href="http://taligarsiel.com/Projects/howbrowserswork1.htm" rel="external nofollow" target="_blank"&gt;http://taligarsiel.com/Projects/howbrowserswork1.htm&lt;/a&gt;&lt;br /&gt; &lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;p&gt;http://blog.jobbole.com/12749/&lt;/p&gt;&lt;a href="http://blog.csdn.net/zzzaquarius" rel="external nofollow" target="_blank"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2340004.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/06/2340004.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/02/03/2337323.html</id><title type="text">JavaScript 作用域链解析</title><summary type="text">最近看了下JavaScript方面的几本书，把里面的一些核心概念按照自己的理解做个总结。 JavaScript中有Scope(作用域)，Scope chain(作用域链)，Execute context(执行上下文)，Active Object (活动对象),Dynamic Scope(动态作用域)，Closure(闭包)这些概念，要理解这些概念，我们从静态和动态两个方面去分析一下。 首先我们写一个简单的function来做一个例子： function add(num1, num2){ var sum = num1 + num2; return sum; } ...</summary><published>2012-02-03T08:54:00Z</published><updated>2012-02-03T08:54:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/03/2337323.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/03/2337323.html"/><content type="html">&lt;div&gt;&lt;div id="main_post"&gt;&lt;p style="text-indent:21.0pt"&gt;&lt;span data-scayt_word="最近看了下JavaScript方面的几本书，把里面的一些核心概念按照自己的理解做个总结。" data-scaytid="1"&gt;最近看了下JavaScript方面的几本书，把里面的一些核心概念按照自己的理解做个总结。&lt;/span&gt;&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;&lt;span data-scayt_word="JavaScript中有Scope" data-scaytid="2"&gt;JavaScript中有Scope&lt;/span&gt;(作用域)&lt;span data-scayt_word="，Scope" data-scaytid="3"&gt;，Scope&lt;/span&gt; chain(&lt;span data-scayt_word="作用域链" data-scaytid="4"&gt;作用域链&lt;/span&gt;)&lt;span data-scayt_word="，Execute" data-scaytid="5"&gt;，Execute&lt;/span&gt; context(&lt;span data-scayt_word="执行上下文" data-scaytid="6"&gt;执行上下文&lt;/span&gt;)&lt;span data-scayt_word="，Active" data-scaytid="7"&gt;，Active&lt;/span&gt; Object (&lt;span data-scayt_word="活动对象" data-scaytid="8"&gt;活动对象&lt;/span&gt;),Dynamic Scope(&lt;span data-scayt_word="动态作用域" data-scaytid="9"&gt;动态作用域&lt;/span&gt;)&lt;span data-scayt_word="，Closure" data-scaytid="10"&gt;，Closure&lt;/span&gt;(&lt;span data-scayt_word="闭包" data-scaytid="11"&gt;闭包&lt;/span&gt;)&lt;span data-scayt_word="这些概念，要理解这些概念，我们从静态和动态两个方面去分析一下。" data-scaytid="12"&gt;这些概念，要理解这些概念，我们从静态和动态两个方面去分析一下。&lt;/span&gt;&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;&lt;span data-scayt_word="首先我们写一个简单的function来做一个例子：" data-scaytid="13"&gt;首先我们写一个简单的function来做一个例子：&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:31.5pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;function add(&lt;span data-scayt_word="num1" data-scaytid="14"&gt;num1&lt;/span&gt;, &lt;span data-scayt_word="num2" data-scaytid="15"&gt;num2&lt;/span&gt;){&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:42.0pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;&lt;span data-scayt_word="var" data-scaytid="18"&gt;var&lt;/span&gt; sum = &lt;span data-scayt_word="num1" data-scaytid="16"&gt;num1&lt;/span&gt; + &lt;span data-scayt_word="num2" data-scaytid="17"&gt;num2&lt;/span&gt;;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:42.0pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;return sum;&lt;/span&gt;&lt;/p&gt; &lt;p style="text-indent:32.9pt"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;}&lt;/span&gt;&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;&lt;span data-scayt_word="我们定义了一个具有两个形参的add函数。" data-scaytid="19"&gt;我们定义了一个具有两个形参的add函数。&lt;/span&gt;&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;静态方面：&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;当创建add函数的时候，Javascript引擎会创建add函数的Scope chain,这个作用域链指向了Global Context(全局上下文)。如果用图形形象化的表述如下图所示：&lt;/p&gt; &lt;p style="text-indent:21pt;text-align:center"&gt;&lt;br /&gt; 	&lt;img style="width: 540px; height: 164px;" alt="" src="http://t.cyol.com/cache/temp/img/2011/10/1000/119/img/img_1319037650_0.jpg" /&gt;&lt;/p&gt; &lt;p style="text-indent:21pt;text-align:center"&gt;&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;从上图可以看出，当add函数创建的时候，作用域链就已经创建了，因此可以得出一个结论，函数的作用域链是创建函数的时候就已经创建了，而不是动态运行期。下面就来看看动态运行期的时候会发生什么事情。&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;动态方面：&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;当执行add函数的时候，JavaScript会创建一个Execute context（执行上下文），执行上下文中就包含了add函数运行期所需要的所有信息。Execute context也有自己的Scope chain,当函数运行的时候，JavaScript引擎会首先从用add函数的作用域链来初始化执行上下文的作用域链，然后JavaScript引擎又会创建一个Active Object,这个对象里面包含了函数运行期的所有局部变量，参数以及this等变量。&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;如果形象的描述add函数动态运行期会发生什么，可以用如下图来描述：&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;&lt;br /&gt; 	&lt;img style="width: 540px; height: 340px;" alt="" src="http://t.cyol.com/cache/temp/img/2011/10/1000/119/img/img_1319037651_1.jpg" /&gt;&lt;br /&gt; 	&amp;nbsp;从上图可以看出，执行上下文是一个动态的概念，它是当函数运行的时候创建的，同时Active Object对象也是一个动态的概念，它是被执行上下文的作用域链引用的。因此可以得出一个结论：执行上下文和活动对象都是动态概念，并且执行上下文的作用域链是由函数作用域链初始化的。&lt;/p&gt; &lt;p style="text-indent:21.0pt"&gt;上面说了函数作用域和执行上下文作用域，下面接着说一下动态作用域的问题，当在JavaScript通过with语句，try-catch的catch子句，以及eval方法的时候，JavaScript引擎就会动态的改变执行上下文的作用域。下面还是通过一个例子来看看：&lt;/p&gt; &lt;p style="margin-left:10.5pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;function initUI(){&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:21.0pt;text-align:left" align="left"&gt;&lt;strong&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W7Bold"&gt;with (document){ //avoid!&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p style="margin-left:31.5pt;text-align:left" align="left"&gt;&lt;strong&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W7Bold"&gt;var bd = body,&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p style="margin-left:31.5pt;text-align:left" align="left"&gt;&lt;strong&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W7Bold"&gt;links = getElementsByTagName("a"),&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p style="margin-left:31.5pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;i= 0,&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:31.5pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;len = links.length;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:31.5pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;while(i &amp;lt; len){&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:31.5pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;update(links[i++]);&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:21.0pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;}&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:21.0pt;text-align:left" align="left"&gt;&lt;strong&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W7Bold"&gt;getElementById("go-btn").onclick = function(){&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p style="margin-left:21.0pt;text-align:left;text-indent:21.0pt" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;start();&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:21.0pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;};&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:21.0pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;bd.className = "active";&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:10.5pt;text-align:left" align="left"&gt;&lt;strong&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W7Bold"&gt;}&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p style="text-indent:10.5pt"&gt;当执行上面的initUI函数的时候，JavaScript会动态的创建一个with 语句对应的作用域放到执行上下文作用域链的最前端，通过下图可以形象的描述上述过程，下图红色标注的区域就显示了with语句产生的作用域。&lt;/p&gt; &lt;p style="text-indent:10.5pt"&gt;&lt;br /&gt; 	&lt;img style="width: 540px; height: 463px;" alt="" src="http://t.cyol.com/cache/temp/img/2011/10/1000/119/img/img_1319037651_2.jpg" /&gt;&lt;br /&gt; 	&amp;nbsp;最后，我们来看看JavaScript最神秘的Closure（闭包），闭包在JavaScript其实就是一个函数，闭包是在函数运行期被创建的，下面还是以一个实例来看看：&lt;/p&gt; &lt;p style="text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;function assignEvents(){&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:10.5pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;var id = "xdi9592";&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:10.5pt;text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;document.getElementById("save-btn").onclick = function(event){&lt;/span&gt;&lt;/p&gt; &lt;p style="margin-left:10.5pt;text-align:left;text-indent:10.5pt" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;saveDocument(id);&lt;/span&gt;&lt;/p&gt; &lt;p style="text-align:left;text-indent:10.5pt" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;};&lt;/span&gt;&lt;/p&gt; &lt;p style="text-align:left" align="left"&gt;&lt;span style="font-size:8.0pt;font-family:TheSansMonoCd-W5Regular"&gt;}&lt;/span&gt;&lt;/p&gt; &lt;p&gt;当上面的assignEvents函数被执行的时候，会创建一个闭包，而这个闭包会引用assignEvents作用域中的id变量，如果按照传统的编程语言的方式，id是存储在堆栈上的一个变量，当函数执行完了以后id就消失，那么怎么可能再次引用呢？显然这里JavaScript采用了另外的方式。下面就来看看JavaScript是如何来实现闭包的。当执行assignEvents函数的时候，JavaScript引擎会创建函数作用域链，这个作用域链包含了assignEvents执行时的活动对象，而同时JavaScript引擎也会创建一个闭包，而闭包的作用域链也会引用assignEvent执行时候的活动对象，这样当assignEvents执行完的时候，虽然它本身执行上下文的作用域链不再引用活动对象了，但是闭包还是引用着assignEvents运行期对应的活动对象，这就解释了JavaScipt内部的闭包机制。可以用下图形象的表述上面assignEvents函数运行期的情形：&lt;/p&gt; &lt;p&gt;&lt;br /&gt; 	&lt;img style="width: 540px; height: 317px;" alt="" src="http://t.cyol.com/cache/temp/img/2011/10/1000/119/img/img_1319037651_3.jpg" /&gt;&lt;br /&gt; 	&amp;nbsp; &amp;nbsp;&amp;nbsp;从上面可以看出，当assignEvents函数执行完毕以后，document.getElementById("save-btn").onclick 引用了闭包，这样当用户点击save-btn的时候，就会触发闭包的执行，那么下面就来看看闭包执行时的情形。前面也说了JavaScript中闭包其实就是函数，因此闭包执行和函数执行时的情形是一致的，通过下图来形象的描述上述onclick事件所关联的闭包。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;br /&gt; 	&lt;img style="width: 540px; height: 402px;" alt="" src="http://t.cyol.com/cache/temp/img/2011/10/1000/119/img/img_1319037651_4.jpg" /&gt;&lt;br /&gt; 	&amp;nbsp;从上图可以看出JavaScript引擎首先创建了闭包的执行上下文，然后用闭包作用域链来初始化闭包的执行上下文作用域链，最后再将闭包执行时对应的活动对象放入到作用域的最前端，这也进一步验证了闭包就是函数的论断。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;参考资料：&lt;/p&gt; &lt;p&gt;1.High Performance JavaScript.&amp;nbsp;http://book.douban.com/subject/5362856/&lt;/p&gt; &lt;p&gt;2.JavaScript高级程序设计. http://book.douban.com/subject/4886879/&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;转载：&lt;div&gt;http://www.starming.com/index.php?action=plugin&amp;amp;v=wave&amp;amp;tpl=union&amp;amp;ac=viewgrouppost&amp;amp;gid=119&amp;amp;tid=17313&lt;/div&gt; &lt;br /&gt;&lt;/p&gt; &lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2337323.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/03/2337323.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/02/03/2337320.html</id><title type="text">转载：Javascript作用域原理</title><summary type="text">问题的提出 首先看一个例子: var name = &amp;#39;laruence&amp;#39;;function echo() {alert(name);var name = &amp;#39;eve&amp;#39;;alert(name);alert(age);}echo(); 运行结果是什么呢? 上面的问题, 我相信会有很多人会认为是: laruenceeve[脚本出错] 因为会以为在echo中, 第一次alert的时候, 会取到全局变量name的值, 而第二次值被局部变量name覆盖, 所以第二次alert是’eve’. 而age属性没有定义, 所以脚本会出错. 但其实, 运行结果应该是: undefine</summary><published>2012-02-03T08:53:00Z</published><updated>2012-02-03T08:53:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/03/2337320.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/03/2337320.html"/><content type="html">&lt;div&gt;&lt;div&gt; 			   &lt;p&gt;&lt;strong&gt;问题的提出&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;首先看一个例子:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;var name = 'laruence';&lt;/li&gt;&lt;li&gt;function echo() {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(name);&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var name = 'eve';&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(name);&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(age);&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;echo();&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;运行结果是什么呢?&lt;/p&gt; &lt;p&gt;上面的问题, 我相信会有很多人会认为是:&lt;/p&gt; &lt;pre name="code"&gt;&lt;ol&gt;&lt;li&gt;laruence&lt;/li&gt;&lt;li&gt;eve&lt;/li&gt;&lt;li&gt;[脚本出错]&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;因为会以为在echo中, 第一次alert的时候, 会取到全局变量name的值, 而第二次值被局部变量name覆盖, 所以第二次alert是&amp;#8217;eve&amp;#8217;. 而age属性没有定义, 所以脚本会出错.&lt;/p&gt; &lt;p&gt;但其实, 运行结果应该是:&lt;/p&gt; &lt;pre name="code"&gt;&lt;ol&gt;&lt;li&gt;undefined&lt;/li&gt;&lt;li&gt;eve&lt;/li&gt;&lt;li&gt;[脚本出错]&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;为什么呢?&lt;/p&gt; &lt;p&gt;&lt;strong&gt;JavaScript的作用域链&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;首先让让我们来看看Javasript(简称JS, 不完全代表JScript)的作用域的原理: JS权威指南中有一句很精辟的描述:　&amp;#8221;JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.&amp;#8221;　&lt;/p&gt; &lt;p&gt;为了接下来的知识, 你能顺利理解, 我再提醒一下, 在JS中:&amp;#8221;一切皆是对象, 函数也是&amp;#8221;.&lt;/p&gt; &lt;p&gt;在JS中，作用域的概念和其他语言差不多， 在每次调用一个函数的时候 ，就会进入一个函数内的作用域，当从函数返回以后，就返回调用前的作用域.&lt;/p&gt; &lt;p&gt;JS的语法风格和C/C++类似, 但作用域的实现却和C/C++不同，并非用&amp;#8220;堆栈&amp;#8221;方式，而是使用列表，具体过程如下(ECMA262中所述):&lt;br /&gt; 任何执行上下文时刻的作用域, 都是由作用域链(scope chain, 后面介绍)来实现.&lt;br /&gt; 在一个函数被定义的时候, 会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性.&lt;br /&gt; 在一个函数对象被调用的时候，会创建一个活动对象(也就是一个对象), 然后对于每一个函数的形参，都命名为该活动对象的命名属性,   然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中.&lt;/p&gt; &lt;p&gt;看个例子:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var func = function(lps, rps){&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var name = 'laruence';&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;........&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func();&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;在执行func的定义语句的时候, 会创建一个这个函数对象的[[scope]]属性(内部属性,只有JS引擎可以访问,   但FireFox的几个引擎(SpiderMonkey和Rhino)提供了私有属性__parent__来访问它),   并将这个[[scope]]属性, 链接到定义它的作用域链上(后面会详细介绍), 此时因为func定义在全局环境,   所以此时的[[scope]]只是指向全局活动对象window active object.&lt;/p&gt; &lt;p&gt;在调用func的时候,  会创建一个活动对象(假设为aObj, 由JS引擎预编译时刻创建, 后面会介绍)，并创建arguments属性,   然后会给这个对象添加俩个命名属性aObj.lps, aObj.rps; 对于每一个在这个函数中申明的局部变量和函数定义,   都作为该活动对象的同名命名属性.&lt;/p&gt; &lt;p&gt;然后将调用参数赋值给形参数，对于缺少的调用参数，赋值为undefined。&lt;/p&gt; &lt;p&gt;然后将这个活动对象做为scope chain的最前端, 并将func的[[scope]]属性所指向的,定义func时候的顶级活动对象, 加入到scope china.&lt;/p&gt; &lt;p&gt;有了上面的作用域链, 在发生标识符解析的时候, 就会逆向查询当前scope chain列表的每一个活动对象的属性，如果找到同名的就返回。找不到，那就是这个标识符没有被定义。&lt;/p&gt; &lt;p&gt;注意到, 因为函数对象的[[scope]]属性是在定义一个函数的时候决定的, 而非调用的时候, 所以如下面的例子:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var name = 'laruence';&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function echo() {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(name);&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function env() {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var name = 'eve';&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;echo();&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;env();&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;运行结果是:&lt;/p&gt; &lt;pre name="code"&gt;&lt;ol&gt;&lt;li&gt;laruence&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;结合上面的知识, 我们来看看下面这个例子:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;function factory() {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var name = 'laruence';&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var intro = function(){&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert('I am ' + name);&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return intro;&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;function app(para){&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var name = para;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var func = factory();&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func();&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;app('eve');&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;当调用app的时候, scope chain是由: {window活动对象(全局)}-&amp;gt;{app的活动对象} 组成.&lt;/p&gt; &lt;p&gt;在刚进入app函数体时, app的活动对象有一个arguments属性, 俩个值为undefined的属性: name和func. 和一个值为&amp;#8217;eve&amp;#8217;的属性para;&lt;/p&gt; &lt;p&gt;此时的scope chain如下:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;[[scope chain]] = [&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;para : 'eve',&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name : undefined,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func : undefined,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;arguments : []&lt;/li&gt;&lt;li&gt;}, {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window call object&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;]&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;当调用进入factory的函数体的时候, 此时的factory的scope chain为:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;[[scope chain]] = [&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name : undefined,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;intor : undefined&lt;/li&gt;&lt;li&gt;}, {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window call object&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;]&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;注意到, 此时的作用域链中, 并不包含app的活动对象.&lt;/p&gt; &lt;p&gt;在定义intro函数的时候, intro函数的[[scope]]为:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;[[scope chain]] = [&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name : 'laruence',&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;intor : undefined&lt;/li&gt;&lt;li&gt;}, {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window call object&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;]&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;从factory函数返回以后,在app体内调用intor的时候, 发生了标识符解析, 而此时的sope chain是:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;[[scope chain]] = [&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;intro call object&lt;/li&gt;&lt;li&gt;}, {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name : 'laruence',&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;intor : undefined&lt;/li&gt;&lt;li&gt;}, {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;window call object&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;]&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;因为scope chain中,并不包含factory活动对象. 所以, name标识符解析的结果应该是factory活动对象中的name属性, 也就是&amp;#8217;laruence&amp;#8217;.&lt;/p&gt; &lt;p&gt;所以运行结果是:&lt;/p&gt; &lt;pre name="code"&gt;&lt;ol&gt;&lt;li&gt;I am laruence&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;现在, 大家对&amp;#8221;JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.&amp;#8221;这句话, 应该有了个全面的认识了吧?&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Javascript的预编译&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;我们都知道,JS是一种脚本语言, JS的执行过程, 是一种翻译执行的过程.&lt;br /&gt; 那么JS的执行中, 有没有类似编译的过程呢?&lt;/p&gt; &lt;p&gt;首先, 我们来看一个例子:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;script&amp;gt;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(typeof eve); //function&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function eve() {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert('I am Laruence');&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/script&amp;gt;&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;诶? 在alert的时候, eve不是应该还是未定义的么? 怎么eve的类型还是function呢?&lt;/p&gt; &lt;p&gt;恩, 对, 在JS中, 是有预编译的过程的, JS在执行每一段JS代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式).&lt;br /&gt; 如上文所说, 在调用函数执行之前, 会首先创建一个活动对象, 然后搜寻这个函数中的局部变量定义,和函数定义, 将变量名和函数名都做为这个活动对象的同名属性, 对于局部变量定义,变量的值会在真正执行的时候才计算, 此时只是简单的赋为undefined.&lt;/p&gt; &lt;p&gt;而对于函数的定义,是一个要注意的地方:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;&amp;lt;script&amp;gt;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(typeof eve); //结果:function&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(typeof walle); //结果:undefined&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function eve() { //函数定义式&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert('I am Laruence');&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var walle = function() { //函数表达式&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(typeof walle); //结果:function&lt;/li&gt;&lt;li&gt;&amp;lt;/script&amp;gt;&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;这就是函数定义式和函数表达式的不同, 对于函数定义式, 会将函数定义提前. 而函数表达式, 会在执行过程中才计算.&lt;/p&gt; &lt;p&gt;说到这里, 顺便说一个问题 :&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var name = 'laruence';&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;age = 26;&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;我们都知道不使用var关键字定义的变量, 相当于是全局变量, 联系到我们刚才的知识:&lt;/p&gt; &lt;p&gt;在对age做标识符解析的时候, 因为是写操作, 所以当找到到全局的window活动对象的时候都没有找到这个标识符的时候, 会在window活动对象的基础上, 返回一个值为undefined的age属性.&lt;/p&gt; &lt;p&gt;也就是说, age会被定义在顶级作用域中.&lt;/p&gt; &lt;p&gt;现在, 也许你注意到了我刚才说的: JS在执行&lt;strong&gt;每一段JS代码&lt;/strong&gt;..&lt;br /&gt; 对, 让我们看看下面的例子:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;&amp;lt;script&amp;gt;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(typeof eve); //结果:undefined&lt;/li&gt;&lt;li&gt;&amp;lt;/script&amp;gt;&lt;/li&gt;&lt;li&gt;&amp;lt;script&amp;gt;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;function eve() {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert('I am Laruence');&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/li&gt;&lt;li&gt;&amp;lt;/script&amp;gt;&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;明白了么? 也就是JS的预编译是以段为处理单元的&amp;#8230;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;揭开谜底&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;现在让我们回到我们的第一个问题:&lt;/p&gt; &lt;p&gt;当echo函数被调用的时候, echo的活动对象已经被预编译过程创建, 此时echo的活动对象为:&lt;/p&gt; &lt;pre sh_sourcecode"="" name="code"&gt;&lt;ol&gt;&lt;li&gt;[callObj] = {&lt;/li&gt;&lt;li&gt;name : undefined&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;/ol&gt;&lt;/pre&gt; &lt;p&gt;当第一次alert的时候, 发生了标识符解析, 在echo的活动对象中找到了name属性, 所以这个name属性, 完全的遮挡了全局活动对象中的name属性.&lt;/p&gt; &lt;p&gt;现在你明白了吧?&lt;/p&gt;           &lt;/div&gt;&lt;/div&gt;未找到出处，请知道的人告之。&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2337320.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/03/2337320.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/02/02/2335475.html</id><title type="text">JavaScript中的值类型和引用类型</title><summary type="text">一、拥抱JavaScript 曾经名不经传的JavaScript随着AJAX的流行而身价倍增，现在JavaScript不再仅仅是WEB开发中一个可有可无的辅助工具，甚至 有了专门属于它的职位“JavaScript工程师”，那怕你仅仅是一名WEB后台开发程序员，你都必须了解JavaScript，至少在一些相关招聘职 位要求上你可以看到“熟悉JavaScript优先”的字眼。甚至我还要告诉你，你将可以用JavaScript开发桌面软件，这得益于Adobe AIR的另外一种开发模式，即用HTML+CSS+JavaScript开发AIR。 二、值类型和引用类型话题 随着部分有大型面向对象语言基础朋友.</summary><published>2012-02-02T02:58:00Z</published><updated>2012-02-02T02:58:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/02/2335475.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/02/2335475.html"/><content type="html">&lt;div&gt;&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;&lt;strong&gt;一、拥抱JavaScript&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;曾经名不经传的JavaScript随着AJAX的流行而身价倍增，现在JavaScript不再仅仅是WEB开发中一个可有可无的辅助工具，甚至 有了专门属于它的职位&amp;#8220;JavaScript工程师&amp;#8221;，那怕你仅仅是一名WEB后台开发程序员，你都必须了解JavaScript，至少在一些相关招聘职 位要求上你可以看到&amp;#8220;熟悉JavaScript优先&amp;#8221;的字眼。甚至我还要告诉你，你将可以用JavaScript开发桌面软件，这得益于Adobe  AIR的另外一种开发模式，即&lt;a href="http://help.adobe.com/zh_CN/AIR/1.5/devappshtml/" target="_blank"&gt;用HTML+CSS+JavaScript开发AIR&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;二、值类型和引用类型话题&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;随着部分有大型面向对象语言基础朋友的介入，他们试着用JavaScript去模拟面像对象的各种特征，尽管有些模拟显得较为牵强，但也让我们见识 到了JavaScript的强大与灵活性。本文暂不探讨JavaScript面向对象编程技术。就讲讲JavaScript中的两种变量类型：即值类型和 引用类型，这通常又会让你联想到&amp;#8220;堆栈&amp;#8221;，另外还有&amp;#8220;引用地址&amp;#8221;或&amp;#8220;指针&amp;#8221;相关概念，有过Java或C#编程经验的人相信对这两种类型不陌生。下面就举例 讲一下这两种类型在JavaScript中的体现、用法及注意事项。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;三、JavaScript值类型和引用类型有哪些&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;（1）值类型：&lt;/strong&gt;数值、布尔值、null、undefined。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;（2）引用类型：&lt;/strong&gt;对象、数组、函数。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;四、如何理解值类型和引用类型及举例&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;我们可以用&amp;#8220;连锁店&amp;#8221;和&amp;#8220;连锁店钥匙&amp;#8221;来理解，不知道以下比喻合不合适，^-^。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;（1）值类型理解：&lt;/strong&gt;变量的交换等于在一个新的地方按照连锁店的规范标准（统一店面理解为相同的变量内容）新开一个分店，这样新开的店与其它旧店互不相关、各自运营。&lt;/p&gt; &lt;p&gt;【值类型例子】&lt;/p&gt; &lt;div&gt;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;chainStore()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;store1&lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt;'&lt;/span&gt;&lt;span style="color: #000000"&gt;Nike&amp;nbsp;China&lt;/span&gt;&lt;span style="color: #000000"&gt;'&lt;/span&gt;&lt;span style="color: #000000"&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;store2&lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt;store1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;store1&lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt;'&lt;/span&gt;&lt;span style="color: #000000"&gt;Nike&amp;nbsp;U.S.A.&lt;/span&gt;&lt;span style="color: #000000"&gt;'&lt;/span&gt;&lt;span style="color: #000000"&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(store2);&amp;nbsp;&lt;/span&gt;&lt;span style="color: #008000"&gt;//&lt;/span&gt;&lt;span style="color: #008000"&gt;Nike&amp;nbsp;China&lt;/span&gt;&lt;span style="color: #008000"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;}&lt;br /&gt;chainStore();&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008000"&gt;//&lt;/span&gt;&lt;span style="color: #008000"&gt;把一个值类型（也可以叫基本类型）store2传递给另一个变量（赋值）时，其实是分配了一块新的内存空间，因此改变store1的值对store2没有任何影响，因为它不像引用类型，变量的交换其实是交换了指像同一个内容的地址。&lt;/span&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;（2）引用类型理解：&lt;/strong&gt;变量的交换等于把现有一间店的钥匙（变量引用地址）复制一把给了另外一个老板，此时两个老板同时管理一间店，两个老板的行为都有可能对一间店的运营造成影响。&lt;/p&gt; &lt;p&gt;【引用类型例子】&lt;/p&gt; &lt;div&gt;&lt;span style="color: #0000ff"&gt;function&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;chainStore()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;store1&lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt;[&lt;/span&gt;&lt;span style="color: #000000"&gt;'&lt;/span&gt;&lt;span style="color: #000000"&gt;Nike&amp;nbsp;China&lt;/span&gt;&lt;span style="color: #000000"&gt;'&lt;/span&gt;&lt;span style="color: #000000"&gt;];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;var&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;store2&lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt;store1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(store2[&lt;/span&gt;&lt;span style="color: #000000"&gt;0&lt;/span&gt;&lt;span style="color: #000000"&gt;]);&amp;nbsp;&lt;/span&gt;&lt;span style="color: #008000"&gt;//&lt;/span&gt;&lt;span style="color: #008000"&gt;Nike&amp;nbsp;China&lt;/span&gt;&lt;span style="color: #008000"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;store1[&lt;/span&gt;&lt;span style="color: #000000"&gt;0&lt;/span&gt;&lt;span style="color: #000000"&gt;]&lt;/span&gt;&lt;span style="color: #000000"&gt;=&lt;/span&gt;&lt;span style="color: #000000"&gt;'&lt;/span&gt;&lt;span style="color: #000000"&gt;Nike&amp;nbsp;U.S.A.&lt;/span&gt;&lt;span style="color: #000000"&gt;'&lt;/span&gt;&lt;span style="color: #000000"&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert(store2[&lt;/span&gt;&lt;span style="color: #000000"&gt;0&lt;/span&gt;&lt;span style="color: #000000"&gt;]);&amp;nbsp;&lt;/span&gt;&lt;span style="color: #008000"&gt;//&lt;/span&gt;&lt;span style="color: #008000"&gt;Nike&amp;nbsp;U.S.A.&lt;/span&gt;&lt;span style="color: #008000"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000"&gt;}&lt;br /&gt;chainStore();&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008000"&gt;//&lt;/span&gt;&lt;span style="color: #008000"&gt;在上面的代码中，store2只进行了一次赋值，理论上它的值已定，但后面通过改写store1的值，发现store2的值也发生了改变，这正是引用类型的特征，也是我们要注意的地方。&lt;/span&gt;&lt;/div&gt;&lt;p&gt;转载：&lt;div&gt;http://www.cnblogs.com/webflash/archive/2009/08/22/1552042.html&lt;/div&gt; &lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2335475.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/02/2335475.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/02/01/2334802.html</id><title type="text">JavaScript 秘密花园 http://bonsaiden.github.com/JavaScript-Garden/zh/</title><summary type="text">简介JavaScript 秘密花园是一个不断更新，主要关心 JavaScript 一些古怪用法的文档。 对于如何避免常见的错误，难以发现的问题，以及性能问题和不好的实践给出建议， 初学者可以籍此深入了解 JavaScript 的语言特性。 JavaScript 秘密花园不是用来教你 JavaScript。为了更好的理解这篇文章的内容， 你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习向导。 译者注： 文中提到的 ES5 是 ECMAScript 5 的简写，是 ECMAScript 标准语言的下一版本，正在...</summary><published>2012-02-01T08:22:00Z</published><updated>2012-02-01T08:22:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/01/2334802.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/01/2334802.html"/><content type="html">&lt;div&gt;&lt;header id="intro.intro"&gt;&lt;p&gt;&lt;strong&gt;简介&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;JavaScript 秘密花园&lt;/strong&gt;是一个不断更新，主要关心 JavaScript 一些古怪用法的文档。 对于如何避免常见的错误，难以发现的问题，以及性能问题和不好的实践给出建议， 初学者可以籍此深入了解 JavaScript 的语言特性。&lt;/p&gt;  &lt;p&gt;JavaScript 秘密花园&lt;strong&gt;不是&lt;/strong&gt;用来教你 JavaScript。为了更好的理解这篇文章的内容， 你需要事先学习 JavaScript 的基础知识。在 Mozilla 开发者网络中有一系列非常棒的 JavaScript 学习&lt;a href="https://developer.mozilla.org/en/JavaScript/Guide"&gt;向导&lt;/a&gt;。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;译者注：&lt;/strong&gt; 文中提到的 ES5 是 ECMAScript 5 的简写，是 ECMAScript 标准语言的下一版本，正在开发中。   JavaScript 是此标准语言的一个方言。&lt;/p&gt; &lt;/aside&gt;&lt;/div&gt;&lt;/header&gt;&lt;article id="intro.authors"&gt;&lt;p&gt;&lt;strong&gt;﻿关于作者&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;这篇文章的作者是两位 &lt;a href="http://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; 用户, &lt;a href="http://stackoverflow.com/users/170224/ivo-wetzel"&gt;伊沃&amp;#183;韦特泽尔 Ivo Wetzel&lt;/a&gt;（写作） 和 &lt;a href="http://stackoverflow.com/users/313758/yi-jiang"&gt;张易江 Zhang Yi Jiang&lt;/a&gt;（设计）。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="intro.contributors"&gt;&lt;p&gt;&lt;strong&gt;﻿贡献者&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/caio"&gt;Caio Rom&amp;#227;o&lt;/a&gt; （拼写检查）&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/blixt"&gt;Andreas Blixt&lt;/a&gt; （语言修正）&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;中文翻译&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;&lt;li&gt;&lt;a href="http://sanshi.me/"&gt;三生石上&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;此中文翻译由&lt;a href="http://sanshi.me/"&gt;三生石上&lt;/a&gt;独立完成，&lt;a href="http://cnblogs.com/sanshi/"&gt;博客园&lt;/a&gt;首发，转载请注明出处。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="intro.license"&gt;&lt;p&gt;&lt;strong&gt;﻿许可&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;JavaScript 秘密花园在 &lt;a href="https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE"&gt;MIT license&lt;/a&gt; 许可协议下发布，并存放在 &lt;a href="https://github.com/BonsaiDen/JavaScript-Garden"&gt;GitHub&lt;/a&gt; 开源社区。 如果你发现错误或者打字错误，请&lt;a href="https://github.com/BonsaiDen/JavaScript-Garden/issues"&gt;新建一个任务单&lt;/a&gt;或者发一个抓取请求。 你也可以在 Stack Overflow 的 &lt;a href="http://chat.stackoverflow.com/rooms/17/javascript"&gt;JavaScript 聊天室&lt;/a&gt;找到我们。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;section id="object"&gt;&lt;header id="object.intro"&gt;&lt;p&gt;&lt;strong&gt;对象&lt;/strong&gt;&lt;/p&gt;&lt;/header&gt;&lt;article id="object.general"&gt;&lt;p&gt;&lt;strong&gt;﻿对象使用和属性&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;JavaScript 中所有变量都是对象，除了两个例外 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#core.undefined"&gt;&lt;code&gt;null&lt;/code&gt;&lt;/a&gt; 和 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#core.undefined"&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;false.toString() // 'false'&lt;br /&gt;[1, 2, 3].toString(); // '1,2,3'&lt;br /&gt;&lt;br /&gt;function Foo(){}&lt;br /&gt;Foo.bar = 1;&lt;br /&gt;Foo.bar; // 1&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;一个常见的误解是数字的字面值（literal）不是对象。这是因为 JavaScript 解析器的一个错误， 它试图将&lt;em&gt;点操作符&lt;/em&gt;解析为浮点数字面值的一部分。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;2.toString(); // 出错：SyntaxError&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;有很多变通方法可以让数字的字面值看起来像对象。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;2..toString(); // 第二个点号可以正常解析&lt;br /&gt;2 .toString(); // 注意点号前面的空格&lt;br /&gt;(2).toString(); // 2先被计算&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;对象作为数据类型&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;JavaScript 的对象可以作为&lt;a href="http://en.wikipedia.org/wiki/Hashmap"&gt;&lt;em&gt;哈希表&lt;/em&gt;&lt;/a&gt;使用，主要用来保存命名的键与值的对应关系。&lt;/p&gt;  &lt;p&gt;使用对象的字面语法 - &lt;code&gt;{}&lt;/code&gt; - 可以创建一个简单对象。这个新创建的对象从 &lt;code&gt;Object.prototype&lt;/code&gt; &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.prototype"&gt;继承&lt;/a&gt;下面，没有任何&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.hasownproperty"&gt;自定义属性&lt;/a&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = {}; // 一个空对象&lt;br /&gt;&lt;br /&gt;// 一个新对象，拥有一个值为12的自定义属性'test'&lt;br /&gt;var bar = {test: 12}; &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;访问属性&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;有两种方式来访问对象的属性，点操作符或者中括号操作符。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = {name: 'Kitten'}&lt;br /&gt;foo.name; // kitten&lt;br /&gt;foo['name']; // kitten&lt;br /&gt;&lt;br /&gt;var get = 'name';&lt;br /&gt;foo[get]; // kitten&lt;br /&gt;&lt;br /&gt;foo.1234; // SyntaxError&lt;br /&gt;foo['1234']; // works&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;两种语法是等价的，但是中括号操作符在下面两种情况下依然有效  - 动态设置属性  - 属性名不是一个有效的变量名（&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;比如属性名中包含空格，或者属性名是 JS 的关键词）&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;在 &lt;a href="http://www.jslint.com/"&gt;JSLint&lt;/a&gt; 语法检测工具中，点操作符是推荐做法。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;删除属性&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;删除属性的唯一方法是使用 &lt;code&gt;delete&lt;/code&gt; 操作符；设置属性为 &lt;code&gt;undefined&lt;/code&gt; 或者 &lt;code&gt;null&lt;/code&gt; 并不能真正的删除属性， 而&lt;strong&gt;仅仅&lt;/strong&gt;是移除了属性和值的关联。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var obj = {&lt;br /&gt;&amp;nbsp; &amp;nbsp; bar: 1,&lt;br /&gt;&amp;nbsp; &amp;nbsp; foo: 2,&lt;br /&gt;&amp;nbsp; &amp;nbsp; baz: 3&lt;br /&gt;};&lt;br /&gt;obj.bar = undefined;&lt;br /&gt;obj.foo = null;&lt;br /&gt;delete obj.baz;&lt;br /&gt;&lt;br /&gt;for(var i in obj) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (obj.hasOwnProperty(i)) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(i, '' + obj[i]);&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面的输出结果有 &lt;code&gt;bar undefined&lt;/code&gt; 和 &lt;code&gt;foo null&lt;/code&gt; - 只有 &lt;code&gt;baz&lt;/code&gt; 被真正的删除了，所以从输出结果中消失。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;属性名的语法&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var test = {&lt;br /&gt;&amp;nbsp; &amp;nbsp; 'case': 'I am a keyword so I must be notated as a string',&lt;br /&gt;&amp;nbsp; &amp;nbsp; delete: 'I am a keyword too so me' // 出错：SyntaxError&lt;br /&gt;};&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;对象的属性名可以使用字符串或者普通字符声明。但是由于 JavaScript 解析器的另一个错误设计， 上面的第二种声明方式在 ECMAScript 5 之前会抛出 &lt;code&gt;SyntaxError&lt;/code&gt; 的错误。&lt;/p&gt;  &lt;p&gt;这个错误的原因是 &lt;code&gt;delete&lt;/code&gt; 是 JavaScript 语言的一个&lt;em&gt;关键词&lt;/em&gt;；因此为了在更低版本的 JavaScript 引擎下也能正常运行， 必须使用&lt;em&gt;字符串字面值&lt;/em&gt;声明方式。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="object.prototype"&gt;&lt;p&gt;&lt;strong&gt;﻿原型&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;JavaScript 不包含传统的类继承模型，而是使用 &lt;em&gt;prototypal&lt;/em&gt; 原型模型。&lt;/p&gt;  &lt;p&gt;虽然这经常被当作是 JavaScript 的缺点被提及，其实基于原型的继承模型比传统的类继承还要强大。 实现传统的类继承模型是很简单，但是实现 JavaScript 中的原型继承则要困难的多。 (It is for example fairly trivial to build a classic model on top of it, while the other way around is a far more difficult task.)&lt;/p&gt;  &lt;p&gt;由于 JavaScript 是唯一一个被广泛使用的基于原型继承的语言，所以理解两种继承模式的差异是需要一定时间的。&lt;/p&gt;  &lt;p&gt;第一个不同之处在于 JavaScript 使用&lt;em&gt;原型链&lt;/em&gt;的继承方式。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 简单的使用 &lt;code&gt;Bar.prototype = Foo.prototype&lt;/code&gt; 将会导致两个对象共享&lt;strong&gt;相同&lt;/strong&gt;的原型。   因此，改变任意一个对象的原型都会影响到另一个对象的原型，在大多数情况下这不是希望的结果。&lt;/p&gt; &lt;/aside&gt;  &lt;pre&gt;&lt;code&gt;function Foo() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.value = 42;&lt;br /&gt;}&lt;br /&gt;Foo.prototype = {&lt;br /&gt;&amp;nbsp; &amp;nbsp; method: function() {}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;function Bar() {}&lt;br /&gt;&lt;br /&gt;// 设置Bar的prototype属性为Foo的实例对象&lt;br /&gt;Bar.prototype = new Foo();&lt;br /&gt;Bar.prototype.foo = 'Hello World';&lt;br /&gt;&lt;br /&gt;// 修正Bar.prototype.constructor为Bar本身&lt;br /&gt;Bar.prototype.constructor = Bar;&lt;br /&gt;&lt;br /&gt;var test = new Bar() // 创建Bar的一个新实例&lt;br /&gt;&lt;br /&gt;// 原型链&lt;br /&gt;test [Bar的实例]&lt;br /&gt;&amp;nbsp; &amp;nbsp; Bar.prototype [Foo的实例] &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; { foo: 'Hello World' }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Foo.prototype&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {method: ...};&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Object.prototype&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {toString: ... /* etc. */};&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面的例子中，&lt;code&gt;test&lt;/code&gt; 对象从 &lt;code&gt;Bar.prototype&lt;/code&gt; 和 &lt;code&gt;Foo.prototype&lt;/code&gt; 继承下来；因此， 它能访问 &lt;code&gt;Foo&lt;/code&gt; 的原型方法 &lt;code&gt;method&lt;/code&gt;。同时，它也能够访问&lt;strong&gt;那个&lt;/strong&gt;定义在原型上的 &lt;code&gt;Foo&lt;/code&gt; 实例属性 &lt;code&gt;value&lt;/code&gt;。 需要注意的是 &lt;code&gt;new Bar()&lt;/code&gt; &lt;strong&gt;不会&lt;/strong&gt;创造出一个新的 &lt;code&gt;Foo&lt;/code&gt; 实例，而是 重复使用它原型上的那个实例；因此，所有的 &lt;code&gt;Bar&lt;/code&gt; 实例都会共享&lt;strong&gt;相同&lt;/strong&gt;的 &lt;code&gt;value&lt;/code&gt; 属性。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; &lt;strong&gt;不要&lt;/strong&gt;使用 &lt;code&gt;Bar.prototype = Foo&lt;/code&gt;，因为这不会执行 &lt;code&gt;Foo&lt;/code&gt; 的原型，而是指向函数 &lt;code&gt;Foo&lt;/code&gt;。   因此原型链将会回溯到 &lt;code&gt;Function.prototype&lt;/code&gt; 而不是 &lt;code&gt;Foo.prototype&lt;/code&gt;，因此 &lt;code&gt;method&lt;/code&gt; 将不会在 Bar 的原型链上。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;属性查找&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;当查找一个对象的属性时，JavaScript 会&lt;strong&gt;向上&lt;/strong&gt;遍历原型链，直到找到给定名称的属性为止。&lt;/p&gt;  &lt;p&gt;到查找到达原型链的顶部 - 也就是 &lt;code&gt;Object.prototype&lt;/code&gt; - 但是仍然没有找到指定的属性，就会返回 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#core.undefined"&gt;undefined&lt;/a&gt;。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;原型属性&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;当原型属性用来创建原型链时，可以把&lt;strong&gt;任何&lt;/strong&gt;类型的值赋给它（prototype）。 然而将原子类型赋给 prototype 的操作将会被忽略。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Foo() {}&lt;br /&gt;Foo.prototype = 1; // 无效&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;而将对象赋值给 prototype，正如上面的例子所示，将会动态的创建原型链。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;性能&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;如果一个属性在原型链的上端，则对于查找时间将带来不利影响。特别的，试图获取一个不存在的属性将会遍历整个原型链。&lt;/p&gt;  &lt;p&gt;并且，当使用 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.forinloop"&gt;&lt;code&gt;for in&lt;/code&gt;&lt;/a&gt; 循环遍历对象的属性时，原型链上的&lt;strong&gt;所有&lt;/strong&gt;属性都将被访问。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;扩展内置类型的原型&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;一个错误特性被经常使用，那就是扩展 &lt;code&gt;Object.prototype&lt;/code&gt; 或者其他内置类型的原型对象。&lt;/p&gt;  &lt;p&gt;这种技术被称之为 &lt;a href="http://en.wikipedia.org/wiki/Monkey_patch"&gt;monkey patching&lt;/a&gt; 并且会破坏&lt;em&gt;封装&lt;/em&gt;。虽然它被广泛的应用到一些 JavaScript 类库中比如 &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt;, 但是我仍然不认为为内置类型添加一些&lt;em&gt;非标准&lt;/em&gt;的函数是个好主意。&lt;/p&gt;  &lt;p&gt;扩展内置类型的&lt;strong&gt;唯一&lt;/strong&gt;理由是为了和新的 JavaScript 保持一致，比如 &lt;a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach"&gt;&lt;code&gt;Array.forEach&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;这是编程领域常用的一种方式，称之为 &lt;a href="http://en.wikipedia.org/wiki/Backport"&gt;Backport&lt;/a&gt;，也就是将新的补丁添加到老版本中。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;在写复杂的 JavaScript 应用之前，充分理解原型链继承的工作方式是每个 JavaScript 程序员&lt;strong&gt;必修&lt;/strong&gt;的功课。 要提防原型链过长带来的性能问题，并知道如何通过缩短原型链来提高性能。 更进一步，绝对&lt;strong&gt;不要&lt;/strong&gt;扩展内置类型的原型，除非是为了和新的 JavaScript 引擎兼容。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="object.hasownproperty"&gt;&lt;p&gt;&lt;strong&gt;﻿&lt;code&gt;hasOwnProperty&lt;/code&gt; 函数&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;为了判断一个对象是否包含&lt;em&gt;自定义&lt;/em&gt;属性而&lt;em&gt;不是&lt;/em&gt;&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.prototype"&gt;原型链&lt;/a&gt;上的属性， 我们需要使用继承自 &lt;code&gt;Object.prototype&lt;/code&gt; 的 &lt;code&gt;hasOwnProperty&lt;/code&gt; 方法。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 通过判断一个属性是否 &lt;code&gt;undefined&lt;/code&gt; 是&lt;strong&gt;不够&lt;/strong&gt;的。   因为一个属性可能确实存在，只不过它的值被设置为 &lt;code&gt;undefined&lt;/code&gt;。&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;&lt;code&gt;hasOwnProperty&lt;/code&gt; 是 JavaScript 中唯一一个处理属性但是&lt;strong&gt;不&lt;/strong&gt;查找原型链的函数。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 修改Object.prototype&lt;br /&gt;Object.prototype.bar = 1; &lt;br /&gt;var foo = {goo: undefined};&lt;br /&gt;&lt;br /&gt;foo.bar; // 1&lt;br /&gt;'bar' in foo; // true&lt;br /&gt;&lt;br /&gt;foo.hasOwnProperty('bar'); // false&lt;br /&gt;foo.hasOwnProperty('goo'); // true&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;只有 &lt;code&gt;hasOwnProperty&lt;/code&gt; 可以给出正确和期望的结果，这在遍历对象的属性时会很有用。 &lt;strong&gt;没有&lt;/strong&gt;其它方法可以用来排除原型链上的属性，而不是定义在对象&lt;em&gt;自身&lt;/em&gt;上的属性。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;hasOwnProperty&lt;/code&gt; 作为属性&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;JavaScript &lt;strong&gt;不会&lt;/strong&gt;保护 &lt;code&gt;hasOwnProperty&lt;/code&gt; 被非法占用，因此如果一个对象碰巧存在这个属性， 就需要使用&lt;em&gt;外部&lt;/em&gt;的 &lt;code&gt;hasOwnProperty&lt;/code&gt; 函数来获取正确的结果。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = {&lt;br /&gt;&amp;nbsp; &amp;nbsp; hasOwnProperty: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return false;&lt;br /&gt;&amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; bar: 'Here be dragons'&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;foo.hasOwnProperty('bar'); // 总是返回 false&lt;br /&gt;&lt;br /&gt;// 使用其它对象的 hasOwnProperty，并将其上下为设置为foo&lt;br /&gt;{}.hasOwnProperty.call(foo, 'bar'); // true&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;当检查对象上某个属性是否存在时，&lt;code&gt;hasOwnProperty&lt;/code&gt; 是&lt;strong&gt;唯一&lt;/strong&gt;可用的方法。 同时在使用 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.forinloop"&gt;&lt;code&gt;for in&lt;/code&gt; loop&lt;/a&gt; 遍历对象时，推荐&lt;strong&gt;总是&lt;/strong&gt;使用 &lt;code&gt;hasOwnProperty&lt;/code&gt; 方法， 这将会避免&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.prototype"&gt;原型&lt;/a&gt;对象扩展带来的干扰。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="object.forinloop"&gt;&lt;p&gt;&lt;strong&gt;﻿&lt;code&gt;for in&lt;/code&gt; 循环&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;和 &lt;code&gt;in&lt;/code&gt; 操作符一样，&lt;code&gt;for in&lt;/code&gt; 循环同样在查找对象属性时遍历原型链上的所有属性。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; &lt;code&gt;for in&lt;/code&gt; 循环&lt;strong&gt;不会&lt;/strong&gt;遍历那些 &lt;code&gt;enumerable&lt;/code&gt; 设置为 &lt;code&gt;false&lt;/code&gt; 的属性；比如数组的 &lt;code&gt;length&lt;/code&gt; 属性。&lt;/p&gt; &lt;/aside&gt;  &lt;pre&gt;&lt;code&gt;// 修改 Object.prototype&lt;br /&gt;Object.prototype.bar = 1;&lt;br /&gt;&lt;br /&gt;var foo = {moo: 2};&lt;br /&gt;for(var i in foo) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; console.log(i); // 输出两个属性：bar 和 moo&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;由于不可能改变 &lt;code&gt;for in&lt;/code&gt; 自身的行为，因此有必要过滤出那些不希望出现在循环体中的属性， 这可以通过 &lt;code&gt;Object.prototype&lt;/code&gt; 原型上的 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.hasownproperty"&gt;&lt;code&gt;hasOwnProperty&lt;/code&gt;&lt;/a&gt; 函数来完成。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 由于 &lt;code&gt;for in&lt;/code&gt; 总是要遍历整个原型链，因此如果一个对象的继承层次太深的话会影响性能。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;使用 &lt;code&gt;hasOwnProperty&lt;/code&gt; 过滤&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// foo 变量是上例中的&lt;br /&gt;for(var i in foo) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (foo.hasOwnProperty(i)) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(i);&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这个版本的代码是唯一正确的写法。由于我们使用了 &lt;code&gt;hasOwnProperty&lt;/code&gt;，所以这次&lt;strong&gt;只&lt;/strong&gt;输出 &lt;code&gt;moo&lt;/code&gt;。 如果不使用 &lt;code&gt;hasOwnProperty&lt;/code&gt;，则这段代码在原生对象原型（比如 &lt;code&gt;Object.prototype&lt;/code&gt;）被扩展时可能会出错。&lt;/p&gt;  &lt;p&gt;一个广泛使用的类库 &lt;a href="http://www.prototypejs.org/"&gt;Prototype&lt;/a&gt; 就扩展了原生的 JavaScript 对象。 因此，但这个类库被包含在页面中时，不使用 &lt;code&gt;hasOwnProperty&lt;/code&gt; 过滤的 &lt;code&gt;for in&lt;/code&gt; 循环难免会出问题。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;推荐&lt;strong&gt;总是&lt;/strong&gt;使用 &lt;code&gt;hasOwnProperty&lt;/code&gt;。不要对代码运行的环境做任何假设，不要假设原生对象是否已经被扩展了。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;/section&gt;&lt;section id="function"&gt;&lt;header id="function.intro"&gt;&lt;p&gt;&lt;strong&gt;函数&lt;/strong&gt;&lt;/p&gt;&lt;/header&gt;&lt;article id="function.general"&gt;&lt;p&gt;&lt;strong&gt;﻿函数声明与表达式&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;函数是JavaScript中的一等对象，这意味着可以把函数像其它值一样传递。 一个常见的用法是把&lt;em&gt;匿名函数&lt;/em&gt;作为回调函数传递对异步函数中。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;函数声明&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function foo() {}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面的方法会在执行前被 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.scopes"&gt;解析(hoisted)&lt;/a&gt;，因此它存在于当前上下文的&lt;em&gt;任意&lt;/em&gt;一个地方， 即使在函数定义体的上面被调用也是对的。 &lt;/p&gt;  &lt;pre&gt;&lt;code&gt;foo(); // 正常运行，因为foo在代码运行前已经被创建&lt;br /&gt;function foo() {}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;函数赋值表达式&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = function() {};&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这个例子把一个&lt;em&gt;匿名&lt;/em&gt;的函数赋值给变量 &lt;code&gt;foo&lt;/code&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;foo; // 'undefined'&lt;br /&gt;foo(); // 出错：TypeError&lt;br /&gt;var foo = function() {};&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;由于 &lt;code&gt;var&lt;/code&gt; 定义了一个声明语句，对变量 &lt;code&gt;foo&lt;/code&gt; 的解析是在代码运行之前，因此 &lt;code&gt;foo&lt;/code&gt; 变量在代码运行时已经被定义过了。&lt;/p&gt;  &lt;p&gt;但是由于赋值语句只在运行时执行，因此在相应代码执行之前， &lt;code&gt;foo&lt;/code&gt; 的值缺省为 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#core.undefined"&gt;undefined&lt;/a&gt;。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;命名函数的赋值表达式&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;另外一个特殊的情况是将命名函数赋值给一个变量。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = function bar() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; bar(); // 正常运行&lt;br /&gt;}&lt;br /&gt;bar(); // 出错：ReferenceError&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;code&gt;bar&lt;/code&gt; 函数声明外是不可见的，这是因为我们已经把函数赋值给了 &lt;code&gt;foo&lt;/code&gt;； 然而在 &lt;code&gt;bar&lt;/code&gt; 内部依然可见。这是由于 JavaScript 的 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.scopes"&gt;命名处理&lt;/a&gt; 所致， 函数名在函数内&lt;em&gt;总是&lt;/em&gt;可见的。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="function.this"&gt;&lt;p&gt;&lt;strong&gt;﻿&lt;code&gt;this&lt;/code&gt; 的工作原理&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;JavaScript 有一套完全不同于其它语言的对 &lt;code&gt;this&lt;/code&gt; 的处理机制。 在&lt;strong&gt;五&lt;/strong&gt;种不同的情况下 ，&lt;code&gt;this&lt;/code&gt; 指向的各不相同。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;全局范围内&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;this;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;当在全部范围内使用 &lt;code&gt;this&lt;/code&gt;，它将会指向&lt;em&gt;全局&lt;/em&gt;对象。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;浏览器中运行的 JavaScript 脚本，这个全局对象是 &lt;code&gt;window&lt;/code&gt;。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;函数调用&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;foo();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这里 &lt;code&gt;this&lt;/code&gt; 也会指向&lt;em&gt;全局&lt;/em&gt;对象。&lt;/p&gt;  &lt;aside&gt;&lt;p&gt;&lt;strong&gt;ES5 注意:&lt;/strong&gt; 在严格模式下（strict mode），不存在全局变量。   这种情况下 &lt;code&gt;this&lt;/code&gt; 将会是 &lt;code&gt;undefined&lt;/code&gt;。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;方法调用&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;test.foo(); &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这个例子中，&lt;code&gt;this&lt;/code&gt; 指向 &lt;code&gt;test&lt;/code&gt; 对象。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;调用构造函数&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;new foo(); &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;如果函数倾向于和 &lt;code&gt;new&lt;/code&gt; 关键词一块使用，则我们称这个函数是 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.constructors"&gt;构造函数&lt;/a&gt;。 在函数内部，&lt;code&gt;this&lt;/code&gt; 指向&lt;em&gt;新创建&lt;/em&gt;的对象。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;显式的设置 &lt;code&gt;this&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function foo(a, b, c) {}&lt;br /&gt;&lt;br /&gt;var bar = {};&lt;br /&gt;foo.apply(bar, [1, 2, 3]); // 数组将会被扩展，如下所示&lt;br /&gt;foo.call(bar, 1, 2, 3); // 传递到foo的参数是：a = 1, b = 2, c = 3&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;当使用 &lt;code&gt;Function.prototype&lt;/code&gt; 上的 &lt;code&gt;call&lt;/code&gt; 或者 &lt;code&gt;apply&lt;/code&gt; 方法时，函数内的 &lt;code&gt;this&lt;/code&gt; 将会被 &lt;strong&gt;显式设置&lt;/strong&gt;为函数调用的第一个参数。&lt;/p&gt;  &lt;p&gt;因此&lt;em&gt;函数调用&lt;/em&gt;的规则在上例中已经不适用了，在&lt;code&gt;foo&lt;/code&gt; 函数内 &lt;code&gt;this&lt;/code&gt; 被设置成了 &lt;code&gt;bar&lt;/code&gt;。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 在对象的字面声明语法中，&lt;code&gt;this&lt;/code&gt; &lt;strong&gt;不能&lt;/strong&gt;用来指向对象本身。   因此 &lt;code&gt;var obj = {me: this}&lt;/code&gt; 中的 &lt;code&gt;me&lt;/code&gt; 不会指向 &lt;code&gt;obj&lt;/code&gt;，因为 &lt;code&gt;this&lt;/code&gt; 只可能出现在上述的五种情况中。   &lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;这个例子中，如果是在浏览器中运行，&lt;code&gt;obj.me&lt;/code&gt; 等于 &lt;code&gt;window&lt;/code&gt; 对象。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;常见误解&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;尽管大部分的情况都说的过去，不过第一个规则（&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;这里指的应该是第二个规则，也就是直接调用函数时，&lt;code&gt;this&lt;/code&gt; 指向全局对象） 被认为是JavaScript语言另一个错误设计的地方，因为它&lt;strong&gt;从来&lt;/strong&gt;就没有实际的用途。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;Foo.method = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; function test() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // this 将会被设置为全局对象（译者注：浏览器环境中也就是 window 对象）&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; test();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;一个常见的误解是 &lt;code&gt;test&lt;/code&gt; 中的 &lt;code&gt;this&lt;/code&gt; 将会指向 &lt;code&gt;Foo&lt;/code&gt; 对象，实际上&lt;strong&gt;不是&lt;/strong&gt;这样子的。&lt;/p&gt;  &lt;p&gt;为了在 &lt;code&gt;test&lt;/code&gt; 中获取对 &lt;code&gt;Foo&lt;/code&gt; 对象的引用，我们需要在 &lt;code&gt;method&lt;/code&gt; 函数内部创建一个局部变量指向 &lt;code&gt;Foo&lt;/code&gt; 对象。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;Foo.method = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var that = this;&lt;br /&gt;&amp;nbsp; &amp;nbsp; function test() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 使用 that 来指向 Foo 对象&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; test();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;code&gt;that&lt;/code&gt; 只是我们随意起的名字，不过这个名字被广泛的用来指向外部的 &lt;code&gt;this&lt;/code&gt; 对象。 在 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.closures"&gt;闭包&lt;/a&gt; 一节，我们可以看到 &lt;code&gt;that&lt;/code&gt; 可以作为参数传递。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;方法的赋值表达式&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;另一个看起来奇怪的地方是函数别名，也就是将一个方法&lt;strong&gt;赋值&lt;/strong&gt;给一个变量。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var test = someObject.methodTest;&lt;br /&gt;test();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上例中，&lt;code&gt;test&lt;/code&gt; 就像一个普通的函数被调用；因此，函数内的 &lt;code&gt;this&lt;/code&gt; 将不再被指向到 &lt;code&gt;someObject&lt;/code&gt; 对象。&lt;/p&gt;  &lt;p&gt;虽然 &lt;code&gt;this&lt;/code&gt; 的晚绑定特性似乎并不友好，但是这确实&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.prototype"&gt;基于原型继承&lt;/a&gt;赖以生存的土壤。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Foo() {}&lt;br /&gt;Foo.prototype.method = function() {};&lt;br /&gt;&lt;br /&gt;function Bar() {}&lt;br /&gt;Bar.prototype = Foo.prototype;&lt;br /&gt;&lt;br /&gt;new Bar().method();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;当 &lt;code&gt;method&lt;/code&gt; 被调用时，&lt;code&gt;this&lt;/code&gt; 将会指向 &lt;code&gt;Bar&lt;/code&gt; 的实例对象。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="function.closures"&gt;&lt;p&gt;&lt;strong&gt;﻿闭包和引用&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;闭包是 JavaScript 一个非常重要的特性，这意味着当前作用域&lt;strong&gt;总是&lt;/strong&gt;能够访问外部作用域中的变量。 因为 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.scopes"&gt;函数&lt;/a&gt; 是 JavaScript 中唯一拥有自身作用域的结构，因此闭包的创建依赖于函数。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;模拟私有变量&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Counter(start) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var count = start;&lt;br /&gt;&amp;nbsp; &amp;nbsp; return {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; increment: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; count++;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; },&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; get: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return count;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var foo = Counter(4);&lt;br /&gt;foo.increment();&lt;br /&gt;foo.get(); // 5&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这里，&lt;code&gt;Counter&lt;/code&gt; 函数返回两个闭包，函数 &lt;code&gt;increment&lt;/code&gt; 和函数 &lt;code&gt;get&lt;/code&gt;。 这两个函数都维持着 对外部作用域 &lt;code&gt;Counter&lt;/code&gt; 的引用，因此总可以访问此作用域内定义的变量 &lt;code&gt;count&lt;/code&gt;.&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;为什么不可以在外部访问私有变量&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;因为 JavaScript 中不可以对作用域进行引用或赋值，因此没有办法在外部访问 &lt;code&gt;count&lt;/code&gt; 变量。 唯一的途径就是通过那两个闭包。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = new Counter(4);&lt;br /&gt;foo.hack = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; count = 1337;&lt;br /&gt;};&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面的代码&lt;strong&gt;不会&lt;/strong&gt;改变定义在 &lt;code&gt;Counter&lt;/code&gt; 作用域中的 &lt;code&gt;count&lt;/code&gt; 变量的值，因为 &lt;code&gt;foo.hack&lt;/code&gt; 没有 定义在那个&lt;strong&gt;作用域&lt;/strong&gt;内。它将会创建或者覆盖&lt;em&gt;全局&lt;/em&gt;变量 &lt;code&gt;count&lt;/code&gt;。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;循环中的闭包&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;一个常见的错误出现在循环中使用闭包，假设我们需要在每次循环中调用循环序号&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;for(var i = 0; i &amp;lt; 10; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; setTimeout(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(i); &amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }, 1000);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面的代码不会输出数字 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;9&lt;/code&gt;，而是会输出数字 &lt;code&gt;10&lt;/code&gt; 十次。&lt;/p&gt;  &lt;p&gt;当 &lt;code&gt;console.log&lt;/code&gt; 被调用的时候，&lt;em&gt;匿名&lt;/em&gt;函数保持对外部变量 &lt;code&gt;i&lt;/code&gt; 的引用，此时 &lt;code&gt;for&lt;/code&gt;循环已经结束， &lt;code&gt;i&lt;/code&gt; 的值被修改成了 &lt;code&gt;10&lt;/code&gt;. &lt;/p&gt;  &lt;p&gt;为了得到想要的结果，需要在每次循环中创建变量 &lt;code&gt;i&lt;/code&gt; 的&lt;strong&gt;拷贝&lt;/strong&gt;。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;避免引用错误&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;为了正确的获得循环序号，最好使用 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.scopes"&gt;匿名包裹器&lt;/a&gt;（&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;其实就是我们通常说的自执行匿名函数）。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;for(var i = 0; i &amp;lt; 10; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; (function(e) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; setTimeout(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(e); &amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }, 1000);&lt;br /&gt;&amp;nbsp; &amp;nbsp; })(i);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;外部的匿名函数会立即执行，并把 &lt;code&gt;i&lt;/code&gt; 作为它的参数，此时函数内 &lt;code&gt;e&lt;/code&gt; 变量就拥有了 &lt;code&gt;i&lt;/code&gt; 的一个拷贝。&lt;/p&gt;  &lt;p&gt;当传递给 &lt;code&gt;setTimeout&lt;/code&gt; 的匿名函数执行时，它就拥有了对 &lt;code&gt;e&lt;/code&gt; 的引用，而这个值是&lt;strong&gt;不会&lt;/strong&gt;被循环改变的。&lt;/p&gt;  &lt;p&gt;有另一个方法完成同样的工作；那就是从匿名包装器中返回一个函数。这和上面的代码效果一样。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;for(var i = 0; i &amp;lt; 10; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; setTimeout((function(e) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(e);&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; })(i), 1000)&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="function.arguments"&gt;&lt;p&gt;&lt;strong&gt;﻿&lt;code&gt;arguments&lt;/code&gt; 对象&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;JavaScript 中每个函数内都能访问一个特别变量 &lt;code&gt;arguments&lt;/code&gt;。这个变量维护着所有传递到这个函数中的参数列表。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 由于 &lt;code&gt;arguments&lt;/code&gt; 已经被定义为函数内的一个变量。   因此通过 &lt;code&gt;var&lt;/code&gt; 关键字定义 &lt;code&gt;arguments&lt;/code&gt; 或者将 &lt;code&gt;arguments&lt;/code&gt; 声明为一个形式参数，   都将导致原生的 &lt;code&gt;arguments&lt;/code&gt; 不会被创建。&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;&lt;code&gt;arguments&lt;/code&gt; 变量&lt;strong&gt;不是&lt;/strong&gt;一个数组（&lt;code&gt;Array&lt;/code&gt;）。 尽管在语法上它有数组相关的属性 &lt;code&gt;length&lt;/code&gt;，但它不从 &lt;code&gt;Array.prototype&lt;/code&gt; 继承，实际上它是一个对象（&lt;code&gt;Object&lt;/code&gt;）。&lt;/p&gt;  &lt;p&gt;因此，无法对 &lt;code&gt;arguments&lt;/code&gt; 变量使用标准的数组方法，比如 &lt;code&gt;push&lt;/code&gt;, &lt;code&gt;pop&lt;/code&gt; 或者 &lt;code&gt;slice&lt;/code&gt;。 虽然使用 &lt;code&gt;for&lt;/code&gt; 循环遍历也是可以的，但是为了更好的使用数组方法，最好把它转化为一个真正的数组。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;转化为数组&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;下面的代码将会创建一个新的数组，包含所有 &lt;code&gt;arguments&lt;/code&gt; 对象中的元素。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;Array.prototype.slice.call(arguments);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这个转化比较&lt;strong&gt;慢&lt;/strong&gt;，在性能不好的代码中&lt;strong&gt;不推荐&lt;/strong&gt;这种做法。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;传递参数&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;下面将参数从一个函数传递到另一个函数，是推荐的做法。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function foo() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; bar.apply(null, arguments);&lt;br /&gt;}&lt;br /&gt;function bar(a, b, c) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; // do stuff here&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;另一个技巧是同时使用 &lt;code&gt;call&lt;/code&gt; 和 &lt;code&gt;apply&lt;/code&gt;，创建一个快速的解绑定包装器。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Foo() {}&lt;br /&gt;&lt;br /&gt;Foo.prototype.method = function(a, b, c) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; console.log(this, a, b, c);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// Create an unbound version of "method" &lt;br /&gt;// 输入参数为: this, arg1, arg2...argN&lt;br /&gt;Foo.method = function() {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; // 结果: Foo.prototype.method.call(this, arg1, arg2... argN)&lt;br /&gt;&amp;nbsp; &amp;nbsp; Function.call.apply(Foo.prototype.method, arguments);&lt;br /&gt;};&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;&lt;/strong&gt;：上面的 &lt;code&gt;Foo.method&lt;/code&gt; 函数和下面代码的效果是一样的:&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;Foo.method = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var args = Array.prototype.slice.call(arguments);&lt;br /&gt;&amp;nbsp; &amp;nbsp; Foo.prototype.method.apply(args[0], args.slice(1));&lt;br /&gt;};&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;自动更新&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;code&gt;arguments&lt;/code&gt; 对象为其内部属性以及函数形式参数创建 &lt;em&gt;getter&lt;/em&gt; 和 &lt;em&gt;setter&lt;/em&gt; 方法。&lt;/p&gt;  &lt;p&gt;因此，改变形参的值会影响到 &lt;code&gt;arguments&lt;/code&gt; 对象的值，反之亦然。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function foo(a, b, c) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; arguments[0] = 2;&lt;br /&gt;&amp;nbsp; &amp;nbsp; a; // 2 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; b = 4;&lt;br /&gt;&amp;nbsp; &amp;nbsp; arguments[1]; // 4&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var d = c;&lt;br /&gt;&amp;nbsp; &amp;nbsp; d = 9;&lt;br /&gt;&amp;nbsp; &amp;nbsp; c; // 3&lt;br /&gt;}&lt;br /&gt;foo(1, 2, 3);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;性能真相&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;code&gt;arguments&lt;/code&gt; 对象总会被创建，除了两个特殊情况 - 作为局部变量声明和作为形式参数。 而不管它是否有被使用。&lt;/p&gt;  &lt;p&gt;&lt;code&gt;arguments&lt;/code&gt; 的 &lt;em&gt;getters&lt;/em&gt; 和 &lt;em&gt;setters&lt;/em&gt; 方法总会被创佳；因此使用 &lt;code&gt;arguments&lt;/code&gt; 对性能不会有什么影响。 除非是需要对 &lt;code&gt;arguments&lt;/code&gt; 对象的属性进行多次访问。&lt;/p&gt;  &lt;aside&gt;&lt;p&gt;&lt;strong&gt;ES5 提示:&lt;/strong&gt; 这些 &lt;em&gt;getters&lt;/em&gt; 和 &lt;em&gt;setters&lt;/em&gt; 在严格模式下（strict mode）不会被创建。&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;在 &lt;a href="https://developer.mozilla.org/en/JavaScript/Strict_mode"&gt;MDC&lt;/a&gt; 中对 &lt;code&gt;strict mode&lt;/code&gt; 模式下 &lt;code&gt;arguments&lt;/code&gt; 的描述有助于我们的理解，请看下面代码：&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 阐述在 ES5 的严格模式下 `arguments` 的特性&lt;br /&gt;function f(a) {&lt;br /&gt;&amp;nbsp; "use strict";&lt;br /&gt;&amp;nbsp; a = 42;&lt;br /&gt;&amp;nbsp; return [a, arguments[0]];&lt;br /&gt;}&lt;br /&gt;var pair = f(17);&lt;br /&gt;assert(pair[0] === 42);&lt;br /&gt;assert(pair[1] === 17);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;然而，的确有一种情况会显著的影响现代 JavaScript 引擎的性能。这就是使用 &lt;code&gt;arguments.callee&lt;/code&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function foo() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; arguments.callee; // do something with this function object&lt;br /&gt;&amp;nbsp; &amp;nbsp; arguments.callee.caller; // and the calling function object&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function bigLoop() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; for(var i = 0; i &amp;lt; 100000; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo(); // Would normally be inlined...&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面代码中，&lt;code&gt;foo&lt;/code&gt; 不再是一个单纯的内联函数 &lt;a href="http://en.wikipedia.org/wiki/Inlining"&gt;inlining&lt;/a&gt;（&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;&lt;/strong&gt;：这里指的是解析器可以做内联处理）， 因为它需要知道它自己和它的调用者。 这不仅抵消了内联函数带来的性能提升，而且破坏了封装，因此现在函数可能要依赖于特定的上下文。&lt;/p&gt;  &lt;p&gt;因此&lt;strong&gt;强烈&lt;/strong&gt;建议大家&lt;strong&gt;不要&lt;/strong&gt;使用 &lt;code&gt;arguments.callee&lt;/code&gt; 和它的属性。&lt;/p&gt;  &lt;aside&gt;&lt;p&gt;&lt;strong&gt;ES5 提示:&lt;/strong&gt; 在严格模式下，&lt;code&gt;arguments.callee&lt;/code&gt; 会报错 &lt;code&gt;TypeError&lt;/code&gt;，因为它已经被废除了。&lt;/p&gt; &lt;/aside&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="function.constructors"&gt;&lt;p&gt;&lt;strong&gt;﻿构造函数&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;JavaScript 中的构造函数和其它语言中的构造函数是不同的。 通过 &lt;code&gt;new&lt;/code&gt; 关键字方式调用的函数都被认为是构造函数。&lt;/p&gt;  &lt;p&gt;在构造函数内部 - 也就是被调用的函数内 - &lt;code&gt;this&lt;/code&gt; 指向新创建的对象 &lt;code&gt;Object&lt;/code&gt;。 这个&lt;strong&gt;新创建&lt;/strong&gt;的对象的 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.prototype"&gt;&lt;code&gt;prototype&lt;/code&gt;&lt;/a&gt; 被指向到构造函数的 &lt;code&gt;prototype&lt;/code&gt;。&lt;/p&gt;  &lt;p&gt;如果被调用的函数没有显式的 &lt;code&gt;return&lt;/code&gt; 表达式，则隐式的会返回 &lt;code&gt;this&lt;/code&gt; 对象 - 也就是新创建的对象。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Foo() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.bla = 1;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Foo.prototype.test = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; console.log(this.bla);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;var test = new Foo();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面代码把 &lt;code&gt;Foo&lt;/code&gt; 作为构造函数调用，并设置新创建对象的 &lt;code&gt;prototype&lt;/code&gt; 为 &lt;code&gt;Foo.prototype&lt;/code&gt;。&lt;/p&gt;  &lt;p&gt;显式的 &lt;code&gt;return&lt;/code&gt; 表达式将会影响返回结果，但&lt;strong&gt;仅限&lt;/strong&gt;于返回的是一个对象。                                 &lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Bar() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return 2;&lt;br /&gt;}&lt;br /&gt;new Bar(); // 返回新创建的对象&lt;br /&gt;&lt;br /&gt;function Test() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.value = 2;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; return {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo: 1&lt;br /&gt;&amp;nbsp; &amp;nbsp; };&lt;br /&gt;}&lt;br /&gt;new Test(); // 返回的对象&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;&lt;code&gt;new Bar()&lt;/code&gt; 返回的是新创建的对象，而不是数字的字面值 2。 因此 &lt;code&gt;new Bar().constructor === Bar&lt;/code&gt;，但是如果返回的是数字对象，结果就不同了，如下所示&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Bar() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return new Number(2);&lt;br /&gt;}&lt;br /&gt;new Bar().constructor === Number&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;这里得到的 &lt;code&gt;new Test()&lt;/code&gt;是函数返回的对象，而不是通过&lt;code&gt;new&lt;/code&gt;关键字新创建的对象，因此：&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;(new Test()).value === undefined&lt;br /&gt;(new Test()).foo === 1&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;如果 &lt;code&gt;new&lt;/code&gt; 被遗漏了，则函数&lt;strong&gt;不会&lt;/strong&gt;返回新创建的对象。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Foo() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.bla = 1; // 获取设置全局参数&lt;br /&gt;}&lt;br /&gt;Foo(); // undefined&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;虽然上例在有些情况下也能正常运行，但是由于 JavaScript 中 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.this"&gt;&lt;code&gt;this&lt;/code&gt;&lt;/a&gt; 的工作原理， 这里的 &lt;code&gt;this&lt;/code&gt; 指向&lt;em&gt;全局对象&lt;/em&gt;。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;工厂模式&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;为了不使用 &lt;code&gt;new&lt;/code&gt; 关键字，构造函数必须显式的返回一个值。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Bar() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var value = 1;&lt;br /&gt;&amp;nbsp; &amp;nbsp; return {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; method: function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return value;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;Bar.prototype = {&lt;br /&gt;&amp;nbsp; &amp;nbsp; foo: function() {}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;new Bar();&lt;br /&gt;Bar();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面两种对 &lt;code&gt;Bar&lt;/code&gt; 函数的调用返回的值完全相同，一个新创建的拥有 &lt;code&gt;method&lt;/code&gt; 属性的对象被返回， 其实这里创建了一个&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.closures"&gt;闭包&lt;/a&gt;。&lt;/p&gt;  &lt;p&gt;还需要注意， &lt;code&gt;new Bar()&lt;/code&gt; 并&lt;strong&gt;不会&lt;/strong&gt;改变返回对象的原型（&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;也就是返回对象的原型不会指向 &lt;code&gt;Bar.prototype&lt;/code&gt;）。 因为构造函数的原型会被指向到刚刚创建的新对象，而这里的 &lt;code&gt;Bar&lt;/code&gt; 没有把这个新对象返回（&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：而是返回了一个包含 &lt;code&gt;method&lt;/code&gt; 属性的自定义对象）。 &lt;/p&gt;  &lt;p&gt;在上面的例子中，使用或者不使用 &lt;code&gt;new&lt;/code&gt; 关键字没有功能性的区别。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;上面两种方式创建的对象不能访问 &lt;code&gt;Bar&lt;/code&gt; 原型链上的属性，如下所示：&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var bar1 = new Bar();&lt;br /&gt;typeof(bar1.method); // "function"&lt;br /&gt;typeof(bar1.foo); // "undefined"&lt;br /&gt;&lt;br /&gt;var bar2 = Bar();&lt;br /&gt;typeof(bar2.method); // "function"&lt;br /&gt;typeof(bar2.foo); // "undefined"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;通过工厂模式创建新对象&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;我们常听到的一条忠告是&lt;strong&gt;不要&lt;/strong&gt;使用 &lt;code&gt;new&lt;/code&gt; 关键字来调用函数，因为如果忘记使用它就会导致错误。&lt;/p&gt;  &lt;p&gt;为了创建新对象，我们可以创建一个工厂方法，并且在方法内构造一个新对象。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Foo() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var obj = {};&lt;br /&gt;&amp;nbsp; &amp;nbsp; obj.value = 'blub';&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var private = 2;&lt;br /&gt;&amp;nbsp; &amp;nbsp; obj.someMethod = function(value) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.value = value;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; obj.getPrivate = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return private;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; return obj;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;虽然上面的方式比起 &lt;code&gt;new&lt;/code&gt; 的调用方式不容易出错，并且可以充分利用&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.closures"&gt;私有变量&lt;/a&gt;带来的便利， 但是随之而来的是一些不好的地方。&lt;/p&gt;  &lt;ol&gt;&lt;li&gt;会占用更多的内存，因为新创建的对象&lt;strong&gt;不能&lt;/strong&gt;共享原型上的方法。&lt;/li&gt;&lt;li&gt;为了实现继承，工厂方法需要从另外一个对象拷贝所有属性，或者把一个对象作为新创建对象的原型。&lt;/li&gt;&lt;li&gt;放弃原型链仅仅是因为防止遗漏 &lt;code&gt;new&lt;/code&gt; 带来的问题，这似乎和语言本身的思想相违背。&lt;/li&gt;&lt;/ol&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;虽然遗漏 &lt;code&gt;new&lt;/code&gt; 关键字可能会导致问题，但这并&lt;strong&gt;不是&lt;/strong&gt;放弃使用原型链的借口。 最终使用哪种方式取决于应用程序的需求，选择一种代码书写风格并&lt;strong&gt;坚持&lt;/strong&gt;下去才是最重要的。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="function.scopes"&gt;&lt;p&gt;&lt;strong&gt;﻿作用域与命名空间&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;尽管 JavaScript 支持一对花括号创建的代码段，但是并不支持块级作用域； 而仅仅支持 &lt;em&gt;函数作用域&lt;/em&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function test() { // 一个作用域&lt;br /&gt;&amp;nbsp; &amp;nbsp; for(var i = 0; i &amp;lt; 10; i++) { // 不是一个作用域&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // count&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; console.log(i); // 10&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 如果不是在赋值语句中，而是在 return 表达式或者函数参数中，&lt;code&gt;{...}&lt;/code&gt; 将会作为代码段解析，   而不是作为对象的字面语法解析。如果考虑到 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#core.semicolon"&gt;自动分号插入&lt;/a&gt;，这可能会导致一些不易察觉的错误。&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;如果 &lt;code&gt;return&lt;/code&gt; 对象的左括号和 &lt;code&gt;return&lt;/code&gt; 不在一行上就会出错。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 译者注：下面输出 undefined&lt;br /&gt;function add(a, b) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; return &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; a + b;&lt;br /&gt;}&lt;br /&gt;console.log(add(1, 2));&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;JavaScript 中没有显式的命名空间定义，这就意味着所有对象都定义在一个&lt;em&gt;全局共享&lt;/em&gt;的命名空间下面。&lt;/p&gt;  &lt;p&gt;每次引用一个变量，JavaScript 会向上遍历整个作用域直到找到这个变量为止。 如果到达全局作用域但是这个变量仍未找到，则会抛出 &lt;code&gt;ReferenceError&lt;/code&gt; 异常。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;隐式的全局变量&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 脚本 A&lt;br /&gt;foo = '42';&lt;br /&gt;&lt;br /&gt;// 脚本 B&lt;br /&gt;var foo = '42'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面两段脚本效果&lt;strong&gt;不同&lt;/strong&gt;。脚本 A 在&lt;em&gt;全局&lt;/em&gt;作用域内定义了变量 &lt;code&gt;foo&lt;/code&gt;，而脚本 B 在&lt;em&gt;当前&lt;/em&gt;作用域内定义变量 &lt;code&gt;foo&lt;/code&gt;。&lt;/p&gt;  &lt;p&gt;再次强调，上面的效果&lt;strong&gt;完全不同&lt;/strong&gt;，不使用 &lt;code&gt;var&lt;/code&gt; 声明变量将会导致隐式的全局变量产生。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 全局作用域&lt;br /&gt;var foo = 42;&lt;br /&gt;function test() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; // 局部作用域&lt;br /&gt;&amp;nbsp; &amp;nbsp; foo = 21;&lt;br /&gt;}&lt;br /&gt;test();&lt;br /&gt;foo; // 21&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;在函数 &lt;code&gt;test&lt;/code&gt; 内不使用 &lt;code&gt;var&lt;/code&gt; 关键字声明 &lt;code&gt;foo&lt;/code&gt; 变量将会覆盖外部的同名变量。 起初这看起来并不是大问题，但是当有成千上万行代码时，不使用 &lt;code&gt;var&lt;/code&gt; 声明变量将会带来难以跟踪的 BUG。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 全局作用域&lt;br /&gt;var items = [/* 数组 */];&lt;br /&gt;for(var i = 0; i &amp;lt; 10; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; subLoop();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function subLoop() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; // subLoop 函数作用域&lt;br /&gt;&amp;nbsp; &amp;nbsp; for(i = 0; i &amp;lt; 10; i++) { // 没有使用 var 声明变量&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 干活&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;外部循环在第一次调用 &lt;code&gt;subLoop&lt;/code&gt; 之后就会终止，因为 &lt;code&gt;subLoop&lt;/code&gt; 覆盖了全局变量 &lt;code&gt;i&lt;/code&gt;。 在第二个 &lt;code&gt;for&lt;/code&gt; 循环中使用 &lt;code&gt;var&lt;/code&gt; 声明变量可以避免这种错误。 声明变量时&lt;strong&gt;绝对不要&lt;/strong&gt;遗漏 &lt;code&gt;var&lt;/code&gt; 关键字，除非这就是&lt;em&gt;期望&lt;/em&gt;的影响外部作用域的行为。 &lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;局部变量&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;JavaScript 中局部变量只可能通过两种方式声明，一个是作为&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function"&gt;函数&lt;/a&gt;参数，另一个是通过 &lt;code&gt;var&lt;/code&gt; 关键字声明。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 全局变量&lt;br /&gt;var foo = 1;&lt;br /&gt;var bar = 2;&lt;br /&gt;var i = 2;&lt;br /&gt;&lt;br /&gt;function test(i) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; // 函数 test 内的局部作用域&lt;br /&gt;&amp;nbsp; &amp;nbsp; i = 5;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var foo = 3;&lt;br /&gt;&amp;nbsp; &amp;nbsp; bar = 4;&lt;br /&gt;}&lt;br /&gt;test(10);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;code&gt;foo&lt;/code&gt; 和 &lt;code&gt;i&lt;/code&gt; 是函数 &lt;code&gt;test&lt;/code&gt; 内的局部变量，而对 &lt;code&gt;bar&lt;/code&gt; 的赋值将会覆盖全局作用域内的同名变量。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;变量声明提升（Hoisting）&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;JavaScript 会&lt;strong&gt;提升&lt;/strong&gt;变量声明。这意味着 &lt;code&gt;var&lt;/code&gt; 表达式和 &lt;code&gt;function&lt;/code&gt; 声明都将会被提升到当前作用域的顶部。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;bar();&lt;br /&gt;var bar = function() {};&lt;br /&gt;var someValue = 42;&lt;br /&gt;&lt;br /&gt;test();&lt;br /&gt;function test(data) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (false) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; goo = 1;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; } else {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var goo = 2;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; for(var i = 0; i &amp;lt; 100; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var e = data[i];&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面代码在运行之前将会被转化。JavaScript 将会把 &lt;code&gt;var&lt;/code&gt; 表达式和 &lt;code&gt;function&lt;/code&gt; 声明提升到当前作用域的顶部。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// var 表达式被移动到这里&lt;br /&gt;var bar, someValue; // 缺省值是 'undefined'&lt;br /&gt;&lt;br /&gt;// 函数声明也会提升&lt;br /&gt;function test(data) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var goo, i, e; // 没有块级作用域，这些变量被移动到函数顶部&lt;br /&gt;&amp;nbsp; &amp;nbsp; if (false) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; goo = 1;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; } else {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; goo = 2;&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; for(i = 0; i &amp;lt; 100; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; e = data[i];&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;bar(); // 出错：TypeError，因为 bar 依然是 'undefined'&lt;br /&gt;someValue = 42; // 赋值语句不会被提升规则（hoisting）影响&lt;br /&gt;bar = function() {};&lt;br /&gt;&lt;br /&gt;test();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;没有块级作用域不仅导致 &lt;code&gt;var&lt;/code&gt; 表达式被从循环内移到外部，而且使一些 &lt;code&gt;if&lt;/code&gt; 表达式更难看懂。&lt;/p&gt;  &lt;p&gt;在原来代码中，&lt;code&gt;if&lt;/code&gt; 表达式看起来修改了&lt;em&gt;全部变量&lt;/em&gt; &lt;code&gt;goo&lt;/code&gt;，实际上在提升规则被应用后，却是在修改&lt;em&gt;局部变量&lt;/em&gt;。&lt;/p&gt;  &lt;p&gt;如果没有提升规则（hoisting）的知识，下面的代码看起来会抛出异常 &lt;code&gt;ReferenceError&lt;/code&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 检查 SomeImportantThing 是否已经被初始化&lt;br /&gt;if (!SomeImportantThing) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var SomeImportantThing = {};&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;实际上，上面的代码正常运行，因为 &lt;code&gt;var&lt;/code&gt; 表达式会被提升到&lt;em&gt;全局作用域&lt;/em&gt;的顶部。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var SomeImportantThing;&lt;br /&gt;&lt;br /&gt;// 其它一些代码，可能会初始化 SomeImportantThing，也可能不会&lt;br /&gt;&lt;br /&gt;// 检查是否已经被初始化&lt;br /&gt;if (!SomeImportantThing) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; SomeImportantThing = {};&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;在 Nettuts+ 网站有一篇介绍 hoisting 的&lt;a href="http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/"&gt;文章&lt;/a&gt;，其中的代码很有启发性。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 译者注：来自 Nettuts+ 的一段代码，生动的阐述了 JavaScript 中变量声明提升规则&lt;br /&gt;var myvar = 'my value'; &amp;nbsp;&lt;br /&gt;&lt;br /&gt;(function() { &amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; alert(myvar); // undefined &amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var myvar = 'local value'; &amp;nbsp;&lt;br /&gt;})(); &amp;nbsp;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;名称解析顺序&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;JavaScript 中的所有作用域，包括&lt;em&gt;全局作用域&lt;/em&gt;，都有一个特别的名称 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.this"&gt;&lt;code&gt;this&lt;/code&gt;&lt;/a&gt; 指向当前对象。&lt;/p&gt;  &lt;p&gt;函数作用域内也有默认的变量 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.arguments"&gt;&lt;code&gt;arguments&lt;/code&gt;&lt;/a&gt;，其中包含了传递到函数中的参数。&lt;/p&gt;  &lt;p&gt;比如，当访问函数内的 &lt;code&gt;foo&lt;/code&gt; 变量时，JavaScript 会按照下面顺序查找：&lt;/p&gt;  &lt;ol&gt;&lt;li&gt;当前作用域内是否有 &lt;code&gt;var foo&lt;/code&gt; 的定义。&lt;/li&gt;&lt;li&gt;函数形式参数是否有使用 &lt;code&gt;foo&lt;/code&gt; 名称的。&lt;/li&gt;&lt;li&gt;函数自身是否叫做 &lt;code&gt;foo&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;回溯到上一级作用域，然后从 &lt;strong&gt;#1&lt;/strong&gt; 重新开始。&lt;/li&gt;&lt;/ol&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 自定义 &lt;code&gt;arguments&lt;/code&gt; 参数将会阻止原生的 &lt;code&gt;arguments&lt;/code&gt; 对象的创建。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;命名空间&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中，这可以通过 &lt;em&gt;匿名包装器&lt;/em&gt; 轻松解决。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; // 函数创建一个命名空间&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; window.foo = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 对外公开的函数，创建了闭包&lt;br /&gt;&amp;nbsp; &amp;nbsp; };&lt;br /&gt;&lt;br /&gt;})(); // 立即执行此匿名函数&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;匿名函数被认为是 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function"&gt;表达式&lt;/a&gt;；因此为了可调用性，它们首先会被执行。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;( // 小括号内的函数首先被执行&lt;br /&gt;function() {}&lt;br /&gt;) // 并且返回函数对象&lt;br /&gt;() // 调用上面的执行结果，也就是函数对象&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;有一些其他的调用函数表达式的方法，比如下面的两种方式语法不同，但是效果一模一样。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 另外两种方式&lt;br /&gt;+function(){}();&lt;br /&gt;(function(){}());&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;推荐使用&lt;em&gt;匿名包装器&lt;/em&gt;（&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;也就是自执行的匿名函数）来创建命名空间。这样不仅可以防止命名冲突， 而且有利于程序的模块化。&lt;/p&gt;  &lt;p&gt;另外，使用全局变量被认为是&lt;strong&gt;不好的习惯&lt;/strong&gt;。这样的代码倾向于产生错误和带来高的维护成本。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;/section&gt;&lt;section id="array"&gt;&lt;header id="array.intro"&gt;&lt;p&gt;&lt;strong&gt;数组&lt;/strong&gt;&lt;/p&gt;&lt;/header&gt;&lt;article id="array.general"&gt;&lt;p&gt;&lt;strong&gt;﻿数组遍历与属性&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;虽然在 JavaScript 中数组是是对象，但是没有好的理由去使用 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.forinloop"&gt;&lt;code&gt;for in&lt;/code&gt; 循环&lt;/a&gt; 遍历数组。 相反，有一些好的理由&lt;strong&gt;不去&lt;/strong&gt;使用 &lt;code&gt;for in&lt;/code&gt; 遍历数组。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; JavaScript 中数组&lt;strong&gt;不是&lt;/strong&gt; &lt;em&gt;关联数组&lt;/em&gt;。   JavaScript 中只有&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.general"&gt;对象&lt;/a&gt; 来管理键值的对应关系。但是关联数组是&lt;strong&gt;保持&lt;/strong&gt;顺序的，而对象&lt;strong&gt;不是&lt;/strong&gt;。&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;由于 &lt;code&gt;for in&lt;/code&gt; 循环会枚举原型链上的所有属性，唯一过滤这些属性的方式是使用 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.hasownproperty"&gt;&lt;code&gt;hasOwnProperty&lt;/code&gt;&lt;/a&gt; 函数， 因此会比普通的 &lt;code&gt;for&lt;/code&gt; 循环慢上好多倍。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;遍历&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;为了达到遍历数组的最佳性能，推荐使用经典的 &lt;code&gt;for&lt;/code&gt; 循环。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var list = [1, 2, 3, 4, 5, ...... 100000000];&lt;br /&gt;for(var i = 0, l = list.length; i &amp;lt; l; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; console.log(list[i]);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面代码有一个处理，就是通过 &lt;code&gt;l = list.length&lt;/code&gt; 来缓存数组的长度。&lt;/p&gt;  &lt;p&gt;虽然 &lt;code&gt;length&lt;/code&gt; 是数组的一个属性，但是在每次循环中访问它还是有性能开销。 &lt;strong&gt;可能&lt;/strong&gt;最新的 JavaScript 引擎在这点上做了优化，但是我们没法保证自己的代码是否运行在这些最近的引擎之上。&lt;/p&gt;  &lt;p&gt;实际上，不使用缓存数组长度的方式比缓存版本要慢很多。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;length&lt;/code&gt; 属性&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;code&gt;length&lt;/code&gt; 属性的 &lt;em&gt;getter&lt;/em&gt; 方式会简单的返回数组的长度，而 &lt;em&gt;setter&lt;/em&gt; 方式会&lt;strong&gt;截断&lt;/strong&gt;数组。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = [1, 2, 3, 4, 5, 6];&lt;br /&gt;foo.length = 3;&lt;br /&gt;foo; // [1, 2, 3]&lt;br /&gt;&lt;br /&gt;foo.length = 6;&lt;br /&gt;foo; // [1, 2, 3]&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;strong&gt;译者注：&lt;/strong&gt; 在 Firebug 中查看此时 &lt;code&gt;foo&lt;/code&gt; 的值是： &lt;code&gt;[1, 2, 3, undefined, undefined, undefined]&lt;/code&gt; 但是这个结果并不准确，如果你在 Chrome 的控制台查看 &lt;code&gt;foo&lt;/code&gt; 的结果，你会发现是这样的： &lt;code&gt;[1, 2, 3]&lt;/code&gt; 因为在 JavaScript 中 &lt;code&gt;undefined&lt;/code&gt; 是一个变量，注意是变量不是关键字，因此上面两个结果的意义是完全不相同的。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 译者注：为了验证，我们来执行下面代码，看序号 5 是否存在于 foo 中。&lt;br /&gt;5 in foo; // 不管在 Firebug 或者 Chrome 都返回 false&lt;br /&gt;foo[5] = undefined;&lt;br /&gt;5 in foo; // 不管在 Firebug 或者 Chrome 都返回 true&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;为 &lt;code&gt;length&lt;/code&gt; 设置一个更小的值会截断数组，但是增大 &lt;code&gt;length&lt;/code&gt; 属性值不会对数组产生影响。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;为了更好的性能，推荐使用普通的 &lt;code&gt;for&lt;/code&gt; 循环并缓存数组的 &lt;code&gt;length&lt;/code&gt; 属性。 使用 &lt;code&gt;for in&lt;/code&gt; 遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="array.constructor"&gt;&lt;p&gt;&lt;strong&gt;﻿&lt;code&gt;Array&lt;/code&gt; 构造函数&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;由于 &lt;code&gt;Array&lt;/code&gt; 的构造函数在如何处理参数时有点模棱两可，因此总是推荐使用数组的字面语法 - &lt;code&gt;[]&lt;/code&gt; - 来创建数组。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;[1, 2, 3]; // 结果: [1, 2, 3]&lt;br /&gt;new Array(1, 2, 3); // 结果: [1, 2, 3]&lt;br /&gt;&lt;br /&gt;[3]; // 结果: [3]&lt;br /&gt;new Array(3); // 结果: [] &lt;br /&gt;new Array('3') // 结果: ['3']&lt;br /&gt;&lt;br /&gt;// 译者注：因此下面的代码将会使人很迷惑&lt;br /&gt;new Array(3, 4, 5); // 结果: [3, 4, 5] &lt;br /&gt;new Array(3) // 结果: []，此数组长度为 3&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;译者注：&lt;/strong&gt;这里的模棱两可指的是数组的&lt;a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array"&gt;两种构造函数语法&lt;/a&gt; &lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;由于只有一个参数传递到构造函数中（译者注：指的是 &lt;code&gt;new Array(3);&lt;/code&gt; 这种调用方式），并且这个参数是数字，构造函数会返回一个 &lt;code&gt;length&lt;/code&gt; 属性被设置为此参数的空数组。 需要特别注意的是，此时只有 &lt;code&gt;length&lt;/code&gt; 属性被设置，真正的数组并没有生成。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;译者注：&lt;/strong&gt;在 Firebug 中，你会看到 &lt;code&gt;[undefined, undefined, undefined]&lt;/code&gt;，这其实是不对的。在上一节有详细的分析。&lt;/p&gt; &lt;/aside&gt;  &lt;pre&gt;&lt;code&gt;var arr = new Array(3);&lt;br /&gt;arr[1]; // undefined&lt;br /&gt;1 in arr; // false, 数组还没有生成&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这种优先于设置数组长度属性的做法只在少数几种情况下有用，比如需要循环字符串，可以避免 &lt;code&gt;for&lt;/code&gt; 循环的麻烦。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;new Array(count + 1).join(stringToRepeat);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;译者注：&lt;/strong&gt; &lt;code&gt;new Array(3).join('#')&lt;/code&gt; 将会返回 &lt;code&gt;##&lt;/code&gt;&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁，因此增加了代码的可读性。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;/section&gt;&lt;section id="types"&gt;&lt;header id="types.intro"&gt;&lt;p&gt;&lt;strong&gt;类型&lt;/strong&gt;&lt;/p&gt;&lt;/header&gt;&lt;article id="types.equality"&gt;&lt;p&gt;&lt;strong&gt;﻿相等与比较&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;JavaScript 有两种方式判断两个值是否相等。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;等于操作符&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;等于操作符由两个等号组成：&lt;code&gt;==&lt;/code&gt;&lt;/p&gt;  &lt;p&gt;JavaScript 是&lt;em&gt;弱类型&lt;/em&gt;语言，这就意味着，等于操作符会为了比较两个值而进行&lt;strong&gt;强制类型转换&lt;/strong&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;"" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ==&amp;nbsp;"0" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;==&amp;nbsp;"" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// true&lt;br /&gt;0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;==&amp;nbsp;"0" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // true&lt;br /&gt;false &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;==&amp;nbsp;"false" &amp;nbsp; &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;false &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;==&amp;nbsp;"0" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // true&lt;br /&gt;false &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;==&amp;nbsp;undefined &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;false &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;==&amp;nbsp;null &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// false&lt;br /&gt;null &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ==&amp;nbsp;undefined &amp;nbsp; &amp;nbsp; // true&lt;br /&gt;" \t\r\n" &amp;nbsp; &amp;nbsp;==&amp;nbsp;0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // true&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面的表格展示了强类型转换，这也是使用 &lt;code&gt;==&lt;/code&gt; 被广泛认为是不好编程习惯的主要原因， 由于它的复杂转换规则，会导致难以跟踪的问题。&lt;/p&gt;  &lt;p&gt;此外，强制类型转换也会带来性能消耗，比如一个字符串为了和一个数组进行比较，必须事先被强制转换为数字。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;严格等于操作符&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;严格等于操作符由&lt;strong&gt;三&lt;/strong&gt;个等号组成：&lt;code&gt;===&lt;/code&gt;&lt;/p&gt;  &lt;p&gt;不想普通的等于操作符，严格等于操作符&lt;strong&gt;不会&lt;/strong&gt;进行强制类型转换。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;"" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ===&amp;nbsp;"0" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;===&amp;nbsp;"" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// false&lt;br /&gt;0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;===&amp;nbsp;"0" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;false &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;===&amp;nbsp;"false" &amp;nbsp; &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;false &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;===&amp;nbsp;"0" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;false &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;===&amp;nbsp;undefined &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;false &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;===&amp;nbsp;null &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// false&lt;br /&gt;null &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ===&amp;nbsp;undefined &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;" \t\r\n" &amp;nbsp; &amp;nbsp;===&amp;nbsp;0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面的结果更加清晰并有利于代码的分析。如果两个操作数类型不同就肯定不相等也有助于性能的提升。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;比较对象&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;虽然 &lt;code&gt;==&lt;/code&gt; 和 &lt;code&gt;===&lt;/code&gt; 操作符都是等于操作符，但是当其中有一个操作数为对象时，行为就不同了。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;{} === {}; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;new String('foo') === 'foo'; // false&lt;br /&gt;new Number(10) === 10; &amp;nbsp; &amp;nbsp; &amp;nbsp; // false&lt;br /&gt;var foo = {};&lt;br /&gt;foo === foo; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // true&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这里等于操作符比较的&lt;strong&gt;不是&lt;/strong&gt;值是否相等，而是是否属于同一个&lt;strong&gt;身份&lt;/strong&gt;；也就是说，只有对象的同一个实例才被认为是相等的。 这有点像 Python 中的 &lt;code&gt;is&lt;/code&gt; 和 C 中的指针比较。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;强烈推荐使用&lt;strong&gt;严格等于操作符&lt;/strong&gt;。如果类型需要转换，应该在比较之前&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#types.casting"&gt;显式&lt;/a&gt;的转换， 而不是使用语言本身复杂的强制转换规则。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="types.typeof"&gt;&lt;p&gt;&lt;strong&gt;﻿&lt;code&gt;typeof&lt;/code&gt; 操作符&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;code&gt;typeof&lt;/code&gt; 操作符（和 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#types.instanceof"&gt;&lt;code&gt;instanceof&lt;/code&gt;&lt;/a&gt; 一起）或许是 JavaScript 中最大的设计缺陷， 因为几乎不可能从它们那里得到想要的结果。&lt;/p&gt;  &lt;p&gt;尽管 &lt;code&gt;instanceof&lt;/code&gt; 还有一些极少数的应用场景，&lt;code&gt;typeof&lt;/code&gt; 只有一个实际的应用（&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;这个实际应用是用来检测一个对象是否已经定义或者是否已经赋值）， 而这个应用却&lt;strong&gt;不是&lt;/strong&gt;用来检查对象的类型。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 由于 &lt;code&gt;typeof&lt;/code&gt; 也可以像函数的语法被调用，比如 &lt;code&gt;typeof(obj)&lt;/code&gt;，但这并是一个函数调用。   那两个小括号只是用来计算一个表达式的值，这个返回值会作为 &lt;code&gt;typeof&lt;/code&gt; 操作符的一个操作数。   实际上&lt;strong&gt;不存在&lt;/strong&gt;名为 &lt;code&gt;typeof&lt;/code&gt; 的函数。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;JavaScript 类型表格&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;Value &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Class &amp;nbsp; &amp;nbsp; &amp;nbsp;Type&lt;br /&gt;-------------------------------------&lt;br /&gt;"foo" &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; String &amp;nbsp; &amp;nbsp; string&lt;br /&gt;new String("foo")&amp;nbsp;String &amp;nbsp; &amp;nbsp; object&lt;br /&gt;1.2 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Number &amp;nbsp; &amp;nbsp; number&lt;br /&gt;new Number(1.2) &amp;nbsp; &amp;nbsp; Number &amp;nbsp; &amp;nbsp; object&lt;br /&gt;true &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Boolean &amp;nbsp; &amp;nbsp;boolean&lt;br /&gt;new Boolean(true)&amp;nbsp;Boolean &amp;nbsp; &amp;nbsp;object&lt;br /&gt;new Date() &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Date &amp;nbsp; &amp;nbsp; &amp;nbsp; object&lt;br /&gt;new Error() &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Error &amp;nbsp; &amp;nbsp; &amp;nbsp;object&lt;br /&gt;[1,2,3] &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Array &amp;nbsp; &amp;nbsp; &amp;nbsp;object&lt;br /&gt;new Array(1, 2, 3)&amp;nbsp;Array &amp;nbsp; &amp;nbsp; &amp;nbsp;object&lt;br /&gt;new Function("") &amp;nbsp; &amp;nbsp;Function&amp;nbsp;function&lt;br /&gt;/abc/g &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;RegExp &amp;nbsp; &amp;nbsp; object (function in Nitro/V8)&lt;br /&gt;new RegExp("meow")&amp;nbsp;RegExp &amp;nbsp; &amp;nbsp; object (function in Nitro/V8)&lt;br /&gt;{} &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Object &amp;nbsp; &amp;nbsp; object&lt;br /&gt;new Object() &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Object &amp;nbsp; &amp;nbsp; object&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面表格中，&lt;em&gt;Type&lt;/em&gt; 一列表示 &lt;code&gt;typeof&lt;/code&gt; 操作符的运算结果。可以看到，这个值在大多数情况下都返回 "object"。&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Class&lt;/em&gt; 一列表示对象的内部属性 &lt;code&gt;[[Class]]&lt;/code&gt; 的值。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;JavaScript 标准文档中定义:&lt;/strong&gt; &lt;code&gt;[[Class]]&lt;/code&gt; 的值只可能是下面字符串中的一个：   &lt;code&gt;Arguments&lt;/code&gt;, &lt;code&gt;Array&lt;/code&gt;, &lt;code&gt;Boolean&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Error&lt;/code&gt;,    &lt;code&gt;Function&lt;/code&gt;, &lt;code&gt;JSON&lt;/code&gt;, &lt;code&gt;Math&lt;/code&gt;, &lt;code&gt;Number&lt;/code&gt;, &lt;code&gt;Object&lt;/code&gt;, &lt;code&gt;RegExp&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;为了获取对象的 &lt;code&gt;[[Class]]&lt;/code&gt;，我们需要使用定义在 &lt;code&gt;Object.prototype&lt;/code&gt; 上的方法 &lt;code&gt;toString&lt;/code&gt;。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;对象的类定义&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;JavaScript 标准文档只给出了一种获取 &lt;code&gt;[[Class]]&lt;/code&gt; 值的方法，那就是使用 &lt;code&gt;Object.prototype.toString&lt;/code&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function is(type, obj) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var clas = Object.prototype.toString.call(obj).slice(8, -1);&lt;br /&gt;&amp;nbsp; &amp;nbsp; return obj !== undefined &amp;amp;&amp;amp; obj !== null &amp;amp;&amp;amp; clas === type;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;is('String', 'test'); // true&lt;br /&gt;is('String', new String('test')); // true&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面例子中，&lt;code&gt;Object.prototype.toString&lt;/code&gt; 方法被调用，&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.this"&gt;this&lt;/a&gt; 被设置为了需要获取 &lt;code&gt;[[Class]]&lt;/code&gt; 值的对象。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;&lt;code&gt;Object.prototype.toString&lt;/code&gt; 返回一种标准格式字符串，所以上例可以通过 &lt;code&gt;slice&lt;/code&gt; 截取指定位置的字符串，如下所示：&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;Object.prototype.toString.call([])&amp;nbsp;// "[object Array]"&lt;br /&gt;Object.prototype.toString.call({})&amp;nbsp;// "[object Object]"&lt;br /&gt;Object.prototype.toString.call(2)&amp;nbsp;// "[object Number]"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;aside&gt;&lt;p&gt;&lt;strong&gt;ES5 提示:&lt;/strong&gt; 在 ECMAScript 5 中，为了方便，对 &lt;code&gt;null&lt;/code&gt; 和 &lt;code&gt;undefined&lt;/code&gt; 调用 &lt;code&gt;Object.prototype.toString&lt;/code&gt; 方法，   其返回值由 &lt;code&gt;Object&lt;/code&gt; 变成了 &lt;code&gt;Null&lt;/code&gt; 和 &lt;code&gt;Undefined&lt;/code&gt;。&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;这种变化可以从 IE8 和 Firefox 4 中看出区别，如下所示：&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// IE8&lt;br /&gt;Object.prototype.toString.call(null) &amp;nbsp; &amp;nbsp;// "[object Object]"&lt;br /&gt;Object.prototype.toString.call(undefined)&amp;nbsp;// "[object Object]"&lt;br /&gt;&lt;br /&gt;// Firefox 4&lt;br /&gt;Object.prototype.toString.call(null) &amp;nbsp; &amp;nbsp;// "[object Null]"&lt;br /&gt;Object.prototype.toString.call(undefined)&amp;nbsp;// "[object Undefined]"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;测试为定义变量&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;typeof foo !== 'undefined'&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面代码会检测 &lt;code&gt;foo&lt;/code&gt; 是否已经定义；如果没有定义而直接使用会导致 &lt;code&gt;ReferenceError&lt;/code&gt; 的异常。 这是 &lt;code&gt;typeof&lt;/code&gt; 唯一有用的地方。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;为了检测一个对象的类型，强烈推荐使用 &lt;code&gt;Object.prototype.toString&lt;/code&gt; 方法； 因为这是唯一一个可依赖的方式。正如上面表格所示，&lt;code&gt;typeof&lt;/code&gt; 的一些返回值在标准文档中并未定义， 因此不同的引擎实现可能不同。&lt;/p&gt;  &lt;p&gt;除非为了检测一个变量是否已经定义，我们应尽量避免使用 &lt;code&gt;typeof&lt;/code&gt; 操作符。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="types.instanceof"&gt;&lt;p&gt;&lt;strong&gt;﻿&lt;code&gt;instanceof&lt;/code&gt; 操作符&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;code&gt;instanceof&lt;/code&gt; 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 如果用来比较内置类型，将会和 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#types.typeof"&gt;&lt;code&gt;typeof&lt;/code&gt; 操作符&lt;/a&gt; 一样用处不大。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;比较自定义对象&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Foo() {}&lt;br /&gt;function Bar() {}&lt;br /&gt;Bar.prototype = new Foo();&lt;br /&gt;&lt;br /&gt;new Bar() instanceof Bar; // true&lt;br /&gt;new Bar() instanceof Foo; // true&lt;br /&gt;&lt;br /&gt;// 如果仅仅设置 Bar.prototype 为函数 Foo 本省，而不是 Foo 构造函数的一个实例&lt;br /&gt;Bar.prototype = Foo;&lt;br /&gt;new Bar() instanceof Foo; // false&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;instanceof&lt;/code&gt; 比较内置类型&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;new String('foo') instanceof String; // true&lt;br /&gt;new String('foo') instanceof Object; // true&lt;br /&gt;&lt;br /&gt;'foo' instanceof String; // false&lt;br /&gt;'foo' instanceof Object; // false&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;有一点需要注意，&lt;code&gt;instanceof&lt;/code&gt; 用来比较属于不同 JavaScript 上下文的对象（比如，浏览器中不同的文档结构）时将会出错， 因为它们的构造函数不会是同一个对象。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;code&gt;instanceof&lt;/code&gt; 操作符应该&lt;strong&gt;仅仅&lt;/strong&gt;用来比较来自同一个 JavaScript 上下文的自定义对象。 正如 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#types.typeof"&gt;&lt;code&gt;typeof&lt;/code&gt;&lt;/a&gt; 操作符一样，任何其它的用法都应该是避免的。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="types.casting"&gt;&lt;p&gt;&lt;strong&gt;﻿类型转换&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;JavaScript 是&lt;em&gt;弱类型&lt;/em&gt;语言，所以会在&lt;strong&gt;任何&lt;/strong&gt;可能的情况下应用&lt;em&gt;强制类型转换&lt;/em&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 下面的比较结果是：true&lt;br /&gt;new Number(10) == 10; // Number.toString() 返回的字符串被再次转换为数字&lt;br /&gt;&lt;br /&gt;10 == '10'; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 字符串被转换为数字&lt;br /&gt;10 == '+10 '; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 同上&lt;br /&gt;10 == '010'; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// 同上 &lt;br /&gt;isNaN(null) == false; // null 被转换为数字 0&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 0 当然不是一个 NaN（译者注：否定之否定）&lt;br /&gt;&lt;br /&gt;// 下面的比较结果是：false&lt;br /&gt;10 == 010;&lt;br /&gt;10 == '-10';&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;aside&gt;&lt;p&gt;&lt;strong&gt;ES5 提示:&lt;/strong&gt; 以 &lt;code&gt;0&lt;/code&gt; 开头的数字字面值会被作为八进制数字解析。   而在 ECMAScript 5 严格模式下，这个特性被&lt;strong&gt;移除&lt;/strong&gt;了。&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;为了避免上面复杂的强制类型转换，&lt;strong&gt;强烈&lt;/strong&gt;推荐使用&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#types.equality"&gt;严格的等于操作符&lt;/a&gt;。 虽然这可以避免大部分的问题，但 JavaScript 的弱类型系统仍然会导致一些其它问题。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;内置类型的构造函数&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;内置类型（比如 &lt;code&gt;Number&lt;/code&gt; 和 &lt;code&gt;String&lt;/code&gt;）的构造函数在被调用时，使用或者不使用 &lt;code&gt;new&lt;/code&gt; 的结果完全不同。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;new Number(10) === 10; &amp;nbsp; &amp;nbsp; // False, 对象与数字的比较&lt;br /&gt;Number(10) === 10; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // True, 数字与数字的比较&lt;br /&gt;new Number(10) + 0 === 10; // True, 由于隐式的类型转换&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;使用内置类型 &lt;code&gt;Number&lt;/code&gt; 作为构造函数将会创建一个新的 &lt;code&gt;Number&lt;/code&gt; 对象， 而在不使用 &lt;code&gt;new&lt;/code&gt; 关键字的 &lt;code&gt;Number&lt;/code&gt; 函数更像是一个数字转换器。&lt;/p&gt;  &lt;p&gt;另外，在比较中引入对象的字面值将会导致更加复杂的强制类型转换。&lt;/p&gt;  &lt;p&gt;最好的选择是把要比较的值&lt;strong&gt;显式&lt;/strong&gt;的转换为三种可能的类型之一。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;转换为字符串&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;'' + 10 === '10'; // true&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;将一个值加上空字符串可以轻松转换为字符串类型。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;转换为数字&lt;/strong&gt;&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;+'10' === 10; // true&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;使用&lt;strong&gt;一元&lt;/strong&gt;的加号操作符，可以把字符串转换为数字。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;字符串转换为数字的常用方法：&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;+'010' === 10&lt;br /&gt;Number('010') === 10&lt;br /&gt;parseInt('010', 10) === 10&amp;nbsp;// 用来转换为整数&lt;br /&gt;&lt;br /&gt;+'010.2' === 10.2&lt;br /&gt;Number('010.2') === 10.2&lt;br /&gt;parseInt('010.2', 10) === 10&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;转换为布尔型&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;通过使用 &lt;strong&gt;否&lt;/strong&gt; 操作符两次，可以把一个值转换为布尔型。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;!!'foo';&amp;nbsp;// true&lt;br /&gt;!!''; &amp;nbsp; &amp;nbsp; &amp;nbsp;// false&lt;br /&gt;!!'0'; &amp;nbsp; &amp;nbsp; // true&lt;br /&gt;!!'1'; &amp;nbsp; &amp;nbsp; // true&lt;br /&gt;!!'-1' &amp;nbsp; &amp;nbsp; // true&lt;br /&gt;!!{}; &amp;nbsp; &amp;nbsp; &amp;nbsp;// true&lt;br /&gt;!!true; &amp;nbsp; &amp;nbsp;// true&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/article&gt;&lt;/section&gt;&lt;section id="core"&gt;&lt;header id="core.intro"&gt;&lt;p&gt;&lt;strong&gt;核心&lt;/strong&gt;&lt;/p&gt;&lt;/header&gt;&lt;article id="core.eval"&gt;&lt;p&gt;&lt;strong&gt;﻿为什么不要使用 &lt;code&gt;eval&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;code&gt;eval&lt;/code&gt; 函数会在当前作用域中执行一段 JavaScript 代码字符串。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = 1;&lt;br /&gt;function test() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var foo = 2;&lt;br /&gt;&amp;nbsp; &amp;nbsp; eval('foo = 3');&lt;br /&gt;&amp;nbsp; &amp;nbsp; return foo;&lt;br /&gt;}&lt;br /&gt;test(); // 3&lt;br /&gt;foo; // 1&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;但是 &lt;code&gt;eval&lt;/code&gt; 只在被&lt;strong&gt;直接&lt;/strong&gt;调用并且调用函数就是 &lt;code&gt;eval&lt;/code&gt; 本身时，才在当前作用域中执行。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = 1;&lt;br /&gt;function test() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var foo = 2;&lt;br /&gt;&amp;nbsp; &amp;nbsp; var bar = eval;&lt;br /&gt;&amp;nbsp; &amp;nbsp; bar('foo = 3');&lt;br /&gt;&amp;nbsp; &amp;nbsp; return foo;&lt;br /&gt;}&lt;br /&gt;test(); // 2&lt;br /&gt;foo; // 3&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;上面的代码等价于在全局作用域中调用 &lt;code&gt;eval&lt;/code&gt;，和下面两种写法效果一样：&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 写法一：直接调用全局作用域下的 foo 变量&lt;br /&gt;var foo = 1;&lt;br /&gt;function test() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var foo = 2;&lt;br /&gt;&amp;nbsp; &amp;nbsp; window.foo = 3;&lt;br /&gt;&amp;nbsp; &amp;nbsp; return foo;&lt;br /&gt;}&lt;br /&gt;test(); // 2&lt;br /&gt;foo; // 3&lt;br /&gt;&lt;br /&gt;// 写法二：使用 call 函数修改 eval 执行的上下文为全局作用域&lt;br /&gt;var foo = 1;&lt;br /&gt;function test() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var foo = 2;&lt;br /&gt;&amp;nbsp; &amp;nbsp; eval.call(window, 'foo = 3');&lt;br /&gt;&amp;nbsp; &amp;nbsp; return foo;&lt;br /&gt;}&lt;br /&gt;test(); // 2&lt;br /&gt;foo; // 3&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;在&lt;strong&gt;任何情况下&lt;/strong&gt;我们都应该避免使用 &lt;code&gt;eval&lt;/code&gt; 函数。99.9% 使用 &lt;code&gt;eval&lt;/code&gt; 的场景都有&lt;strong&gt;不使用&lt;/strong&gt; &lt;code&gt;eval&lt;/code&gt; 的解决方案。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;伪装的 &lt;code&gt;eval&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#other.timeouts"&gt;定时函数&lt;/a&gt; &lt;code&gt;setTimeout&lt;/code&gt; 和 &lt;code&gt;setInterval&lt;/code&gt; 都可以接受字符串作为它们的第一个参数。 这个字符串&lt;strong&gt;总是&lt;/strong&gt;在全局作用域中执行，因此 &lt;code&gt;eval&lt;/code&gt; 在这种情况下没有被直接调用。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;安全问题&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;code&gt;eval&lt;/code&gt; 也存在安全问题，因为它会执行&lt;strong&gt;任意&lt;/strong&gt;传给它的代码， 在代码字符串未知或者是来自一个不信任的源时，绝对不要使用 &lt;code&gt;eval&lt;/code&gt; 函数。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;绝对不要使用 &lt;code&gt;eval&lt;/code&gt;，任何使用它的代码都会在它的工作方式，性能和安全性方面受到质疑。 如果一些情况必须使用到 &lt;code&gt;eval&lt;/code&gt; 才能正常工作，首先它的设计会受到质疑，这&lt;strong&gt;不应该&lt;/strong&gt;是首选的解决方案， 一个更好的不使用 &lt;code&gt;eval&lt;/code&gt; 的解决方案应该得到充分考虑并优先采用。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="core.undefined"&gt;&lt;p&gt;&lt;strong&gt;﻿&lt;code&gt;undefined&lt;/code&gt; 和 &lt;code&gt;null&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;JavaScript 有两个表示&amp;#8216;空&amp;#8217;的值，其中比较有用的是 &lt;code&gt;undefined&lt;/code&gt;。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;undefined&lt;/code&gt; 的值&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;code&gt;undefined&lt;/code&gt; 是一个值为 &lt;code&gt;undefined&lt;/code&gt; 的类型。&lt;/p&gt;  &lt;p&gt;这个语言也定义了一个全局变量，它的值是 &lt;code&gt;undefined&lt;/code&gt;，这个变量也被称为 &lt;code&gt;undefined&lt;/code&gt;。 但是这个变量&lt;strong&gt;不是&lt;/strong&gt;一个常量，也不是一个关键字。这意味着它的&lt;em&gt;值&lt;/em&gt;可以轻易被覆盖。&lt;/p&gt;  &lt;aside&gt;&lt;p&gt;&lt;strong&gt;ES5 提示:&lt;/strong&gt; 在 ECMAScript 5 的严格模式下，&lt;code&gt;undefined&lt;/code&gt; &lt;strong&gt;不再是&lt;/strong&gt; &lt;em&gt;可写&lt;/em&gt;的了。   但是它的名称仍然可以被隐藏，比如定义一个函数名为 &lt;code&gt;undefined&lt;/code&gt;。&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;下面的情况会返回 &lt;code&gt;undefined&lt;/code&gt; 值：&lt;/p&gt;  &lt;ul&gt;&lt;li&gt;访问未修改的全局变量 &lt;code&gt;undefined&lt;/code&gt;。&lt;/li&gt;&lt;li&gt;由于没有定义 &lt;code&gt;return&lt;/code&gt; 表达式的函数隐式返回。&lt;/li&gt;&lt;li&gt;&lt;code&gt;return&lt;/code&gt; 表达式没有显式的返回任何内容。&lt;/li&gt;&lt;li&gt;访问不存在的属性。&lt;/li&gt;&lt;li&gt;函数参数没有被显式的传递值。&lt;/li&gt;&lt;li&gt;任何被设置为 &lt;code&gt;undefined&lt;/code&gt; 值的变量。&lt;/li&gt;&lt;/ul&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;处理 &lt;code&gt;undefined&lt;/code&gt; 值的改变&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;由于全局变量 &lt;code&gt;undefined&lt;/code&gt; 只是保存了 &lt;code&gt;undefined&lt;/code&gt; 类型实际&lt;em&gt;值&lt;/em&gt;的副本， 因此对它赋新值&lt;strong&gt;不会&lt;/strong&gt;改变类型 &lt;code&gt;undefined&lt;/code&gt; 的值。&lt;/p&gt;  &lt;p&gt;然而，为了方便其它变量和 &lt;code&gt;undefined&lt;/code&gt; 做比较，我们需要事先获取类型 &lt;code&gt;undefined&lt;/code&gt; 的值。&lt;/p&gt;  &lt;p&gt;为了避免可能对 &lt;code&gt;undefined&lt;/code&gt; 值的改变，一个常用的技巧是使用一个传递到&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.scopes"&gt;匿名包装器&lt;/a&gt;的额外参数。 在调用时，这个参数不会获取任何值。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var undefined = 123;&lt;br /&gt;(function(something, foo, undefined) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; // 局部作用域里的 undefined 变量重新获得了 `undefined` 值&lt;br /&gt;&lt;br /&gt;})('Hello World', 42);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;另外一种达到相同目的方法是在函数内使用变量声明。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var undefined = 123;&lt;br /&gt;(function(something, foo) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; var undefined;&lt;br /&gt;&amp;nbsp; &amp;nbsp; ...&lt;br /&gt;&lt;br /&gt;})('Hello World', 42);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这里唯一的区别是，在压缩后并且函数内没有其它需要使用 &lt;code&gt;var&lt;/code&gt; 声明变量的情况下，这个版本的代码会多出 4 个字节的代码。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;这里有点绕口，其实很简单。如果此函数内没有其它需要声明的变量，那么 &lt;code&gt;var&lt;/code&gt; 总共 4 个字符（包含一个空白字符）   就是专门为 &lt;code&gt;undefined&lt;/code&gt; 变量准备的，相比上个例子多出了 4 个字节。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;null&lt;/code&gt; 的用处&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;JavaScript 中的 &lt;code&gt;undefined&lt;/code&gt; 的使用场景类似于其它语言中的 &lt;em&gt;null&lt;/em&gt;，实际上 JavaScript 中的 &lt;code&gt;null&lt;/code&gt; 是另外一种数据类型。&lt;/p&gt;  &lt;p&gt;它在 JavaScript 内部有一些使用场景（比如声明原型链的终结 &lt;code&gt;Foo.prototype = null&lt;/code&gt;），但是大多数情况下都可以使用 &lt;code&gt;undefined&lt;/code&gt; 来代替。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;article id="core.semicolon"&gt;&lt;p&gt;&lt;strong&gt;﻿自动分号插入&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;尽管 JavaScript 有 C 的代码风格，但是它&lt;strong&gt;不&lt;/strong&gt;强制要求在代码中使用分号，实际上可以省略它们。&lt;/p&gt;  &lt;p&gt;JavaScript 不是一个没有分号的语言，恰恰相反上它需要分号来就解析源代码。 因此 JavaScript 解析器在遇到由于缺少分号导致的解析错误时，会&lt;strong&gt;自动&lt;/strong&gt;在源代码中插入分号。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = function() {&lt;br /&gt;} // 解析错误，分号丢失&lt;br /&gt;test()&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;自动插入分号，解析器重新解析。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var foo = function() {&lt;br /&gt;}; // 没有错误，解析继续&lt;br /&gt;test()&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;自动的分号插入被认为是 JavaScript 语言&lt;strong&gt;最大&lt;/strong&gt;的设计缺陷之一，因为它&lt;em&gt;能&lt;/em&gt;改变代码的行为。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;工作原理&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;下面的代码没有分号，因此解析器需要自己判断需要在哪些地方插入分号。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;(function(window, undefined) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; function test(options) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; log('testing!')&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (options.list || []).forEach(function(i) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; })&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.value.test(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'long string to pass here',&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'and another long string to pass'&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo: function() {}&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; window.test = test&lt;br /&gt;&lt;br /&gt;})(window)&lt;br /&gt;&lt;br /&gt;(function(window) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; window.someLibrary = {}&lt;br /&gt;})(window)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;下面是解析器"猜测"的结果。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;(function(window, undefined) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; function test(options) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 没有插入分号，两行被合并为一行&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; log('testing!')(options.list || []).forEach(function(i) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }); // &amp;lt;- 插入分号&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.value.test(&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'long string to pass here',&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 'and another long string to pass'&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ); // &amp;lt;- 插入分号&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return; // &amp;lt;- 插入分号, 改变了 return 表达式的行为&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; { // 作为一个代码段处理&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foo: function() {} &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }; // &amp;lt;- 插入分号&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; window.test = test; // &amp;lt;- 插入分号&lt;br /&gt;&lt;br /&gt;// 两行又被合并了&lt;br /&gt;})(window)(function(window) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; window.someLibrary = {}; // &amp;lt;- 插入分号&lt;br /&gt;})(window); //&amp;lt;- 插入分号&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; JavaScript 不能正确的处理 &lt;code&gt;return&lt;/code&gt; 表达式紧跟换行符的情况，   虽然这不能算是自动分号插入的错误，但这确实是一种不希望的副作用。&lt;/p&gt; &lt;/aside&gt;  &lt;p&gt;解析器显著改变了上面代码的行为，在另外一些情况下也会做出&lt;strong&gt;错误的处理&lt;/strong&gt;。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;前置括号&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;在前置括号的情况下，解析器&lt;strong&gt;不会&lt;/strong&gt;自动插入分号。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;log('testing!')&lt;br /&gt;(options.list || []).forEach(function(i) {})&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面代码被解析器转换为一行。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;log('testing!')(options.list || []).forEach(function(i) {})&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;&lt;code&gt;log&lt;/code&gt; 函数的执行结果&lt;strong&gt;极大&lt;/strong&gt;可能&lt;strong&gt;不是&lt;/strong&gt;函数；这种情况下就会出现 &lt;code&gt;TypeError&lt;/code&gt; 的错误，详细错误信息可能是 &lt;code&gt;undefined is not a function&lt;/code&gt;。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;建议&lt;strong&gt;绝对&lt;/strong&gt;不要省略分号，同时也提倡将花括号和相应的表达式放在一行， 对于只有一行代码的 &lt;code&gt;if&lt;/code&gt; 或者 &lt;code&gt;else&lt;/code&gt; 表达式，也不应该省略花括号。 这些良好的编程习惯不仅可以提到代码的一致性，而且可以防止解析器改变代码行为的错误处理。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;/section&gt;&lt;section id="other"&gt;&lt;header id="other.intro"&gt;&lt;p&gt;&lt;strong&gt;其它&lt;/strong&gt;&lt;/p&gt;&lt;/header&gt;&lt;article id="other.timeouts"&gt;&lt;p&gt;&lt;strong&gt;﻿&lt;code&gt;setTimeout&lt;/code&gt; 和 &lt;code&gt;setInterval&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;p&gt;由于 JavaScript 是异步的，可以使用 &lt;code&gt;setTimeout&lt;/code&gt; 和 &lt;code&gt;setInterval&lt;/code&gt; 来计划执行函数。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 定时处理&lt;strong&gt;不是&lt;/strong&gt; ECMAScript 的标准，它们在 &lt;a href="http://en.wikipedia.org/wiki/Document_Object_Model"&gt;DOM (文档对象模型)&lt;/a&gt; 被实现。&lt;/p&gt; &lt;/aside&gt;  &lt;pre&gt;&lt;code&gt;function foo() {}&lt;br /&gt;var id = setTimeout(foo, 1000); // 返回一个大于零的数字&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;当 &lt;code&gt;setTimeout&lt;/code&gt; 被调用时，它会返回一个 ID 标识并且计划在将来&lt;strong&gt;大约&lt;/strong&gt; 1000 毫秒后调用 &lt;code&gt;foo&lt;/code&gt; 函数。 &lt;code&gt;foo&lt;/code&gt; 函数只会被执行&lt;strong&gt;一次&lt;/strong&gt;。&lt;/p&gt;  &lt;p&gt;基于 JavaScript 引擎的计时策略，以及本质上的单线程运行方式，所以其它代码的运行可能会阻塞此线程。 因此&lt;strong&gt;没法确保&lt;/strong&gt;函数会在 &lt;code&gt;setTimeout&lt;/code&gt; 指定的时刻被调用。&lt;/p&gt;  &lt;p&gt;作为第一个参数的函数将会在&lt;em&gt;全局作用域&lt;/em&gt;中执行，因此函数内的 &lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.this"&gt;&lt;code&gt;this&lt;/code&gt;&lt;/a&gt; 将会指向这个全局对象。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function Foo() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.value = 42;&lt;br /&gt;&amp;nbsp; &amp;nbsp; this.method = function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // this 指向全局对象&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(this.value); // 输出：undefined&lt;br /&gt;&amp;nbsp; &amp;nbsp; };&lt;br /&gt;&amp;nbsp; &amp;nbsp; setTimeout(this.method, 500);&lt;br /&gt;}&lt;br /&gt;new Foo();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; &lt;code&gt;setTimeout&lt;/code&gt; 的第一个参数是&lt;strong&gt;函数对象&lt;/strong&gt;，一个常犯的错误是这样的 &lt;code&gt;setTimeout(foo(), 1000)&lt;/code&gt;，   这里回调函数是 &lt;code&gt;foo&lt;/code&gt; 的&lt;strong&gt;返回值&lt;/strong&gt;，而&lt;strong&gt;不是&lt;/strong&gt;&lt;code&gt;foo&lt;/code&gt;本身。   大部分情况下，这是一个潜在的错误，因为如果函数返回 &lt;code&gt;undefined&lt;/code&gt;，&lt;code&gt;setTimeout&lt;/code&gt; 也&lt;strong&gt;不会&lt;/strong&gt;报错。&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;setInterval&lt;/code&gt; 的堆调用&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;code&gt;setTimeout&lt;/code&gt; 只会执行回调函数一次，不过 &lt;code&gt;setInterval&lt;/code&gt; - 正如名字建议的 - 会每隔 &lt;code&gt;X&lt;/code&gt; 毫秒执行函数一次。 但是却不鼓励使用这个函数。&lt;/p&gt;  &lt;p&gt;当回调函数的执行被阻塞时，&lt;code&gt;setInterval&lt;/code&gt; 仍然会发布更多的毁掉指令。在很小的定时间隔情况下，这会导致回调函数被堆积起来。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function foo(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; // 阻塞执行 1 秒&lt;br /&gt;}&lt;br /&gt;setInterval(foo, 1000);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面代码中，&lt;code&gt;foo&lt;/code&gt; 会执行一次随后被阻塞了一分钟。&lt;/p&gt;  &lt;p&gt;在 &lt;code&gt;foo&lt;/code&gt; 被阻塞的时候，&lt;code&gt;setInterval&lt;/code&gt; 仍然在组织将来对回调函数的调用。 因此，当第一次 &lt;code&gt;foo&lt;/code&gt; 函数调用结束时，已经有 &lt;strong&gt;10&lt;/strong&gt; 次函数调用在等待执行。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;处理可能的阻塞调用&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;最简单也是最容易控制的方案，是在回调函数内部使用 &lt;code&gt;setTimeout&lt;/code&gt; 函数。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function foo(){&lt;br /&gt;&amp;nbsp; &amp;nbsp; // 阻塞执行 1 秒&lt;br /&gt;&amp;nbsp; &amp;nbsp; setTimeout(foo, 1000);&lt;br /&gt;}&lt;br /&gt;foo();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这样不仅封装了 &lt;code&gt;setTimeout&lt;/code&gt; 回调函数，而且阻止了调用指令的堆积，可以有更多的控制。 &lt;code&gt;foo&lt;/code&gt; 函数现在可以控制是否继续执行还是终止执行。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;手工清空定时器&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;可以通过将定时时产生的 ID 标识传递给 &lt;code&gt;clearTimeout&lt;/code&gt; 或者 &lt;code&gt;clearInterval&lt;/code&gt; 函数来清除定时， 至于使用哪个函数取决于调用的时候使用的是 &lt;code&gt;setTimeout&lt;/code&gt; 还是 &lt;code&gt;setInterval&lt;/code&gt;。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;var id = setTimeout(foo, 1000);&lt;br /&gt;clearTimeout(id);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;清除所有定时器&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;由于没有内置的清除所有定时器的方法，可以采用一种暴力的方式来达到这一目的。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;// 清空"所有"的定时器&lt;br /&gt;for(var i = 1; i &amp;lt; 1000; i++) {&lt;br /&gt;&amp;nbsp; &amp;nbsp; clearTimeout(i);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;可能还有些定时器不会在上面代码中被清除（&lt;strong&gt;&lt;a href="http://cnblogs.com/sanshi/"&gt;译者注&lt;/a&gt;：&lt;/strong&gt;如果定时器调用时返回的 ID 值大于 1000）， 因此我们可以事先保存所有的定时器 ID，然后一把清除。&lt;/p&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;隐藏使用 &lt;code&gt;eval&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;code&gt;setTimeout&lt;/code&gt; 和 &lt;code&gt;setInterval&lt;/code&gt; 也接受第一个参数为字符串的情况。 这个特性&lt;strong&gt;绝对&lt;/strong&gt;不要使用，因为它在内部使用了 &lt;code&gt;eval&lt;/code&gt;。&lt;/p&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 由于定时器函数不是 ECMAScript 的标准，如何解析字符串参数在不同的 JavaScript 引擎实现中可能不同。   事实上，微软的 JScript 会使用 &lt;code&gt;Function&lt;/code&gt; 构造函数来代替 &lt;code&gt;eval&lt;/code&gt; 的使用。&lt;/p&gt; &lt;/aside&gt;  &lt;pre&gt;&lt;code&gt;function foo() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; // 将会被调用&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function bar() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; function foo() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 不会被调用&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; setTimeout('foo()', 1000);&lt;br /&gt;}&lt;br /&gt;bar();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;由于 &lt;code&gt;eval&lt;/code&gt; 在这种情况下不是被&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#core.eval"&gt;直接&lt;/a&gt;调用，因此传递到 &lt;code&gt;setTimeout&lt;/code&gt; 的字符串会自&lt;em&gt;全局作用域&lt;/em&gt;中执行； 因此，上面的回调函数使用的不是定义在 &lt;code&gt;bar&lt;/code&gt; 作用域中的局部变量 &lt;code&gt;foo&lt;/code&gt;。&lt;/p&gt;  &lt;p&gt;建议&lt;strong&gt;不要&lt;/strong&gt;在调用定时器函数时，为了向回调函数传递参数而使用字符串的形式。&lt;/p&gt;  &lt;pre&gt;&lt;code&gt;function foo(a, b, c) {}&lt;br /&gt;&lt;br /&gt;// 不要这样做&lt;br /&gt;setTimeout('foo(1,2, 3)', 1000)&lt;br /&gt;&lt;br /&gt;// 可以使用匿名函数完成相同功能&lt;br /&gt;setTimeout(function() {&lt;br /&gt;&amp;nbsp; &amp;nbsp; foo(a, b, c);&lt;br /&gt;}, 1000)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;aside&gt;   &lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 虽然也可以使用这样的语法 &lt;code&gt;setTimeout(foo, 1000, a, b, c)&lt;/code&gt;，   但是不推荐这么做，因为在使用对象的&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#function.this"&gt;属性方法&lt;/a&gt;时可能会出错。   （&lt;strong&gt;译者注：&lt;/strong&gt;这里说的是属性方法内，&lt;code&gt;this&lt;/code&gt; 的指向错误）&lt;/p&gt; &lt;/aside&gt;  &lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;绝对不要&lt;/strong&gt;使用字符串作为 &lt;code&gt;setTimeout&lt;/code&gt; 或者 &lt;code&gt;setInterval&lt;/code&gt; 的第一个参数， 这么写的代码明显质量很差。当需要向回调函数传递参数时，可以创建一个&lt;em&gt;匿名函数&lt;/em&gt;，在函数内执行真实的回调函数。&lt;/p&gt;  &lt;p&gt;另外，应该避免使用 &lt;code&gt;setInterval&lt;/code&gt;，因为它的定时执行不会被 JavaScript 阻塞。&lt;/p&gt;&lt;/div&gt;&lt;/article&gt;&lt;/section&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2334802.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/01/2334802.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/02/01/2334786.html</id><title type="text">面向对象的 Javascript （声明篇）</title><summary type="text">有时间重新审视了遍 Javascript 的面向对象机制。与其他的语言不同，Javascript 可以说提供了更灵活的面向对象机制（比如 function 在完成其自身功能的同时也是对象）。 由于才疏学浅，我不得不将 《Javascript 高级程序设计》 中的部分内容摘抄过来，这些同时也算是我的读书笔记吧。由于 Javascript 面向对象机制及其的重要，而且内容非常的繁多，在这里就分篇章逐个介绍。 使用对象首先就是声明它（内置的对象当然就不需要了）。该死的 Javascript 总是会让我们死去很多的脑细胞，这篇文章主要说明下声明 Javascript 类的...</summary><published>2012-02-01T08:14:00Z</published><updated>2012-02-01T08:14:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/01/2334786.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/01/2334786.html"/><content type="html">&lt;div&gt;&lt;div&gt;             &lt;p&gt;有时间重新审视了遍 Javascript 的面向对象机制。与其他的语言不同，Javascript 可以说提供了更灵活的面向对象机制（比如 function 在完成其自身功能的同时也是对象）。&lt;/p&gt;  &lt;p&gt;由于才疏学浅，我不得不将 &lt;a href="http://www.gracecode.com/archives/675/" title="http://www.gracecode.com/archives/675/"&gt;《Javascript 高级程序设计》&lt;/a&gt; 中的部分内容摘抄过来，这些同时也算是我的读书笔记吧。由于 Javascript 面向对象机制及其的重要，而且内容非常的繁多，在这里就分篇章逐个介绍。&lt;/p&gt;  &lt;p&gt;使用对象首先就是声明它（内置的对象当然就不需要了）。该死的 Javascript 总是会让我们死去很多的脑细胞，这篇文章主要说明下声明 Javascript 类的几种方法。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;工厂模式&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;工厂模式可能是很多开发人员使用的一种模式，简单的说这种方法先定义「地基」，然后在往上面扔（绑定）各种功能和属性。下面的代码可能看起来会非常的熟悉：&lt;/p&gt;  &lt;pre&gt;var oCar = new Object; oCar.color = "red"; oCar.showColor = function() {     alert(this.color); } oCar.showColor();&lt;/pre&gt;  &lt;p&gt;当然，既然包装成一个类，就要重用它（上面的方法从语法上说仅仅是变量）。可以使用返回特定对象的工厂函数（factory function）将其封装起来：&lt;/p&gt;  &lt;pre&gt;function createCar() {     var oCar = new Object;     oCar.color = "red";     oCar.showColor = function() {         alert(this.color);     }      return oCar; } oCar = createCar(); oCar.showColor();&lt;/pre&gt;  &lt;p&gt;当然，变通一下，可以在 createCar 函数上加入些参数，这样看起来已经非常地专业了：&lt;/p&gt;  &lt;pre&gt;function createCar(sColor) {     var oCar = new Object;     oCar.color = sColor;     oCar.showColor = function() {         alert(this.color);     }      return oCar; } oCar = createCar(); oCar.showColor();&lt;/pre&gt;  &lt;p&gt;匿名函数总是让人感觉非常的高深，但是有时候也会迷惑了自己。如果不考虑篇幅，可以外部定义它：&lt;/p&gt;  &lt;pre&gt;function showColor() {     alert(this.color); }  function createCar(sColor) {     var oCar = new Object;     oCar.color = sColor;     oCar.showColor = showColor;      return oCar; } oCar = createCar(); oCar.showColor();&lt;/pre&gt;  &lt;p&gt;这样做还有一个好处，就是不用重复定义 oCar.showColor 了（高效率的程序每个人都喜欢）。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;构造函数模式&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;构造函数其实和工厂方式差不多。从代码量上来说，就是省略了构造函数内部没有创建一个对象。&lt;/p&gt;  &lt;pre&gt;function Car(sColor) {     this.color = sColor;     this.showColor = function () {         alert(this.color);     } } oCar = new Car("red"); oCar.showColor();&lt;/pre&gt;  &lt;p&gt;其实此隐含的对象已经在 new 以后就被实例化了。默认情况下，构造函数返回的就是其 this 的值（所以不必使用 return 返回）。但构造函数模式和工厂模式一样可能会重复定义方法，这点可以参考上述工厂模式的做法避免它（始终看起来不完美）。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;原型模式&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;已经受够重复定义的问题了，那么有没有完美的解决办法呢？当然有。使用原型方法可以有效的避免此类的问题。&lt;/p&gt;  &lt;pre&gt;function Car() {} Car.prototype.color = new Array("red", "green", "blue"); Car.prototype.showColor = function() {     alert(this.color); } oCar = new Car(); oCar.showColor();&lt;/pre&gt;  &lt;p&gt;但是使用此模式需要注意的是类中的所有属性和方法都是共用的（其实就是指针）。这意味着虽然被实例化的两个变量，如果其中一处的值被更改，那么另外一个就也会被更改。&lt;/p&gt;  &lt;p&gt;&lt;em&gt;注：此段内容有更改，详细请参见 &lt;a href="http://www.f-dev.com/325" title="http://www.f-dev.com/325"&gt;这里&lt;/a&gt; 和 &lt;a href="http://www.planabc.net/2008/02/20/javascript_new_function/" title="http://www.planabc.net/2008/02/20/javascript_new_function/"&gt;这里&lt;/a&gt; （感谢 fish 兄弟提出）。&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;混合模式&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;看起来越来越完美了，结合上述学到的方法就很容易解决原型模式的问题，这样看起来就更像是专业的程序员了。&lt;/p&gt;  &lt;pre&gt;function Car(sColor) {     this.color = sColor; } Car.prototype.showColor = function() {     alert(this.color); } oCar = new Car("red"); oCar.showColor();&lt;/pre&gt;  &lt;p&gt;上述的方法声明的类， showColor 方法是原型（仅创建了一个实例），其他的都是构造（互不干扰）。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;动态原型模式&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;把自己的方法仍在外面总不是件非常环保的事情，下面的方法就非常的「绿色」：&lt;/p&gt;  &lt;pre&gt;function Car() {     this.color = "red";     if (typeof Car._initialized == "undefined") {         Car.prototype.showColor = function() {             alert(this.color);         };         Car._initialized = true;     } } oCar = new Car("red"); oCar.showColor();&lt;/pre&gt;  &lt;p&gt;此方法于上述的混合模式效果一致，即在构造函数内定义属性，而方法则使用原型模式。唯一的区别就是赋予对象方法的位置。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;混合工厂模式&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;混合工厂模式可以认为是构造模式与混合模式的整合，因为 function 本身就是一个对象，所以可以使用 new 来实例化（请允许我这样描述）。&lt;/p&gt;  &lt;pre&gt;function Car() {     var oCar = new Object;     oCar.color = "red";     oCar.showColor = function() {         alert(this.color);     }      return oCar; } oCar = new Car(); oCar.showColor();&lt;/pre&gt;  &lt;p&gt;不过建议避免使用此方法定义，因为于上述的工厂模式一样，它存在重复声明的问题。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;选用何种模式？&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;其实通过上面的描述已经有所结果，通常使用的是 混合模式 与 动态原型模式 （我个人投动态原型模式一票）。不过不要单独使用 工厂模式 与 构造模式 （或者其两者的结合体），因为这样会造成不必要的浪费。&lt;/p&gt;  &lt;p&gt;附，上述的代码 &lt;a href="http://files.gracecode.com/2008_02_15/1203068392.zip" title="http://files.gracecode.com/2008_02_15/1203068392.zip"&gt;打包下载&lt;/a&gt; 。&lt;/p&gt;  &lt;/div&gt;&lt;p&gt;转载：&lt;div&gt;http://www.gracecode.com/archives/934/&lt;/div&gt; &lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2334786.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/02/01/2334786.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/yuzhongwusan/archive/2012/01/31/2332606.html</id><title type="text">插件：Firebug扩展插件FireQuery</title><summary type="text">之前也不知道看了哪篇文章的介绍，在火狐上装了FireQuery插件，一直没去注意它 的用途。就只发现在FireBug的HTML Tab下的HTML代码层次上多了些事件代码，可以点击查看对应的绑定事件。最近的一些使用，发现了FireQuery的好处，特别是在测试动态生成 DOM以及数据测试上很是方便。 关于FireQuery的简介和安装就不多说了，访问：http://firequery.binaryage.com/。 1. 标签绑定事件查看 打开Firebug的HTML Tab，在一些绑定了事件的HTML标签上可以看到一些事件数据：点击...</summary><published>2012-01-31T01:51:00Z</published><updated>2012-01-31T01:51:00Z</updated><author><name>yuzhongwusan</name><uri>http://www.cnblogs.com/yuzhongwusan/</uri></author><link rel="alternate" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/01/31/2332606.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/yuzhongwusan/archive/2012/01/31/2332606.html"/><content type="html">&lt;div&gt;&lt;div full_post"=""&gt;                             &lt;p&gt;之前也不知道看了哪篇文章的介绍，在火狐上装了FireQuery插件，一直没去注意它 的用途。就只发现在FireBug的HTML  Tab下的HTML代码层次上多了些事件代码，可以点击查看对应的绑定事件。最近的一些使用，发现了FireQuery的好处，特别是在测试动态生成 DOM以及数据测试上很是方便。&lt;/p&gt; &lt;p&gt;关于FireQuery的简介和安装就不多说了，访问：&lt;a target="_blank" href="http://firequery.binaryage.com/" rel="external"&gt;http://firequery.binaryage.com/&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1. 标签绑定事件查看&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;打开Firebug的HTML Tab，在一些绑定了事件的HTML标签上可以看到一些事件数据：&lt;img src="http://www.ihiro.org/blog/wp-content/uploads/2011/11/1.click-to-view-DOM.jpg" alt="" title="Click to view DOM" size-full=""  wp-image-1703"="" width="561" height="94" /&gt;点击后跳转到DOM Tab，可以查看对应的一些信息：&lt;img src="http://www.ihiro.org/blog/wp-content/uploads/2011/11/2.DOM-view-e1322209489464.jpg" alt="" title="DOM view" size-full=""  wp-image-1704"="" width="600" height="233" /&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;2. 查看jQuery的.data()函数添加的数据&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;jQuery的.data()函数是用来给HTML上添加数据的，一般用在插件或数据处理上。一般情况下执行了.data()后，无法确认是否给该 标签正确地绑定了数据。此时通过FireQuery就可以查看，同样支持点击后跳转到DOM  Tab查看，这在给HTML标签绑定一些对象数据时非常实用。[下图中黄色counter=33为data数据&lt;/strong&gt;&lt;strong&gt;]&lt;/strong&gt;&lt;img src="http://www.ihiro.org/blog/wp-content/uploads/2011/11/3.view-data-and-event.jpg" alt="" title="View data" size-full=""  wp-image-1705"="" width="600" height="159" /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;[&lt;span style="color: red;"&gt;你可简单测试这个：&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&amp;nbsp; &amp;lt;script src="jquery-1.4.2.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; $(document).ready(function(){&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;$("p").data('ak',50)&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; }); &lt;br /&gt;&amp;nbsp; &amp;lt;/script&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;p&amp;gt;If you click on me, I will disappear.&amp;lt;/p&amp;gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;] &lt;br /&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;3. 引入jQuery.Lint.js对页面代码测试&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在Console Tab的下拉菜单中启用引入，重新刷新页面后会动态地引入jQuery.Lint.js插件，此时对页面中出现的错误进行输出提示。&lt;img src="http://www.ihiro.org/blog/wp-content/uploads/2011/11/4.enable-Use-jQuery-Lint.jpg" alt="" title="Enable to Use jQuery.Lint" size-full=""  wp-image-1706"="" width="370" height="370" /&gt;&lt;img src="http://www.ihiro.org/blog/wp-content/uploads/2011/11/5.jquery.lint_.jpg" alt="" title="jQuery.lint" size-full=""  wp-image-1707"="" width="137" height="45" /&gt;&lt;img src="http://www.ihiro.org/blog/wp-content/uploads/2011/11/6.track-css-error.jpg" alt="" title="Track CSS error" size-full=""  wp-image-1708"="" width="444" height="43" /&gt;&lt;/p&gt; &lt;p&gt;FireQuery对于前端开发和测试来说绝对是一大助手，更多的好处还是在实际中可以体会出来。&lt;/p&gt; &lt;p&gt;官方测试页面：&lt;a target="_blank" href="http://firequery.binaryage.com/test" rel="external"&gt;http://firequery.binaryage.com/test&lt;/a&gt;&lt;/p&gt;                         &lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/yuzhongwusan/aggbug/2332606.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/yuzhongwusan/archive/2012/01/31/2332606.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
