<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_jenry（云飞扬）</title><subtitle type="text">奋斗改变命运，理想让我们与众不同！</subtitle><id>http://feed.cnblogs.com/blog/u/14223/rss</id><updated>2012-02-11T12:08:46Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/14223/rss"/><entry><id>http://www.cnblogs.com/jenry/archive/2012/02/11/2347012.html</id><title type="text">Canvas里的globalCompositeOperation</title><summary type="text">Canvas里的globalCompositeOperation是个很少用到的函数,不太熟悉程序绘图的同学们估计压根都不知道这玩意是干什么的.简单来说,Composite(组合),就是对你在绘图中,后绘制的图形与先绘制的图形之间的组合显示效果,比如在国画中,你先画一笔红色,再来一笔绿色,相交的部分是一种混色,而在油画中,绿色就会覆盖掉相交部分的红色,这在程序绘图中的处理就是Composite,Canvas API中对应的函数就是globalCompositeOperation,跟globalAlpha一样,这个属性是全局的,所以在使用的时候要注意save和restore.我在练习这个函数的时候</summary><published>2012-02-11T12:07:00Z</published><updated>2012-02-11T12:07:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2012/02/11/2347012.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/02/11/2347012.html"/><content type="html">&lt;p&gt;&lt;strong&gt;Canvas&lt;/strong&gt;里的&lt;strong&gt;globalCompositeOperation&lt;/strong&gt;是个很少用到的函数,不太熟悉程序绘图的同学们估计压根都不知道这玩意是干什么的.&lt;br /&gt;&lt;br /&gt;简单来说,&lt;strong&gt;Composite(组合)&lt;/strong&gt;,就是对你在绘图中,后绘制的图形与先绘制的图形之间的组合显示效果,比如在国画中,你先画一笔红色,再来一笔绿色,相交的部分是一种混色,而在油画中,绿色就会覆盖掉相交部分的红色,这在程序绘图中的处理就是Composite,Canvas API中对应的函数就是globalCompositeOperation,跟globalAlpha一样,这个属性是全局的,所以在使用的时候要注意save和restore.&lt;br /&gt;&lt;br /&gt;我在练习这个函数的时候,用的是chrome浏览器,但是测试结果却跟实际应该出现的结果不太一致,开始我以为是写错了,检查数遍却没有问题,疑惑之下换了各种浏览器来测试,真是囧啊,每个浏览器居然都不一样,连同核心的chrome和safari都不一样...下面是测试结果.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://dl.iteye.com/upload/picture/pic/71054/9f086ab0-932c-3195-884b-cfc8aa3d8ce4.jpg" alt="" /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;chrome&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://dl.iteye.com/upload/picture/pic/71056/bb7bff56-5939-36bb-b325-f82695410862.jpg" alt="" /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;firefox&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://dl.iteye.com/upload/picture/pic/71058/9292f201-bb95-30ec-8920-b9836bd20baa.jpg" alt="" /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;opera&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://dl.iteye.com/upload/picture/pic/71060/8742ecdb-3f3c-3840-82df-90c846e80f5b.jpg" alt="" /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;safari&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img  title="点击查看原始大小图片" src="http://dl.iteye.com/upload/picture/pic/71062/af2e011f-faa8-3f70-819a-8f445dae60a0.jpg" alt="" width="760" height="619" /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;firefox官方网站给的实际效果图&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;下面是每一个选项的说明(我表达的可能不太明白,看图吧):&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;source-over&lt;/strong&gt; 默认,相交部分由后绘制图形的填充(颜色,渐变,纹理)覆盖,全部浏览器通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;source-in&lt;/strong&gt; 只绘制相交部分,由后绘制图形的填充覆盖,其余部分透明,webkit两兄弟没有通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;source-out&lt;/strong&gt; 只绘制后绘制图形不相交的部分,由后绘制图形的填充覆盖,其余部分透明,webkit两兄弟没有通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;source-atop&lt;/strong&gt; 后绘制图形不相交的部分透明,相交部分由后绘制图形的填充覆盖,全部浏览器通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;destination-over&lt;/strong&gt; 相交部分由先绘制图形的填充(颜色,渐变,纹理)覆盖,全部浏览器通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;destination-in&lt;/strong&gt; 只绘制相交部分,由先绘制图形的填充覆盖,其余部分透明,webkit两兄弟没有通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;destination-out&lt;/strong&gt; 只绘制先绘制图形不相交的部分,由先绘制图形的填充覆盖,其余部分透明,全部浏览器通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;destination-atop&lt;/strong&gt; 先绘制图形不相交的部分透明,相交部分由先绘制图形的填充覆盖,webkit两兄弟没有通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;lighter&lt;/strong&gt; 相交部分由根据先后图形填充来增加亮度,全部浏览器通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;darker&lt;/strong&gt; 相交部分由根据先后图形填充来降低亮度,chrome通过,firefox官方说Firefox 3.6 / Thunderbird 3.1 / Fennec 1.0以后版本移除这个效果-0-,why?safari看似可以,但是无论你什么颜色,它都给填充成黑色,opera无效果&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;copy&lt;/strong&gt; 只绘制后绘制图形,只有opera通过&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;xor&lt;/strong&gt; 相交部分透明,全部浏览器通过&lt;br /&gt;&lt;br /&gt;结果太令人无语了,特别是firefox那个新版本移除,我靠,为嘛啊?chrome和safari难兄难弟,成绩一塌糊涂,难道是webkit核心的问题?safari那个填充黑色很有IE6-中png透明问题的风范...opera表现很抢眼,只有一个效果未实现,继续努力!&lt;br /&gt;&lt;br /&gt;评分及浏览器版本:&lt;br /&gt;Chrome dev 7.0.503.0 : 7/12&lt;br /&gt;Firefox 3.6.6 : 10/12&lt;br /&gt;Opera 10.53 : 11/12&lt;br /&gt;Safari 4.0.3(531.9.1) : 6/12&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2347012.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/02/11/2347012.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2012/01/19/2327628.html</id><title type="text">JavaScript秘密花园 - Object, Prototype</title><summary type="text">http://bonsaiden.github.com/JavaScript-Garden/zh/#object.forinloop简介（Intro）JavaScript秘密花园是一个不断更新的文档，主要关心JavaScript一些古怪用法。 对于如何避免常见的错误，难以发现的问题，以及性能问题和不好的实践给出建议， 初学者可以籍此深入了解JavaScript的语言特性。JavaScript秘密花园不是用来教你JavaScript。为了更好的理解这篇文章的内容， 你需要事先学习JavaScript的基础知识。在Mozilla开发者网络中有一系列非常棒的JavaScript学习向导。关于作者（T</summary><published>2012-01-19T13:52:00Z</published><updated>2012-01-19T13:52:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327628.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327628.html"/><content type="html">&lt;p&gt;&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/zh/#object.forinloop"&gt;http://bonsaiden.github.com/JavaScript-Garden/zh/#object.forinloop&lt;/a&gt;&lt;/p&gt;&#xD;
&lt;h1 id="intro"&gt;简介（Intro）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;JavaScript秘密花园&lt;/strong&gt;是一个不断更新的文档，主要关心JavaScript一些古怪用法。 对于如何避免常见的错误，难以发现的问题，以及性能问题和不好的实践给出建议， 初学者可以籍此深入了解JavaScript的语言特性。&lt;/p&gt;&#xD;
&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;&#xD;
&lt;p&gt;&lt;strong&gt;关于作者（The authors）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这篇文章的作者是两位Stack Overflow的用户, &lt;a href="http://stackoverflow.com/users/170224/ivo-wetzel"&gt;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;&#xD;
&lt;p&gt;&lt;strong&gt;贡献者（Contributors）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;&lt;a href="https://github.com/caio"&gt;Caio Rom?o&lt;/a&gt; (拼写检查)&lt;/li&gt;&#xD;
&lt;li&gt;&lt;a href="https://github.com/blixt"&gt;Andreas Blixt&lt;/a&gt; (语言修正)&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;许可（License）&lt;/strong&gt;&lt;/p&gt;&#xD;
&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;file an issue&lt;/a&gt;或者pull request。 你也可以在Stack Overflow的聊天室&lt;a href="http://chat.stackoverflow.com/rooms/17/javascript"&gt;JavaScript room&lt;/a&gt;找到我们。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;中文翻译（Chinese Translation）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/"&gt;JavaScript Garden - 原文&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;&lt;a href="http://sanshi.me/articles/JavaScript-Garden-CN/html/index.html"&gt;JavaScript Garden - 中文翻译&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;&lt;a href="http://sanshi.me/"&gt;译作者：三生石上&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&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;&#xD;
&lt;h2 id="objects"&gt;对象（Objects） &lt;a href="#intro"&gt;#top&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;JavaScript中所有变量都是对象，除了两个例外&lt;a href="#undefined"&gt;&lt;code&gt;null&lt;/code&gt;&lt;/a&gt; 和 &lt;a href="#undefined"&gt;&lt;code&gt;undefined&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_36298" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code &gt;false&lt;/code&gt;&lt;code &gt;.toString() &lt;/code&gt;&lt;code &gt;// 'false'&amp;lt;br&amp;gt;[1, 2, 3].toString(); // '1,2,3'&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;function Foo(){}&amp;lt;br&amp;gt;Foo.bar = 1;&amp;lt;br&amp;gt;Foo.bar; // 1&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;一个常见的误解是数字的字面值（literal）不是对象。这是因为JavaScript解析器的一个错误， 它试图将&lt;em&gt;点操作符&lt;/em&gt;解析为浮点数字面值的一部分。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_798355" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;code&amp;gt;2.toString(); &lt;/code&gt;&lt;code &gt;// 出错：SyntaxError&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;有很多变通方法可以让数字的字面值看起来像对象。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_545266" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;code&amp;gt;2..toString(); &lt;/code&gt;&lt;code &gt;// 第二个点号可以正常解析&amp;lt;br&amp;gt;2 .toString(); // 注意点号前面的空格&amp;lt;br&amp;gt;(2).toString(); // 2先被计算&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;strong&gt;对象作为数据类型（Objects as a data type）&lt;/strong&gt;&lt;/p&gt;&#xD;
&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;&#xD;
&lt;p&gt;使用对象的字面语法 - &lt;code&gt;{}&lt;/code&gt; - 可以创建一个简单对象。这个新创建的对象从&lt;code&gt;Object.prototype&lt;/code&gt;&lt;a href="#prototype"&gt;继承&lt;/a&gt;下面，没有任何&lt;a href="#hasownproperty"&gt;自定义属性&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_690210" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code &gt;var&lt;/code&gt; &lt;code &gt;foo = {}; &lt;/code&gt;&lt;code &gt;// 一个空对象&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;// 一个新对象，拥有一个值为12的自定义属性'test'&amp;lt;br&amp;gt;var bar = {test: 12};&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;strong&gt;访问属性（Accessing properties）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;有两种方式来访问对象的属性，点操作符或者中括号操作符。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_334986" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code &gt;var&lt;/code&gt; &lt;code &gt;foo = {name: &lt;/code&gt;&lt;code &gt;'Kitten'&lt;/code&gt;&lt;code &gt;}&amp;lt;br&amp;gt;foo.name; &lt;/code&gt;&lt;code &gt;// kitten&amp;lt;br&amp;gt;foo['name']; // kitten&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;var get = 'name';&amp;lt;br&amp;gt;foo[get]; // kitten&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;foo.1234; // SyntaxError&amp;lt;br&amp;gt;foo['1234']; // works&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;两种语法是等价的，但是中括号操作符在下面两种情况下依然有效 - 动态设置属性 - 属性名不是一个有效的变量名（译者注：比如属性名中包含空格，或者属性名是JS的关键词） （译者注：在&lt;a href="http://www.jslint.com/"&gt;JSLint&lt;/a&gt;语法检测工具中，点操作符是推荐做法）&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;删除属性（Deleting properties）&lt;/strong&gt;&lt;/p&gt;&#xD;
&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;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_144515" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code &gt;var&lt;/code&gt; &lt;code &gt;obj = {&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; bar: 1,&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; foo: 2,&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; baz: 3&amp;lt;br&amp;gt;};&amp;lt;br&amp;gt;obj.bar = undefined;&amp;lt;br&amp;gt;obj.foo = &lt;/code&gt;&lt;code &gt;null&lt;/code&gt;&lt;code &gt;;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;code &gt;delete&lt;/code&gt; &lt;code &gt;obj.baz;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;code &gt;for&lt;/code&gt;&lt;code &gt;(&lt;/code&gt;&lt;code &gt;var&lt;/code&gt; &lt;code &gt;i &lt;/code&gt;&lt;code &gt;in&lt;/code&gt; &lt;code &gt;obj) {&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;if&lt;/code&gt; &lt;code &gt;(obj.hasOwnProperty(i)) {&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; console.log(i, &lt;/code&gt;&lt;code &gt;''&lt;/code&gt; &lt;code &gt;+ obj[i]);&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;lt;br&amp;gt;}&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&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;&#xD;
&lt;p&gt;&lt;strong&gt;属性名的语法（Notation of keys）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_480959" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code &gt;var&lt;/code&gt; &lt;code &gt;test = {&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;'case'&lt;/code&gt;&lt;code &gt;: &lt;/code&gt;&lt;code &gt;'I am a keyword so I must be notated as a string'&lt;/code&gt;&lt;code &gt;,&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;delete&lt;/code&gt;&lt;code &gt;: &lt;/code&gt;&lt;code &gt;'I am a keyword too so me'&lt;/code&gt; &lt;code &gt;// 出错：SyntaxError&amp;lt;br&amp;gt;};&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;对象的属性名可以使用字符串或者普通字符声明。但是由于JavaScript解析器的另一个错误设计， 上面的第二种声明方式在ECMAScript 5之前会抛出&lt;code&gt;SyntaxError&lt;/code&gt;的错误。&lt;/p&gt;&#xD;
&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;&#xD;
&lt;h2 id="prototype"&gt;原型（The prototype） &lt;a href="#intro"&gt;#top&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;JavaScript不包含传统的类继承模型，而是使用&lt;em&gt;prototypical&lt;/em&gt;原型模型。&lt;/p&gt;&#xD;
&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;&#xD;
&lt;p&gt;由于JavaScript是唯一一个被广泛使用的基于原型继承的语言，所以理解两种继承模式的差异是需要一定时间的。&lt;/p&gt;&#xD;
&lt;p&gt;第一个不同之处在于JavaScript使用&lt;em&gt;原型链&lt;/em&gt;的继承方式。&lt;/p&gt;&#xD;
&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;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_984413" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code &gt;function&lt;/code&gt; &lt;code &gt;Foo() {&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;this&lt;/code&gt;&lt;code &gt;.value = 42;&amp;lt;br&amp;gt;}&amp;lt;br&amp;gt;Foo.prototype = {&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; method: &lt;/code&gt;&lt;code &gt;function&lt;/code&gt;&lt;code &gt;() {}&amp;lt;br&amp;gt;};&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;code &gt;function&lt;/code&gt; &lt;code &gt;Bar() {}&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;code &gt;// 设置Bar的prototype属性为Foo的实例对象&amp;lt;br&amp;gt;Bar.prototype = new Foo();&amp;lt;br&amp;gt;Bar.prototype.foo = 'Hello World';&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;// 修正Bar.prototype.constructor为Bar本身&amp;lt;br&amp;gt;Bar.prototype.constructor = Bar;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;var test = new Bar() // 创建Bar的一个新实例&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;// 原型链&amp;lt;br&amp;gt;test [Bar的实例]&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Bar.prototype [Foo的实例] &amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { foo: 'Hello World' }&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Foo.prototype&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {method: ...};&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Object.prototype&amp;lt;br&amp;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; {toString: ... /* etc. */};&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&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;code&gt;Foo&lt;/code&gt;的实例属性&lt;code&gt;value&lt;/code&gt;， 因为这个属性在&lt;code&gt;Foo&lt;/code&gt;的&lt;a href="#constructor"&gt;构造函数&lt;/a&gt;中定义。 （But it will not have access to the property &lt;code&gt;value&lt;/code&gt; of a &lt;code&gt;Foo&lt;/code&gt; instance, since that property gets defined in the &lt;a href="#constructor"&gt;constructor&lt;/a&gt;of &lt;code&gt;Foo&lt;/code&gt;. But this constructor has to be called explicitly.）&lt;/p&gt;&#xD;
&lt;p&gt;（译者注：我认为这个描述是错误的，test.value是可以访问的。 因为在设置Bar.prototype = new Foo();时，&lt;code&gt;value&lt;/code&gt;也就成为Bar.prototype上的一个属性。 如果你有不同观点，可以到&lt;a href="http://cnblogs.com/sanshi/"&gt;我的博客&lt;/a&gt;评论。）&lt;/p&gt;&#xD;
&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;&#xD;
&lt;p&gt;&lt;strong&gt;属性查找（Property lookup）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;当查找一个对象的属性时，JavaScript会&lt;strong&gt;向上&lt;/strong&gt;遍历原型链，直到找到给定名称的属性为止。&lt;/p&gt;&#xD;
&lt;p&gt;到查找到达原型链的顶部 - 也就是&lt;code&gt;Object.prototype&lt;/code&gt; - 但是仍然没有找到指定的属性，就会返回&lt;a href="#undefined"&gt;undefined&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;原型属性（The prototype property）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;当原型属性用来创建原型链时，可以把&lt;strong&gt;任何&lt;/strong&gt;类型的值赋给它（prototype）。 然而将原子类型赋给prototype的操作将会被忽略。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_915035" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code &gt;function&lt;/code&gt; &lt;code &gt;Foo() {}&amp;lt;br&amp;gt;Foo.prototype = 1; &lt;/code&gt;&lt;code &gt;// no effect&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;而将对象赋值给prototype，正如上面的例子所示，将会动态的创建原型链。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;性能（Performance）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;如果一个属性在原型链的上端，则对于查找时间将带来不利影响。特别的，试图获取一个不存在的属性将会遍历整个原型链。&lt;/p&gt;&#xD;
&lt;p&gt;并且，当使用&lt;a href="#the-for-in-loop"&gt;for-in&lt;/a&gt;循环遍历对象的属性时，原型链上的&lt;strong&gt;所有&lt;/strong&gt;属性都将被访问。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;扩展内置类型的原型（Extension of native prototypes）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;一个错误特性被经常使用，那就是扩展&lt;code&gt;Object.prototype&lt;/code&gt;或者其他内置类型的原型对象。&lt;/p&gt;&#xD;
&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;。虽然它被广泛的应用到一些JS类库中比如&lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt;,但是我仍然不认为为内置类型添加一些&lt;em&gt;非标准&lt;/em&gt;的函数是个好主意。&lt;/p&gt;&#xD;
&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;a href="http://en.wikipedia.org/wiki/Backport"&gt;Backport&lt;/a&gt;，也就是将新的补丁添加到老版本中。）The &lt;strong&gt;only&lt;/strong&gt; good reason for extending a built-in prototype is to backport&amp;nbsp; the features of newer JavaScript engines; for example, &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;&#xD;
&lt;p&gt;&lt;strong&gt;总结（In conclusion）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在写复杂的JavaScript应用之前，充分理解原型链继承的工作方式是每个JavaScript程序员&lt;strong&gt;必修&lt;/strong&gt;的功课。 要提防原型链过长带来的性能问题，并知道如何通过缩短原型链来提高性能。 更进一步，绝对&lt;strong&gt;不要&lt;/strong&gt;扩展内置类型的原型，除非是为了和新的JavaScript引擎兼容。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2327628.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327628.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2012/01/19/2327624.html</id><title type="text">JavaScript继承详解（六）</title><summary type="text">在本章中，我们将分析Prototypejs中关于JavaScript继承的实现。 Prototypejs是最早的JavaScript类库，可以说是JavaScript类库的鼻祖。 我在几年前接触的第一个JavaScript类库就是这位，因此Prototypejs有着广泛的群众基础。 不过当年Prototypejs中的关于继承的实现相当的简单，源代码就寥寥几行，我们来看下。 早期Prototypejs中继承的实现 源码： var Class = { // Class.create仅仅返回另外一个函数，此函数执行时将调用原型方法initialize ...</summary><published>2012-01-19T13:38:00Z</published><updated>2012-01-19T13:38:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327624.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327624.html"/><content type="html">&lt;p&gt;在本章中，我们将分析&lt;a href="http://prototypejs.org/"&gt;Prototypejs&lt;/a&gt;中关于JavaScript继承的实现。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Prototypejs是最早的JavaScript类库，可以说是JavaScript类库的鼻祖。 我在几年前接触的第一个JavaScript类库就是这位，因此Prototypejs有着广泛的群众基础。&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;&amp;nbsp;&amp;nbsp; 不过当年Prototypejs中的关于继承的实现相当的简单，源代码就寥寥几行，我们来看下。&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 早期Prototypejs中继承的实现&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;源码：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Class = {&#xD;
            // Class.create仅仅返回另外一个函数，此函数执行时将调用原型方法initialize&#xD;
            create: function() {&#xD;
                return function() {&#xD;
                    this.initialize.apply(this, arguments);&#xD;
                }&#xD;
            }&#xD;
        };&#xD;
        &#xD;
        // 对象的扩展&#xD;
        Object.extend = function(destination, source) {&#xD;
            for (var property in source) {&#xD;
                destination[property] = source[property];&#xD;
            }&#xD;
            return destination;&#xD;
        };&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;调用方式：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Person = Class.create();&#xD;
        Person.prototype = {&#xD;
            initialize: function(name) {&#xD;
                this.name = name;&#xD;
            },&#xD;
            getName: function(prefix) {&#xD;
                return prefix + this.name;&#xD;
            }&#xD;
        };&#xD;
&#xD;
        var Employee = Class.create();&#xD;
        Employee.prototype = Object.extend(new Person(), {&#xD;
            initialize: function(name, employeeID) {&#xD;
                this.name = name;&#xD;
                this.employeeID = employeeID;&#xD;
            },&#xD;
            getName: function() {&#xD;
                return "Employee name: " + this.name;&#xD;
            }&#xD;
        });&#xD;
&#xD;
&#xD;
        var zhang = new Employee("ZhangSan", "1234");&#xD;
        console.log(zhang.getName());   // "Employee name: ZhangSan"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;很原始的感觉对吧，在子类函数中没有提供调用父类函数的途径。&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Prototypejs 1.6以后的继承实现&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;首先来看下调用方式：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 通过Class.create创建一个新类&#xD;
        var Person = Class.create({&#xD;
            // initialize是构造函数&#xD;
            initialize: function(name) {&#xD;
                this.name = name;&#xD;
            },&#xD;
            getName: function(prefix) {&#xD;
                return prefix + this.name;&#xD;
            }&#xD;
        });&#xD;
        &#xD;
        // Class.create的第一个参数是要继承的父类&#xD;
        var Employee = Class.create(Person, {&#xD;
            // 通过将子类函数的第一个参数设为$super来引用父类的同名函数&#xD;
            // 比较有创意，不过内部实现应该比较复杂，至少要用一个闭包来设置$super的上下文this指向当前对象&#xD;
            initialize: function($super, name, employeeID) {&#xD;
                $super(name);&#xD;
                this.employeeID = employeeID;&#xD;
            },&#xD;
            getName: function($super) {&#xD;
                return $super("Employee name: ");&#xD;
            }&#xD;
        });&#xD;
&#xD;
&#xD;
        var zhang = new Employee("ZhangSan", "1234");&#xD;
        console.log(zhang.getName());   // "Employee name: ZhangSan"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;这里我们将Prototypejs 1.6.0.3中继承实现单独取出来， 那些不想引用整个prototype库而只想使用prototype式继承的朋友， 可以直接把下面代码拷贝出来保存为JS文件就行了。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Prototype = {&#xD;
            emptyFunction: function() { }&#xD;
        };&#xD;
&#xD;
        var Class = {&#xD;
            create: function() {&#xD;
                var parent = null, properties = $A(arguments);&#xD;
                if (Object.isFunction(properties[0]))&#xD;
                    parent = properties.shift();&#xD;
&#xD;
                function klass() {&#xD;
                    this.initialize.apply(this, arguments);&#xD;
                }&#xD;
&#xD;
                Object.extend(klass, Class.Methods);&#xD;
                klass.superclass = parent;&#xD;
                klass.subclasses = [];&#xD;
&#xD;
                if (parent) {&#xD;
                    var subclass = function() { };&#xD;
                    subclass.prototype = parent.prototype;&#xD;
                    klass.prototype = new subclass;&#xD;
                    parent.subclasses.push(klass);&#xD;
                }&#xD;
&#xD;
                for (var i = 0; i &amp;lt; properties.length; i++)&#xD;
                    klass.addMethods(properties[i]);&#xD;
&#xD;
                if (!klass.prototype.initialize)&#xD;
                    klass.prototype.initialize = Prototype.emptyFunction;&#xD;
&#xD;
                klass.prototype.constructor = klass;&#xD;
&#xD;
                return klass;&#xD;
            }&#xD;
        };&#xD;
&#xD;
        Class.Methods = {&#xD;
            addMethods: function(source) {&#xD;
                var ancestor = this.superclass &amp;amp;&amp;amp; this.superclass.prototype;&#xD;
                var properties = Object.keys(source);&#xD;
&#xD;
                if (!Object.keys({ toString: true }).length)&#xD;
                    properties.push("toString", "valueOf");&#xD;
&#xD;
                for (var i = 0, length = properties.length; i &amp;lt; length; i++) {&#xD;
                    var property = properties[i], value = source[property];&#xD;
                    if (ancestor &amp;amp;&amp;amp; Object.isFunction(value) &amp;amp;&amp;amp; value.argumentNames().first() == "$super") {&#xD;
                        var method = value;&#xD;
                        value = (function(m) {&#xD;
                            return function() { return ancestor[m].apply(this, arguments) };&#xD;
                        })(property).wrap(method);&#xD;
&#xD;
                        value.valueOf = method.valueOf.bind(method);&#xD;
                        value.toString = method.toString.bind(method);&#xD;
                    }&#xD;
                    this.prototype[property] = value;&#xD;
                }&#xD;
&#xD;
                return this;&#xD;
            }&#xD;
        };&#xD;
&#xD;
        Object.extend = function(destination, source) {&#xD;
            for (var property in source)&#xD;
                destination[property] = source[property];&#xD;
            return destination;&#xD;
        };&#xD;
&#xD;
        function $A(iterable) {&#xD;
            if (!iterable) return [];&#xD;
            if (iterable.toArray) return iterable.toArray();&#xD;
            var length = iterable.length || 0, results = new Array(length);&#xD;
            while (length--) results[length] = iterable[length];&#xD;
            return results;&#xD;
        }&#xD;
&#xD;
        Object.extend(Object, {&#xD;
            keys: function(object) {&#xD;
                var keys = [];&#xD;
                for (var property in object)&#xD;
                    keys.push(property);&#xD;
                return keys;&#xD;
            },&#xD;
            isFunction: function(object) {&#xD;
                return typeof object == "function";&#xD;
            },&#xD;
            isUndefined: function(object) {&#xD;
                return typeof object == "undefined";&#xD;
            }&#xD;
        });&#xD;
&#xD;
        Object.extend(Function.prototype, {&#xD;
            argumentNames: function() {&#xD;
                var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(',');&#xD;
                return names.length == 1 &amp;amp;&amp;amp; !names[0] ? [] : names;&#xD;
            },&#xD;
            bind: function() {&#xD;
                if (arguments.length &amp;lt; 2 &amp;amp;&amp;amp; Object.isUndefined(arguments[0])) return this;&#xD;
                var __method = this, args = $A(arguments), object = args.shift();&#xD;
                return function() {&#xD;
                    return __method.apply(object, args.concat($A(arguments)));&#xD;
                }&#xD;
            },&#xD;
            wrap: function(wrapper) {&#xD;
                var __method = this;&#xD;
                return function() {&#xD;
                    return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));&#xD;
                }&#xD;
            }&#xD;
        });&#xD;
&#xD;
        Object.extend(Array.prototype, {&#xD;
            first: function() {&#xD;
                return this[0];&#xD;
            }&#xD;
        });&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;首先，我们需要先解释下Prototypejs中一些方法的定义。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;argumentNames: 获取函数的参数数组&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;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;function init($super, name, employeeID) {&#xD;
                }&#xD;
                console.log(init.argumentNames().join(",")); // "$super,name,employeeID"&#xD;
                &lt;/pre&gt;&#xD;
&lt;/li&gt;&#xD;
&lt;li&gt;bind: 绑定函数的上下文this到一个新的对象（一般是函数的第一个参数）&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;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var name = "window";&#xD;
                var p = {&#xD;
                    name: "Lisi",&#xD;
                    getName: function() {&#xD;
                        return this.name;&#xD;
                    }&#xD;
                };&#xD;
&#xD;
                console.log(p.getName());   // "Lisi"&#xD;
                console.log(p.getName.bind(window)());  // "window"&#xD;
                &lt;/pre&gt;&#xD;
&lt;/li&gt;&#xD;
&lt;li&gt;wrap: 把当前调用函数作为包裹器wrapper函数的第一个参数&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;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var name = "window";&#xD;
                var p = {&#xD;
                    name: "Lisi",&#xD;
                    getName: function() {&#xD;
                        return this.name;&#xD;
                    }&#xD;
                };&#xD;
&#xD;
                function wrapper(originalFn) {&#xD;
                    return "Hello: " + originalFn();&#xD;
                }&#xD;
&#xD;
                console.log(p.getName());   // "Lisi"&#xD;
                console.log(p.getName.bind(window)());  // "window"&#xD;
                console.log(p.getName.wrap(wrapper)()); // "Hello: window"&#xD;
                console.log(p.getName.wrap(wrapper).bind(p)()); // "Hello: Lisi"&#xD;
                &lt;/pre&gt;&#xD;
有一点绕口，对吧。这里要注意的是wrap和bind调用返回的都是函数，把握住这个原则，就很容易看清本质了。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;对这些函数有了一定的认识之后，我们再来解析Prototypejs继承的核心内容。&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; 这里有两个重要的定义，一个是Class.extend，另一个是Class.Methods.addMethods。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Class = {&#xD;
            create: function() {&#xD;
                // 如果第一个参数是函数，则作为父类&#xD;
                var parent = null, properties = $A(arguments);&#xD;
                if (Object.isFunction(properties[0]))&#xD;
                    parent = properties.shift();&#xD;
&#xD;
                // 子类构造函数的定义&#xD;
                function klass() {&#xD;
                    this.initialize.apply(this, arguments);&#xD;
                }&#xD;
                &#xD;
                // 为子类添加原型方法Class.Methods.addMethods&#xD;
                Object.extend(klass, Class.Methods);&#xD;
                // 不仅为当前类保存父类的引用，同时记录了所有子类的引用&#xD;
                klass.superclass = parent;&#xD;
                klass.subclasses = [];&#xD;
&#xD;
                if (parent) {&#xD;
                    // 核心代码 - 如果父类存在，则实现原型的继承&#xD;
                    // 这里为创建类时不调用父类的构造函数提供了一种新的途径&#xD;
                    // - 使用一个中间过渡类，这和我们以前使用全局initializing变量达到相同的目的，&#xD;
                    // - 但是代码更优雅一点。&#xD;
                    var subclass = function() { };&#xD;
                    subclass.prototype = parent.prototype;&#xD;
                    klass.prototype = new subclass;&#xD;
                    parent.subclasses.push(klass);&#xD;
                }&#xD;
&#xD;
                // 核心代码 - 如果子类拥有父类相同的方法，则特殊处理，将会在后面详解&#xD;
                for (var i = 0; i &amp;lt; properties.length; i++)&#xD;
                    klass.addMethods(properties[i]);&#xD;
&#xD;
                if (!klass.prototype.initialize)&#xD;
                    klass.prototype.initialize = Prototype.emptyFunction;&#xD;
                &#xD;
                // 修正constructor指向错误&#xD;
                klass.prototype.constructor = klass;&#xD;
&#xD;
                return klass;&#xD;
            }&#xD;
        };&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;再来看addMethods做了哪些事情：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;Class.Methods = {&#xD;
            addMethods: function(source) {&#xD;
                // 如果父类存在，ancestor指向父类的原型对象&#xD;
                var ancestor = this.superclass &amp;amp;&amp;amp; this.superclass.prototype;&#xD;
                var properties = Object.keys(source);&#xD;
                // Firefox和Chrome返回1，IE8返回0，所以这个地方特殊处理&#xD;
                if (!Object.keys({ toString: true }).length)&#xD;
                    properties.push("toString", "valueOf");&#xD;
&#xD;
                // 循环子类原型定义的所有属性，对于那些和父类重名的函数要重新定义&#xD;
                for (var i = 0, length = properties.length; i &amp;lt; length; i++) {&#xD;
                    // property为属性名，value为属性体（可能是函数，也可能是对象）&#xD;
                    var property = properties[i], value = source[property];&#xD;
                    // 如果父类存在，并且当前当前属性是函数，并且此函数的第一个参数为 $super&#xD;
                    if (ancestor &amp;amp;&amp;amp; Object.isFunction(value) &amp;amp;&amp;amp; value.argumentNames().first() == "$super") {&#xD;
                        var method = value;&#xD;
                        // 下面三行代码是精华之所在，大概的意思：&#xD;
                        // - 首先创建一个自执行的匿名函数返回另一个函数，此函数用于执行父类的同名函数&#xD;
                        // - （因为这是在循环中，我们曾多次指出循环中的函数引用局部变量的问题）&#xD;
                        // - 其次把这个自执行的匿名函数的作为method的第一个参数（也就是对应于形参$super）&#xD;
                        // 不过，窃以为这个地方作者有点走火入魔，完全没必要这么复杂，后面我会详细分析这段代码。&#xD;
                        value = (function(m) {&#xD;
                            return function() { return ancestor[m].apply(this, arguments) };&#xD;
                        })(property).wrap(method);&#xD;
&#xD;
                        value.valueOf = method.valueOf.bind(method);&#xD;
                        // 因为我们改变了函数体，所以重新定义函数的toString方法&#xD;
                        // 这样用户调用函数的toString方法时，返回的是原始的函数定义体&#xD;
                        value.toString = method.toString.bind(method);&#xD;
                    }&#xD;
                    this.prototype[property] = value;&#xD;
                }&#xD;
&#xD;
                return this;&#xD;
            }&#xD;
        };&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;上面的代码中我曾有&amp;ldquo;走火入魔&amp;rdquo;的说法，并不是对作者的亵渎，&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 只是觉得作者对JavaScript中的一个重要准则（通过自执行的匿名函数创建作用域）&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;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;value = (function(m) {&#xD;
            return function() { return ancestor[m].apply(this, arguments) };&#xD;
        })(property).wrap(method);&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;其实这段代码和下面的效果一样：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;value = ancestor[property].wrap(method);&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;我们把wrap函数展开就能看的更清楚了：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;value = (function(fn, wrapper) {&#xD;
            var __method = fn;&#xD;
            return function() {&#xD;
                return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));&#xD;
            }&#xD;
        })(ancestor[property], method);&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;可以看到，我们其实为父类的函数ancestor[property]通过自执行的匿名函数创建了作用域。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 而原作者是为property创建的作用域。两则的最终效果是一致的。&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;我们对Prototypejs继承的重实现&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&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; 下面我们就用前几章我们自己实现的jClass来实现Prototypejs形式的继承。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 注意：这是我们自己实现的类似Prototypejs继承方式的代码，可以直接拷贝下来使用&#xD;
        &#xD;
        // 这个方法是借用Prototypejs中的定义&#xD;
        function argumentNames(fn) {&#xD;
            var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(',');&#xD;
            return names.length == 1 &amp;amp;&amp;amp; !names[0] ? [] : names;&#xD;
        }&#xD;
&#xD;
&#xD;
        function jClass(baseClass, prop) {&#xD;
            // 只接受一个参数的情况 - jClass(prop)&#xD;
            if (typeof (baseClass) === "object") {&#xD;
                prop = baseClass;&#xD;
                baseClass = null;&#xD;
            }&#xD;
&#xD;
            // 本次调用所创建的类（构造函数）&#xD;
            function F() {&#xD;
                // 如果父类存在，则实例对象的baseprototype指向父类的原型&#xD;
                // 这就提供了在实例对象中调用父类方法的途径&#xD;
                if (baseClass) {&#xD;
                    this.baseprototype = baseClass.prototype;&#xD;
                }&#xD;
                this.initialize.apply(this, arguments);&#xD;
            }&#xD;
&#xD;
            // 如果此类需要从其它类扩展&#xD;
            if (baseClass) {&#xD;
                var middleClass = function() {};&#xD;
                middleClass.prototype = baseClass.prototype;&#xD;
                F.prototype = new middleClass();&#xD;
                F.prototype.constructor = F;&#xD;
            }&#xD;
&#xD;
            // 覆盖父类的同名函数&#xD;
            for (var name in prop) {&#xD;
                if (prop.hasOwnProperty(name)) {&#xD;
                    // 如果此类继承自父类baseClass并且父类原型中存在同名函数name&#xD;
                    if (baseClass &amp;amp;&amp;amp;&#xD;
                        typeof (prop[name]) === "function" &amp;amp;&amp;amp;&#xD;
                        argumentNames(prop[name])[0] === "$super") {&#xD;
                        // 重定义子类的原型方法prop[name]&#xD;
                        // - 这里面有很多JavaScript方面的技巧，如果阅读有困难的话，可以参阅我前面关于JavaScript Tips and Tricks的系列文章&#xD;
                        // - 比如$super封装了父类方法的调用，但是调用时的上下文指针要指向当前子类的实例对象&#xD;
                        // - 将$super作为方法调用的第一个参数&#xD;
                        F.prototype[name] = (function(name, fn) {&#xD;
                            return function() {&#xD;
                                var that = this;&#xD;
                                $super = function() {&#xD;
                                    return baseClass.prototype[name].apply(that, arguments);&#xD;
                                };&#xD;
                                return fn.apply(this, Array.prototype.concat.apply($super, arguments));&#xD;
                            };&#xD;
                        })(name, prop[name]);&#xD;
                        &#xD;
                    } else {&#xD;
                        F.prototype[name] = prop[name];&#xD;
                    }&#xD;
                }&#xD;
            }&#xD;
&#xD;
            return F;&#xD;
        };&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;调用方式和Prototypejs的调用方式保持一致：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Person = jClass({&#xD;
            initialize: function(name) {&#xD;
                this.name = name;&#xD;
            },&#xD;
            getName: function() {&#xD;
                return this.name;&#xD;
            }&#xD;
        });&#xD;
&#xD;
        var Employee = jClass(Person, {&#xD;
            initialize: function($super, name, employeeID) {&#xD;
                $super(name);&#xD;
                this.employeeID = employeeID;&#xD;
            },&#xD;
            getEmployeeID: function() {&#xD;
                return this.employeeID;&#xD;
            },&#xD;
            getName: function($super) {&#xD;
                return "Employee name: " + $super();&#xD;
            }&#xD;
        });&#xD;
&#xD;
&#xD;
        var zhang = new Employee("ZhangSan", "1234");&#xD;
        console.log(zhang.getName());   // "Employee name: ZhangSan"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;经过本章的学习，就更加坚定了我们的信心，像Prototypejs形式的继承我们也能够轻松搞定。&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; 以后的几个章节，我们会逐步分析mootools,Extjs等JavaScript类库中继承的实现，敬请期待。&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2327624.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327624.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2012/01/19/2327622.html</id><title type="text">JavaScript继承详解（四）</title><summary type="text">在本章中，我们将分析Douglas Crockford关于JavaScript继承的一个实现 - Classical Inheritance in JavaScript。 Crockford是JavaScript开发社区最知名的权威，是JSON、JSLint、JSMin和ADSafe之父，是《JavaScript: The Good Parts》的作者。 现在是Yahoo的资深JavaScript架构师，参与YUI的设计开发。 这里有一篇文章详细介绍了Crockford的生平和著作。 当然Crockford也是我等小辈崇拜的对象。调用方式首先让我们看下使用Crockford式继承的调用方式： </summary><published>2012-01-19T13:37:00Z</published><updated>2012-01-19T13:37:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327622.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327622.html"/><content type="html">&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在本章中，我们将分析Douglas Crockford关于JavaScript继承的一个实现 - &lt;a href="http://javascript.crockford.com/inheritance.html"&gt;Classical Inheritance in JavaScript&lt;/a&gt;。 Crockford是JavaScript开发社区最知名的权威，是&lt;a href="http://www.json.org/"&gt;JSON&lt;/a&gt;、&lt;a href="http://www.jslint.com/"&gt;JSLint&lt;/a&gt;、&lt;a href="http://www.crockford.com/javascript/jsmin.html"&gt;JSMin&lt;/a&gt;和&lt;a href="http://www.adsafe.org/"&gt;ADSafe&lt;/a&gt;之父，是《JavaScript: The Good Parts》的作者。 现在是Yahoo的资深JavaScript架构师，参与YUI的设计开发。 这里有一篇&lt;a href="http://dancewithnet.com/2009/03/29/douglas-crockford/"&gt;文章&lt;/a&gt;详细介绍了Crockford的生平和著作。 当然Crockford也是我等小辈崇拜的对象。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;调用方式&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;首先让我们看下使用Crockford式继承的调用方式： 注意：代码中的method、inherits、uber都是自定义的对象，我们会在后面的代码分析中详解。&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 定义Person类&#xD;
        function Person(name) {&#xD;
            this.name = name;&#xD;
        }&#xD;
        // 定义Person的原型方法&#xD;
        Person.method("getName", function() {&#xD;
            return this.name;&#xD;
        });  &#xD;
        &#xD;
        // 定义Employee类&#xD;
        function Employee(name, employeeID) {&#xD;
            this.name = name;&#xD;
            this.employeeID = employeeID;&#xD;
        }&#xD;
        // 指定Employee类从Person类继承&#xD;
        Employee.inherits(Person);&#xD;
        // 定义Employee的原型方法&#xD;
        Employee.method("getEmployeeID", function() {&#xD;
            return this.employeeID;&#xD;
        });&#xD;
        Employee.method("getName", function() {&#xD;
            // 注意，可以在子类中调用父类的原型方法&#xD;
            return "Employee name: " + this.uber("getName");&#xD;
        });&#xD;
        // 实例化子类&#xD;
        var zhang = new Employee("ZhangSan", "1234");&#xD;
        console.log(zhang.getName());   // "Employee name: ZhangSan"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这里面有几处不得不提的硬伤：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;子类从父类继承的代码必须在子类和父类都定义好之后进行，并且必须在子类原型方法定义之前进行。&lt;/li&gt;&#xD;
&lt;li&gt;虽然子类方法体中可以调用父类的方法，但是子类的构造函数无法调用父类的构造函数。&lt;/li&gt;&#xD;
&lt;li&gt;代码的书写不够优雅，比如原型方法的定义以及调用父类的方法（不直观）。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;当然Crockford的实现还支持子类中的方法调用带参数的父类方法，如下例子：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;function Person(name) {&#xD;
            this.name = name;&#xD;
        }&#xD;
        Person.method("getName", function(prefix) {&#xD;
            return prefix + this.name;&#xD;
        });&#xD;
&#xD;
        function Employee(name, employeeID) {&#xD;
            this.name = name;&#xD;
            this.employeeID = employeeID;&#xD;
        }&#xD;
        Employee.inherits(Person);&#xD;
        Employee.method("getName", function() {&#xD;
            // 注意，uber的第一个参数是要调用父类的函数名称，后面的参数都是此函数的参数&#xD;
            // 个人觉得这样方式不如这样调用来的直观：this.uber("Employee name: ")&#xD;
            return this.uber("getName", "Employee name: ");&#xD;
        });&#xD;
        var zhang = new Employee("ZhangSan", "1234");&#xD;
        console.log(zhang.getName());   // "Employee name: ZhangSan"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;代码分析&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;首先method函数的定义就很简单了：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;Function.prototype.method = function(name, func) {&#xD;
            // this指向当前函数，也即是typeof(this) === "function"&#xD;
            this.prototype[name] = func;&#xD;
            return this;&#xD;
        };&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;要特别注意这里this的指向。当我们看到this时，不能仅仅关注于当前函数，而应该想到当前函数的调用方式。 比如这个例子中的method我们不会通过new的方式调用，所以method中的this指向的是当前函数。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;inherits函数的定义有点复杂：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;Function.method('inherits', function (parent) {&#xD;
            // 关键是这一段：this.prototype = new parent()，这里实现了原型的引用&#xD;
            var d = {}, p = (this.prototype = new parent());&#xD;
            &#xD;
            // 只为子类的原型增加uber方法，这里的Closure是为了在调用uber函数时知道当前类的父类的原型（也即是变量 - v）&#xD;
            this.method('uber', function uber(name) {&#xD;
                // 这里考虑到如果name是存在于Object.prototype中的函数名的情况&#xD;
                // 比如 "toString" in {} === true&#xD;
                if (!(name in d)) {&#xD;
                    // 通过d[name]计数，不理解具体的含义&#xD;
                    d[name] = 0;&#xD;
                }        &#xD;
                var f, r, t = d[name], v = parent.prototype;&#xD;
                if (t) {&#xD;
                    while (t) {&#xD;
                        v = v.constructor.prototype;&#xD;
                        t -= 1;&#xD;
                    }&#xD;
                    f = v[name];&#xD;
                } else {&#xD;
                    // 个人觉得这段代码有点繁琐，既然uber的含义就是父类的函数，那么f直接指向v[name]就可以了&#xD;
                    f = p[name];&#xD;
                    if (f == this[name]) {&#xD;
                        f = v[name];&#xD;
                    }&#xD;
                }&#xD;
                d[name] += 1;&#xD;
                // 执行父类中的函数name，但是函数中this指向当前对象&#xD;
                // 同时注意使用Array.prototype.slice.apply的方式对arguments进行截断（因为arguments不是标准的数组，没有slice方法）&#xD;
                r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));&#xD;
                d[name] -= 1;&#xD;
                return r;&#xD;
            });&#xD;
            return this;&#xD;
        });&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;注意，在inherits函数中还有一个小小的BUG，那就是没有重定义constructor的指向，所以会发生如下的错误：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var zhang = new Employee("ZhangSan", "1234");&#xD;
        console.log(zhang.getName());   // "Employee name: ZhangSan"&#xD;
        console.log(zhang.constructor === Employee);    // false&#xD;
        console.log(zhang.constructor === Person);      // true&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;改进建议&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;根据前面的分析，个人觉得method函数必要性不大，反而容易混淆视线。 而inherits方法可以做一些瘦身（因为Crockford可能考虑更多的情况，原文中介绍了好几种使用inherits的方式，而我们只关注其中的一种）， 并修正了constructor的指向错误。&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;Function.prototype.inherits = function(parent) {&#xD;
            this.prototype = new parent();&#xD;
            this.prototype.constructor = this;&#xD;
            this.prototype.uber = function(name) {&#xD;
                f = parent.prototype[name];&#xD;
                return f.apply(this, Array.prototype.slice.call(arguments, 1));&#xD;
            };&#xD;
        };&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;调用方式：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;function Person(name) {&#xD;
            this.name = name;&#xD;
        }&#xD;
        Person.prototype.getName = function(prefix) {&#xD;
            return prefix + this.name;&#xD;
        };&#xD;
        function Employee(name, employeeID) {&#xD;
            this.name = name;&#xD;
            this.employeeID = employeeID;&#xD;
        }&#xD;
        Employee.inherits(Person);&#xD;
        Employee.prototype.getName = function() {&#xD;
            return this.uber("getName", "Employee name: ");&#xD;
        };&#xD;
        var zhang = new Employee("ZhangSan", "1234");&#xD;
        console.log(zhang.getName());   // "Employee name: ZhangSan"&#xD;
        console.log(zhang.constructor === Employee);   // true&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;有点意思&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在文章的结尾，Crockford居然放出了这样的话：&lt;/p&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;I have been writing JavaScript for 8 years now, and I have never once found need to use an uber function. The super idea is fairly important in the classical pattern, but it appears to be unnecessary in the prototypal and functional patterns. I now see my early attempts to support the classical model in JavaScript as a mistake.&lt;/blockquote&gt;&#xD;
&lt;p&gt;可见Crockford对在JavaScript中实现面向对象的编程不赞成，并且声称JavaScript应该按照原型和函数的模式（the prototypal and functional patterns）进行编程。 不过就我个人而言，在复杂的场景中如果有面向对象的机制会方便的多。 但谁有能担保呢，即使像jQuery UI这样的项目也没用到继承，而另一方面，像Extjs、Qooxdoo则极力倡导一种面向对象的JavaScript。 甚至Cappuccino项目还发明一种Objective-J语言来实践面向对象的JavaScript。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2327622.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327622.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2012/01/19/2327623.html</id><title type="text">JavaScript继承详解（五）</title><summary type="text">在本章中，我们将分析John Resig关于JavaScript继承的一个实现 - Simple JavaScript Inheritance。 John Resig作为jQuery的创始人而声名在外。是《Pro JavaScript Techniques》的作者，而且Resig将会在今年秋天推出一本书《JavaScript Secrets》，非常期待。调用方式调用方式非常优雅： 注意：代码中的Class、extend、_super都是自定义的对象，我们会在后面的代码分析中详解。 var Person = Class.extend({ // init是构造函...</summary><published>2012-01-19T13:37:00Z</published><updated>2012-01-19T13:37:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327623.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327623.html"/><content type="html">&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在本章中，我们将分析John Resig关于JavaScript继承的一个实现 - &lt;a href="http://ejohn.org/blog/simple-javascript-inheritance/"&gt;Simple JavaScript Inheritance&lt;/a&gt;。 John Resig作为&lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;的创始人而声名在外。是《Pro JavaScript Techniques》的作者，而且Resig将会在今年秋天推出一本书《JavaScript Secrets》，非常期待。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;调用方式&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;调用方式非常优雅： 注意：代码中的Class、extend、_super都是自定义的对象，我们会在后面的代码分析中详解。&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Person = Class.extend({&#xD;
            // init是构造函数&#xD;
            init: function(name) {&#xD;
                this.name = name;&#xD;
            },&#xD;
            getName: function() {&#xD;
                return this.name;&#xD;
            }&#xD;
        });&#xD;
        // Employee类从Person类继承&#xD;
        var Employee = Person.extend({&#xD;
            // init是构造函数&#xD;
            init: function(name, employeeID) {&#xD;
                //  在构造函数中调用父类的构造函数&#xD;
                this._super(name);&#xD;
                this.employeeID = employeeID;&#xD;
            },&#xD;
            getEmployeeID: function() {&#xD;
                return this.employeeID;&#xD;
            },&#xD;
            getName: function() {&#xD;
                //  调用父类的方法&#xD;
                return "Employee name: " + this._super();&#xD;
            }&#xD;
        });&#xD;
&#xD;
        var zhang = new Employee("ZhangSan", "1234");&#xD;
        console.log(zhang.getName());   // "Employee name: ZhangSan"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;说实话，对于完成本系列文章的目标-继承-而言，真找不到什么缺点。方法一如jQuery一样简洁明了。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;代码分析&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;为了一个漂亮的调用方式，内部实现的确复杂了很多，不过这些也是值得的 - 一个人的思考带给了无数程序员快乐的微笑 - 嘿嘿，有点肉麻。 不过其中的一段代码的确迷惑我一段时间：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; &#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;我曾在几天前的博客中写过一篇文章专门阐述这个问题，有兴趣可以向前翻一翻。&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 自执行的匿名函数创建一个上下文，避免引入全局变量&#xD;
        (function() {&#xD;
            // initializing变量用来标示当前是否处于类的创建阶段，&#xD;
            // - 在类的创建阶段是不能调用原型方法init的&#xD;
            // - 我们曾在本系列的第三篇文章中详细阐述了这个问题&#xD;
            // fnTest是一个正则表达式，可能的取值为（/\b_super\b/ 或 /.*/）&#xD;
            // - 对 /xyz/.test(function() { xyz; }) 的测试是为了检测浏览器是否支持test参数为函数的情况&#xD;
            // - 不过我对IE7.0,Chrome2.0,FF3.5进行了测试，此测试都返回true。&#xD;
            // - 所以我想这样对fnTest赋值大部分情况下也是对的：fnTest = /\b_super\b/;&#xD;
            var initializing = false, fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;&#xD;
            // 基类构造函数&#xD;
            // 这里的this是window，所以这整段代码就向外界开辟了一扇窗户 - window.Class&#xD;
            this.Class = function() { };&#xD;
            // 继承方法定义&#xD;
            Class.extend = function(prop) {&#xD;
                // 这个地方很是迷惑人，还记得我在本系列的第二篇文章中提到的么&#xD;
                // - this具体指向什么不是定义时能决定的，而是要看此函数是怎么被调用的&#xD;
                // - 我们已经知道extend肯定是作为方法调用的，而不是作为构造函数&#xD;
                // - 所以这里this指向的不是Object，而是Function（即是Class），那么this.prototype就是父类的原型对象&#xD;
                // - 注意：_super指向父类的原型对象，我们会在后面的代码中多次碰见这个变量&#xD;
                var _super = this.prototype;&#xD;
                // 通过将子类的原型指向父类的一个实例对象来完成继承&#xD;
                // - 注意：this是基类构造函数（即是Class）&#xD;
                initializing = true;&#xD;
                var prototype = new this();&#xD;
                initializing = false;&#xD;
                // 我觉得这段代码是经过作者优化过的，所以读起来非常生硬，我会在后面详解&#xD;
                for (var name in prop) {&#xD;
                    prototype[name] = typeof prop[name] == "function" &amp;amp;&amp;amp;&#xD;
                        typeof _super[name] == "function" &amp;amp;&amp;amp; fnTest.test(prop[name]) ?&#xD;
                        (function(name, fn) {&#xD;
                            return function() {&#xD;
                                var tmp = this._super;&#xD;
                                this._super = _super[name];&#xD;
                                var ret = fn.apply(this, arguments);&#xD;
                                this._super = tmp;&#xD;
                                return ret;&#xD;
                            };&#xD;
                        })(name, prop[name]) :&#xD;
                        prop[name];&#xD;
                }&#xD;
                // 这个地方可以看出，Resig很会伪装哦&#xD;
                // - 使用一个同名的局部变量来覆盖全局变量，很是迷惑人&#xD;
                // - 如果你觉得拗口的话，完全可以使用另外一个名字，比如function F()来代替function Class()&#xD;
                // - 注意：这里的Class不是在最外层定义的那个基类构造函数&#xD;
                function Class() {&#xD;
                    // 在类的实例化时，调用原型方法init&#xD;
                    if (!initializing &amp;amp;&amp;amp; this.init)&#xD;
                        this.init.apply(this, arguments);&#xD;
                }&#xD;
                // 子类的prototype指向父类的实例（完成继承的关键）&#xD;
                Class.prototype = prototype;&#xD;
                // 修正constructor指向错误&#xD;
                Class.constructor = Class;&#xD;
                // 子类自动获取extend方法，arguments.callee指向当前正在执行的函数&#xD;
                Class.extend = arguments.callee;&#xD;
                return Class;&#xD;
            };&#xD;
        })();&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;下面我会对其中的for-in循环进行解读，把自执行的匿名方法用一个局部函数来替换， 这样有利于我们看清真相：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;(function() {&#xD;
            var initializing = false, fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;&#xD;
            this.Class = function() { };&#xD;
            Class.extend = function(prop) {&#xD;
                var _super = this.prototype;&#xD;
                initializing = true;&#xD;
                var prototype = new this();&#xD;
                initializing = false;&#xD;
&#xD;
                // 如果父类和子类有同名方法，并且子类中此方法（name）通过_super调用了父类方法&#xD;
                // - 则重新定义此方法&#xD;
                function fn(name, fn) {&#xD;
                    return function() {&#xD;
                        // 将实例方法_super保护起来。&#xD;
                        // 个人觉得这个地方没有必要，因为每次调用这样的函数时都会对this._super重新定义。&#xD;
                        var tmp = this._super;&#xD;
                        // 在执行子类的实例方法name时，添加另外一个实例方法_super，此方法指向父类的同名方法&#xD;
                        this._super = _super[name];&#xD;
                        // 执行子类的方法name，注意在方法体内this._super可以调用父类的同名方法&#xD;
                        var ret = fn.apply(this, arguments);&#xD;
                        this._super = tmp;&#xD;
                        &#xD;
                        // 返回执行结果&#xD;
                        return ret;&#xD;
                    };&#xD;
                }&#xD;
                // 拷贝prop中的所有属性到子类原型中&#xD;
                for (var name in prop) {&#xD;
                    // 如果prop和父类中存在同名的函数，并且此函数中使用了_super方法，则对此方法进行特殊处理 - fn&#xD;
                    // 否则将此方法prop[name]直接赋值给子类的原型&#xD;
                    if (typeof prop[name] === "function" &amp;amp;&amp;amp;&#xD;
                            typeof _super[name] === "function" &amp;amp;&amp;amp; fnTest.test(prop[name])) {&#xD;
                        prototype[name] = fn(name, prop[name]);&#xD;
                    } else {&#xD;
                        prototype[name] = prop[name];&#xD;
                    }&#xD;
                }&#xD;
&#xD;
                function Class() {&#xD;
                    if (!initializing &amp;amp;&amp;amp; this.init) {&#xD;
                        this.init.apply(this, arguments);&#xD;
                    }&#xD;
                }&#xD;
                Class.prototype = prototype;&#xD;
                Class.constructor = Class;&#xD;
                Class.extend = arguments.callee;&#xD;
                return Class;&#xD;
            };&#xD;
        })();&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;写到这里，大家是否觉得Resig的实现和我们在第三章一步一步实现的jClass很类似。 其实在写这一系列的文章之前，我已经对prototype、mootools、extjs、 jQuery-Simple-Inheritance、Crockford-Classical-Inheritance这些实现有一定的了解，并且大部分都在实际项目中使用过。 在第三章中实现jClass也参考了Resig的实现，在此向Resig表示感谢。 下来我们就把jClass改造成和这里的Class具有相同的行为。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;我们的实现&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;将我们在第三章实现的jClass改造成目前John Resig所写的形式相当简单，只需要修改其中的两三行就行了：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;(function() {&#xD;
            // 当前是否处于创建类的阶段&#xD;
            var initializing = false;&#xD;
            jClass = function() { };&#xD;
            jClass.extend = function(prop) {&#xD;
                // 如果调用当前函数的对象（这里是函数）不是Class，则是父类&#xD;
                var baseClass = null;&#xD;
                if (this !== jClass) {&#xD;
                    baseClass = this;&#xD;
                }&#xD;
                // 本次调用所创建的类（构造函数）&#xD;
                function F() {&#xD;
                    // 如果当前处于实例化类的阶段，则调用init原型函数&#xD;
                    if (!initializing) {&#xD;
                        // 如果父类存在，则实例对象的baseprototype指向父类的原型&#xD;
                        // 这就提供了在实例对象中调用父类方法的途径&#xD;
                        if (baseClass) {&#xD;
                            this._superprototype = baseClass.prototype;&#xD;
                        }&#xD;
                        this.init.apply(this, arguments);&#xD;
                    }&#xD;
                }&#xD;
                // 如果此类需要从其它类扩展&#xD;
                if (baseClass) {&#xD;
                    initializing = true;&#xD;
                    F.prototype = new baseClass();&#xD;
                    F.prototype.constructor = F;&#xD;
                    initializing = false;&#xD;
                }&#xD;
                // 新创建的类自动附加extend函数&#xD;
                F.extend = arguments.callee;&#xD;
&#xD;
                // 覆盖父类的同名函数&#xD;
                for (var name in prop) {&#xD;
                    if (prop.hasOwnProperty(name)) {&#xD;
                        // 如果此类继承自父类baseClass并且父类原型中存在同名函数name&#xD;
                        if (baseClass &amp;amp;&amp;amp;&#xD;
                        typeof (prop[name]) === "function" &amp;amp;&amp;amp;&#xD;
                        typeof (F.prototype[name]) === "function" &amp;amp;&amp;amp;&#xD;
                        /\b_super\b/.test(prop[name])) {&#xD;
                            // 重定义函数name - &#xD;
                            // 首先在函数上下文设置this._super指向父类原型中的同名函数&#xD;
                            // 然后调用函数prop[name]，返回函数结果&#xD;
                            // 注意：这里的自执行函数创建了一个上下文，这个上下文返回另一个函数，&#xD;
                            // 此函数中可以应用此上下文中的变量，这就是闭包（Closure）。&#xD;
                            // 这是JavaScript框架开发中常用的技巧。&#xD;
                            F.prototype[name] = (function(name, fn) {&#xD;
                                return function() {&#xD;
                                    this._super = baseClass.prototype[name];&#xD;
                                    return fn.apply(this, arguments);&#xD;
                                };&#xD;
                            })(name, prop[name]);&#xD;
                        } else {&#xD;
                            F.prototype[name] = prop[name];&#xD;
                        }&#xD;
                    }&#xD;
                }&#xD;
                return F;&#xD;
            };&#xD;
        })();&#xD;
        // 经过改造的jClass&#xD;
        var Person = jClass.extend({&#xD;
            init: function(name) {&#xD;
                this.name = name;&#xD;
            },&#xD;
            getName: function(prefix) {&#xD;
                return prefix + this.name;&#xD;
            }&#xD;
        });&#xD;
        var Employee = Person.extend({&#xD;
            init: function(name, employeeID) {&#xD;
                //  调用父类的方法&#xD;
                this._super(name);&#xD;
                this.employeeID = employeeID;&#xD;
            },&#xD;
            getEmployeeIDName: function() {&#xD;
                // 注意：我们还可以通过这种方式调用父类中的其他函数&#xD;
                var name = this._superprototype.getName.call(this, "Employee name: ");&#xD;
                return name + ", Employee ID: " + this.employeeID;&#xD;
            },&#xD;
            getName: function() {&#xD;
                //  调用父类的方法&#xD;
                return this._super("Employee name: ");&#xD;
            }&#xD;
        });&#xD;
&#xD;
        var zhang = new Employee("ZhangSan", "1234");&#xD;
        console.log(zhang.getName());   // "Employee name: ZhangSan"&#xD;
        console.log(zhang.getEmployeeIDName()); // "Employee name: ZhangSan, Employee ID: 1234"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;JUST COOL!&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2327623.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327623.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2012/01/19/2327616.html</id><title type="text">JavaScript继承详解（三）</title><summary type="text">在第一章中，我们使用构造函数和原型的方式在JavaScript的世界中实现了类和继承， 但是存在很多问题。这一章我们将会逐一分析这些问题，并给出解决方案。 注：本章中的jClass的实现参考了Simple JavaScript Inheritance的做法。首先让我们来回顾一下第一章中介绍的例子： function Person(name) { this.name = name; } Person.prototype = { getName: function() { return this.name; } } function Employee(name, employeeID) { th.</summary><published>2012-01-19T13:32:00Z</published><updated>2012-01-19T13:32:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327616.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327616.html"/><content type="html">&lt;p&gt;在第一章中，我们使用构造函数和原型的方式在JavaScript的世界中实现了类和继承， 但是存在很多问题。这一章我们将会逐一分析这些问题，并给出解决方案。&lt;/p&gt;&#xD;
&lt;p&gt;注：本章中的jClass的实现参考了&lt;a href="http://ejohn.org/blog/simple-javascript-inheritance/"&gt;Simple&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JavaScript Inheritance&lt;/a&gt;的做法。&lt;/p&gt;&#xD;
&lt;p&gt;首先让我们来回顾一下第一章中介绍的例子：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;function Person(name) { &#xD;
this.name = name; &#xD;
} &#xD;
Person.prototype = { &#xD;
getName: function() { &#xD;
return this.name; &#xD;
} &#xD;
} &#xD;
 &#xD;
function Employee(name, employeeID) { &#xD;
this.name = name; &#xD;
this.employeeID = employeeID; &#xD;
} &#xD;
Employee.prototype = new Person(); &#xD;
Employee.prototype.getEmployeeID = function() { &#xD;
return this.employeeID; &#xD;
}; &#xD;
var zhang = new Employee("ZhangSan", "1234"); &#xD;
console.log(zhang.getName()); // "ZhangSan"  &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;修正constructor的指向错误&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;从上一篇文章中关于constructor的描述，我们知道Employee实例的constructor会有一个指向错误，如下所示：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var zhang = new Employee("ZhangSan", "1234"); &#xD;
console.log(zhang.constructor === Employee); // false &#xD;
console.log(zhang.constructor === Object); // true  &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;我们需要简单的修正：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;function Employee(name, employeeID) { &#xD;
this.name = name; &#xD;
this.employeeID = employeeID; &#xD;
} &#xD;
Employee.prototype = new Person(); &#xD;
Employee.prototype.constructor = Employee; &#xD;
Employee.prototype.getEmployeeID = function() { &#xD;
return this.employeeID; &#xD;
}; &#xD;
var zhang = new Employee("ZhangSan", "1234"); &#xD;
console.log(zhang.constructor === Employee); // true &#xD;
console.log(zhang.constructor === Object); // false &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;创建Employee类时实例化Person是不合适的&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;但另一方面，我们又必须依赖于这种机制来实现继承。 解决办法是不在构造函数中初始化数据，而是提供一个原型方法（比如init）来初始化数据。&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 空的构造函数 &#xD;
function Person() { &#xD;
} &#xD;
Person.prototype = { &#xD;
init: function(name) { &#xD;
this.name = name; &#xD;
}, &#xD;
getName: function() { &#xD;
return this.name; &#xD;
} &#xD;
} &#xD;
// 空的构造函数 &#xD;
function Employee() { &#xD;
} &#xD;
// 创建类的阶段不会初始化父类的数据，因为Person是一个空的构造函数 &#xD;
Employee.prototype = new Person(); &#xD;
Employee.prototype.constructor = Employee; &#xD;
Employee.prototype.init = function(name, employeeID) { &#xD;
this.name = name; &#xD;
this.employeeID = employeeID; &#xD;
}; &#xD;
Employee.prototype.getEmployeeID = function() { &#xD;
return this.employeeID; &#xD;
}; &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;这种方式下，必须在实例化一个对象后手工调用init函数，如下：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var zhang = new Employee(); &#xD;
zhang.init("ZhangSan", "1234"); &#xD;
console.log(zhang.getName()); // "ZhangSan" &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;如何自动调用init函数？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;必须达到两个效果，构造类时不要调用init函数和实例化对象时自动调用init函数。看来我们需要在调用空的构造函数时有一个状态标示。&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 创建一个全局的状态标示 - 当前是否处于类的构造阶段 &#xD;
var initializing = false; &#xD;
function Person() { &#xD;
if (!initializing) { &#xD;
this.init.apply(this, arguments); &#xD;
} &#xD;
} &#xD;
Person.prototype = { &#xD;
init: function(name) { &#xD;
this.name = name; &#xD;
}, &#xD;
getName: function() { &#xD;
return this.name; &#xD;
} &#xD;
} &#xD;
function Employee() { &#xD;
if (!initializing) { &#xD;
this.init.apply(this, arguments); &#xD;
} &#xD;
} &#xD;
// 标示当前进入类的创建阶段，不会调用init函数 &#xD;
initializing = true; &#xD;
Employee.prototype = new Person(); &#xD;
Employee.prototype.constructor = Employee; &#xD;
initializing = false; &#xD;
Employee.prototype.init = function(name, employeeID) { &#xD;
this.name = name; &#xD;
this.employeeID = employeeID; &#xD;
}; &#xD;
Employee.prototype.getEmployeeID = function() { &#xD;
return this.employeeID; &#xD;
}; &#xD;
 &#xD;
// 初始化类实例时，自动调用类的原型函数init，并向init中传递参数 &#xD;
var zhang = new Employee("ZhangSan", "1234"); &#xD;
console.log(zhang.getName()); // "ZhangSan" &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;但是这样就必须引入全局变量，这是一个不好的信号。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;如何避免引入全局变量initializing？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;我们需要引入一个全局的函数来简化类的创建过程，同时封装内部细节避免引入全局变量。&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 当前是否处于创建类的阶段 &#xD;
var initializing = false; &#xD;
function jClass(baseClass, prop) { &#xD;
// 只接受一个参数的情况 - jClass(prop) &#xD;
if (typeof (baseClass) === "object") { &#xD;
prop = baseClass; &#xD;
baseClass = null; &#xD;
} &#xD;
// 本次调用所创建的类（构造函数） &#xD;
function F() { &#xD;
// 如果当前处于实例化类的阶段，则调用init原型函数 &#xD;
if (!initializing) { &#xD;
this.init.apply(this, arguments); &#xD;
} &#xD;
} &#xD;
// 如果此类需要从其它类扩展 &#xD;
if (baseClass) { &#xD;
initializing = true; &#xD;
F.prototype = new baseClass(); &#xD;
F.prototype.constructor = F; &#xD;
initializing = false; &#xD;
} &#xD;
// 覆盖父类的同名函数 &#xD;
for (var name in prop) { &#xD;
if (prop.hasOwnProperty(name)) { &#xD;
F.prototype[name] = prop[name]; &#xD;
} &#xD;
} &#xD;
return F; &#xD;
}; &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;使用jClass函数来创建类和继承类的方法：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Person = jClass({ &#xD;
init: function(name) { &#xD;
this.name = name; &#xD;
}, &#xD;
getName: function() { &#xD;
return this.name; &#xD;
} &#xD;
}); &#xD;
var Employee = jClass(Person, { &#xD;
init: function(name, employeeID) { &#xD;
this.name = name; &#xD;
this.employeeID = employeeID; &#xD;
}, &#xD;
getEmployeeID: function() { &#xD;
return this.employeeID; &#xD;
} &#xD;
}); &#xD;
 &#xD;
var zhang = new Employee("ZhangSan", "1234"); &#xD;
console.log(zhang.getName()); // "ZhangSan" &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;OK，现在创建类和实例化类的方式看起来优雅多了。 但是这里面还存在明显的瑕疵，Employee的初始化函数init无法调用父类的同名方法。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;如何调用父类的同名方法？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;我们可以通过为实例化对象提供一个base的属性，来指向父类（构造函数）的原型，如下：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 当前是否处于创建类的阶段 &#xD;
var initializing = false; &#xD;
function jClass(baseClass, prop) { &#xD;
// 只接受一个参数的情况 - jClass(prop) &#xD;
if (typeof (baseClass) === "object") { &#xD;
prop = baseClass; &#xD;
baseClass = null; &#xD;
} &#xD;
// 本次调用所创建的类（构造函数） &#xD;
function F() { &#xD;
// 如果当前处于实例化类的阶段，则调用init原型函数 &#xD;
if (!initializing) { &#xD;
// 如果父类存在，则实例对象的base指向父类的原型 &#xD;
// 这就提供了在实例对象中调用父类方法的途径 &#xD;
if (baseClass) { &#xD;
this.base = baseClass.prototype; &#xD;
} &#xD;
this.init.apply(this, arguments); &#xD;
} &#xD;
} &#xD;
// 如果此类需要从其它类扩展 &#xD;
if (baseClass) { &#xD;
initializing = true; &#xD;
F.prototype = new baseClass(); &#xD;
F.prototype.constructor = F; &#xD;
initializing = false; &#xD;
} &#xD;
// 覆盖父类的同名函数 &#xD;
for (var name in prop) { &#xD;
if (prop.hasOwnProperty(name)) { &#xD;
F.prototype[name] = prop[name]; &#xD;
} &#xD;
} &#xD;
return F; &#xD;
}; &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;调用方式：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Person = jClass({ &#xD;
init: function(name) { &#xD;
this.name = name; &#xD;
}, &#xD;
getName: function() { &#xD;
return this.name; &#xD;
} &#xD;
}); &#xD;
var Employee = jClass(Person, { &#xD;
init: function(name, employeeID) { &#xD;
// 调用父类的原型函数init，注意使用apply函数修改init的this指向 &#xD;
this.base.init.apply(this, [name]); &#xD;
this.employeeID = employeeID; &#xD;
}, &#xD;
getEmployeeID: function() { &#xD;
return this.employeeID; &#xD;
}, &#xD;
getName: function() { &#xD;
// 调用父类的原型函数getName &#xD;
return "Employee name: " + this.base.getName.apply(this); &#xD;
} &#xD;
}); &#xD;
 &#xD;
var zhang = new Employee("ZhangSan", "1234"); &#xD;
console.log(zhang.getName()); // "Employee name: ZhangSan" &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;目前为止，我们已经修正了在第一章手工实现继承的种种弊端。 通过我们自定义的jClass函数来创建类和子类，通过原型方法init初始化数据， 通过实例属性base来调用父类的原型函数。&lt;/p&gt;&#xD;
&lt;p&gt;唯一的缺憾是调用父类的代码太长，并且不好理解， 如果能够按照如下的方式调用岂不是更妙：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Employee = jClass(Person, { &#xD;
init: function(name, employeeID) { &#xD;
// 如果能够这样调用，就再好不过了 &#xD;
this.base(name); &#xD;
this.employeeID = employeeID; &#xD;
} &#xD;
}); &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;优化jClass函数&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 当前是否处于创建类的阶段 &#xD;
var initializing = false; &#xD;
function jClass(baseClass, prop) { &#xD;
// 只接受一个参数的情况 - jClass(prop) &#xD;
if (typeof (baseClass) === "object") { &#xD;
prop = baseClass; &#xD;
baseClass = null; &#xD;
} &#xD;
// 本次调用所创建的类（构造函数） &#xD;
function F() { &#xD;
// 如果当前处于实例化类的阶段，则调用init原型函数 &#xD;
if (!initializing) { &#xD;
// 如果父类存在，则实例对象的baseprototype指向父类的原型 &#xD;
// 这就提供了在实例对象中调用父类方法的途径 &#xD;
if (baseClass) { &#xD;
this.baseprototype = baseClass.prototype; &#xD;
} &#xD;
this.init.apply(this, arguments); &#xD;
} &#xD;
} &#xD;
// 如果此类需要从其它类扩展 &#xD;
if (baseClass) { &#xD;
initializing = true; &#xD;
F.prototype = new baseClass(); &#xD;
F.prototype.constructor = F; &#xD;
initializing = false; &#xD;
} &#xD;
// 覆盖父类的同名函数 &#xD;
for (var name in prop) { &#xD;
if (prop.hasOwnProperty(name)) { &#xD;
// 如果此类继承自父类baseClass并且父类原型中存在同名函数name &#xD;
if (baseClass &amp;amp;&amp;amp; &#xD;
typeof (prop[name]) === "function" &amp;amp;&amp;amp; &#xD;
typeof (F.prototype[name]) === "function") { &#xD;
 &#xD;
// 重定义函数name -  &#xD;
// 首先在函数上下文设置this.base指向父类原型中的同名函数 &#xD;
// 然后调用函数prop[name]，返回函数结果 &#xD;
 &#xD;
// 注意：这里的自执行函数创建了一个上下文，这个上下文返回另一个函数， &#xD;
// 此函数中可以应用此上下文中的变量，这就是闭包（Closure）。 &#xD;
// 这是JavaScript框架开发中常用的技巧。 &#xD;
F.prototype[name] = (function(name, fn) { &#xD;
return function() { &#xD;
this.base = baseClass.prototype[name]; &#xD;
return fn.apply(this, arguments); &#xD;
}; &#xD;
})(name, prop[name]); &#xD;
 &#xD;
} else { &#xD;
F.prototype[name] = prop[name]; &#xD;
} &#xD;
} &#xD;
} &#xD;
return F; &#xD;
}; &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;此时，创建类与子类以及调用方式都显得非常优雅，请看：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var Person = jClass({ &#xD;
init: function(name) { &#xD;
this.name = name; &#xD;
}, &#xD;
getName: function() { &#xD;
return this.name; &#xD;
} &#xD;
}); &#xD;
var Employee = jClass(Person, { &#xD;
init: function(name, employeeID) { &#xD;
this.base(name); &#xD;
this.employeeID = employeeID; &#xD;
}, &#xD;
getEmployeeID: function() { &#xD;
return this.employeeID; &#xD;
}, &#xD;
getName: function() { &#xD;
return "Employee name: " + this.base(); &#xD;
} &#xD;
}); &#xD;
 &#xD;
var zhang = new Employee("ZhangSan", "1234"); &#xD;
console.log(zhang.getName()); // "Employee name: ZhangSan" &#xD;
&lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;至此，我们已经创建了一个完善的函数jClass， 帮助我们在JavaScript中以比较优雅的方式实现类和继承。&lt;/p&gt;&#xD;
&lt;p&gt;在以后的章节中，我们会陆续分析网上一些比较流行的JavaScript类和继承的实现。 不过万变不离其宗，那些实现也无非把我们这章中提到的概念颠来簸去的&amp;ldquo;炒作&amp;rdquo;， 为的就是一种更优雅的调用方式。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2327616.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327616.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2012/01/19/2327612.html</id><title type="text">JavaScript继承详解（二）</title><summary type="text">这一章我们将会重点介绍JavaScript中几个重要的属性（this、constructor、prototype）， 这些属性对于我们理解如何实现JavaScript中的类和继承起着至关重要的作用。thisthis表示当前对象，如果在全局作用范围内使用this，则指代当前页面对象window； 如果在函数中使用this，则this指代什么是根据运行时此函数在什么对象上被调用。 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。先看一个在全局作用范围内使用this的例子： &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt; </summary><published>2012-01-19T13:24:00Z</published><updated>2012-01-19T13:24:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327612.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327612.html"/><content type="html">&lt;p&gt;这一章我们将会重点介绍JavaScript中几个重要的属性（this、constructor、prototype）， 这些属性对于我们理解如何实现JavaScript中的类和继承起着至关重要的作用。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;this&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;this表示当前对象，如果在全局作用范围内使用this，则指代当前页面对象window； 如果在函数中使用this，则this指代什么是根据运行时此函数在什么对象上被调用。 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。&lt;/p&gt;&#xD;
&lt;p&gt;先看一个在全局作用范围内使用this的例子：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&amp;lt;script type="text/javascript"&amp;gt;&#xD;
            console.log(this === window);  // true&#xD;
            console.log(window.alert === this.alert);  // true&#xD;
            console.log(this.parseInt("021", 10));  // 10&#xD;
        &amp;lt;/script&amp;gt;&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;函数中的this是在运行时决定的，而不是函数定义时，如下：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 定义一个全局函数&#xD;
        function foo() {&#xD;
            console.log(this.fruit);&#xD;
        }&#xD;
        // 定义一个全局变量，等价于window.fruit = "apple";&#xD;
        var fruit = "apple";&#xD;
        // 此时函数foo中this指向window对象&#xD;
        // 这种调用方式和window.foo();是完全等价的&#xD;
        foo();  // "apple"&#xD;
&#xD;
        // 自定义一个对象，并将此对象的属性foo指向全局函数foo&#xD;
        var pack = {&#xD;
            fruit: "orange",&#xD;
            foo: foo&#xD;
        };&#xD;
        // 此时函数foo中this指向window.pack对象&#xD;
        pack.foo(); // "orange"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;全局函数apply和call可以用来改变函数中this的指向，如下：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 定义一个全局函数&#xD;
        function foo() {&#xD;
            console.log(this.fruit);&#xD;
        }&#xD;
        &#xD;
        // 定义一个全局变量&#xD;
        var fruit = "apple";&#xD;
        // 自定义一个对象&#xD;
        var pack = {&#xD;
            fruit: "orange"&#xD;
        };&#xD;
        &#xD;
        // 等价于window.foo();&#xD;
        foo.apply(window);  // "apple"&#xD;
        // 此时foo中的this === pack&#xD;
        foo.apply(pack);    // "orange"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;注：apply和call两个函数的作用相同，唯一的区别是两个函数的参数定义不同。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;因为在JavaScript中函数也是对象，所以我们可以看到如下有趣的例子：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 定义一个全局函数&#xD;
        function foo() {&#xD;
            if (this === window) {&#xD;
                console.log("this is window.");&#xD;
            }&#xD;
        }&#xD;
        &#xD;
        // 函数foo也是对象，所以可以定义foo的属性boo为一个函数&#xD;
        foo.boo = function() {&#xD;
            if (this === foo) {&#xD;
                console.log("this is foo.");&#xD;
            } else if (this === window) {&#xD;
                console.log("this is window.");&#xD;
            }&#xD;
        };&#xD;
        // 等价于window.foo();&#xD;
        foo();  // this is window.&#xD;
        &#xD;
        // 可以看到函数中this的指向调用函数的对象&#xD;
        foo.boo();  // this is foo.&#xD;
        &#xD;
        // 使用apply改变函数中this的指向&#xD;
        foo.boo.apply(window);  // this is window.&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;prototype&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;我们已经在第一章中使用prototype模拟类和继承的实现。 prototype本质上还是一个JavaScript对象。 并且每个函数都有一个默认的prototype属性。 如果这个函数被用在创建自定义对象的场景中，我们称这个函数为构造函数。 比如下面一个简单的场景：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 构造函数&#xD;
        function Person(name) {&#xD;
            this.name = name;&#xD;
        }&#xD;
        // 定义Person的原型，原型中的属性可以被自定义对象引用&#xD;
        Person.prototype = {&#xD;
            getName: function() {&#xD;
                return this.name;&#xD;
            }&#xD;
        }&#xD;
        var zhang = new Person("ZhangSan");&#xD;
        console.log(zhang.getName());   // "ZhangSan"&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;作为类比，我们考虑下JavaScript中的数据类型 - 字符串（String）、数字（Number）、数组（Array）、对象（Object）、日期（Date）等。 我们有理由相信，在JavaScript内部这些类型都是作为构造函数来实现的，比如：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 定义数组的构造函数，作为JavaScript的一种预定义类型&#xD;
        function Array() {&#xD;
            // ...&#xD;
        }&#xD;
        &#xD;
        // 初始化数组的实例&#xD;
        var arr1 = new Array(1, 56, 34, 12);&#xD;
        // 但是，我们更倾向于如下的语法定义：&#xD;
        var arr2 = [1, 56, 34, 12];&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;同时对数组操作的很多方法（比如concat、join、push）应该也是在prototype属性中定义的。 实际上，JavaScript所有的固有数据类型都具有只读的prototype属性（这是可以理解的：因为如果修改了这些类型的prototype属性，则哪些预定义的方法就消失了）， 但是我们可以向其中添加自己的扩展方法。&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 向JavaScript固有类型Array扩展一个获取最小值的方法&#xD;
        Array.prototype.min = function() {&#xD;
            var min = this[0];&#xD;
            for (var i = 1; i &amp;lt; this.length; i++) {&#xD;
                if (this[i] &amp;lt; min) {&#xD;
                    min = this[i];&#xD;
                }&#xD;
            }&#xD;
            return min;&#xD;
        };&#xD;
        &#xD;
        // 在任意Array的实例上调用min方法&#xD;
        console.log([1, 56, 34, 12].min());  // 1&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;注意：这里有一个陷阱，向Array的原型中添加扩展方法后，当使用for-in循环数组时，这个扩展方法也会被循环出来。 下面的代码说明这一点（假设已经向Array的原型中扩展了min方法）：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var arr = [1, 56, 34, 12];&#xD;
        var total = 0;&#xD;
        for (var i in arr) {&#xD;
            total += parseInt(arr[i], 10);&#xD;
        }&#xD;
        console.log(total);   // NaN&#xD;
        &#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;解决方法也很简单：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;var arr = [1, 56, 34, 12];&#xD;
        var total = 0;&#xD;
        for (var i in arr) {&#xD;
            if (arr.hasOwnProperty(i)) {&#xD;
                total += parseInt(arr[i], 10);&#xD;
            }&#xD;
        }&#xD;
        console.log(total);   // 103&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;constructor&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;constructor始终指向创建当前对象的构造函数。比如下面例子：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;// 等价于 var foo = new Array(1, 56, 34, 12);&#xD;
        var arr = [1, 56, 34, 12];&#xD;
        console.log(arr.constructor === Array); // true&#xD;
        // 等价于 var foo = new Function();&#xD;
        var Foo = function() { };&#xD;
        console.log(Foo.constructor === Function); // true&#xD;
        // 由构造函数实例化一个obj对象&#xD;
        var obj = new Foo();&#xD;
        console.log(obj.constructor === Foo); // true&#xD;
        &#xD;
        // 将上面两段代码合起来，就得到下面的结论&#xD;
        console.log(obj.constructor.constructor === Function); // true&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;但是当constructor遇到prototype时，有趣的事情就发生了。 我们知道每个函数都有一个默认的属性prototype，而这个prototype的constructor默认指向这个函数。如下例所示：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;function Person(name) {&#xD;
            this.name = name;&#xD;
        };&#xD;
        Person.prototype.getName = function() {&#xD;
            return this.name;&#xD;
        };&#xD;
        var p = new Person("ZhangSan");&#xD;
        &#xD;
        console.log(p.constructor === Person);  // true&#xD;
        console.log(Person.prototype.constructor === Person); // true&#xD;
        // 将上两行代码合并就得到如下结果&#xD;
        console.log(p.constructor.prototype.constructor === Person); // true&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;当时当我们重新定义函数的prototype时（注意：和上例的区别，这里不是修改而是覆盖）， constructor的行为就有点奇怪了，如下示例：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;function Person(name) {&#xD;
            this.name = name;&#xD;
        };&#xD;
        Person.prototype = {&#xD;
            getName: function() {&#xD;
                return this.name;&#xD;
            }&#xD;
        };&#xD;
        var p = new Person("ZhangSan");&#xD;
        console.log(p.constructor === Person);  // false&#xD;
        console.log(Person.prototype.constructor === Person); // false&#xD;
        console.log(p.constructor.prototype.constructor === Person); // false&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;为什么呢？ 原来是因为覆盖Person.prototype时，等价于进行如下代码操作：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;Person.prototype = new Object({&#xD;
            getName: function() {&#xD;
                return this.name;&#xD;
            }&#xD;
        });&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;而constructor始终指向创建自身的构造函数，所以此时Person.prototype.constructor === Object，即是：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;function Person(name) {&#xD;
            this.name = name;&#xD;
        };&#xD;
        Person.prototype = {&#xD;
            getName: function() {&#xD;
                return this.name;&#xD;
            }&#xD;
        };&#xD;
        var p = new Person("ZhangSan");&#xD;
        console.log(p.constructor === Object);  // true&#xD;
        console.log(Person.prototype.constructor === Object); // true&#xD;
        console.log(p.constructor.prototype.constructor === Object); // true&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;怎么修正这种问题呢？方法也很简单，重新覆盖Person.prototype.constructor即可：&lt;/p&gt;&#xD;
&lt;pre style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;function Person(name) {&#xD;
            this.name = name;&#xD;
        };&#xD;
        Person.prototype = new Object({&#xD;
            getName: function() {&#xD;
                return this.name;&#xD;
            }&#xD;
        });&#xD;
        Person.prototype.constructor = Person;&#xD;
        var p = new Person("ZhangSan");&#xD;
        console.log(p.constructor === Person);  // true&#xD;
        console.log(Person.prototype.constructor === Person); // true&#xD;
        console.log(p.constructor.prototype.constructor === Person); // true&#xD;
        &lt;/pre&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;下一章我们将会对第一章提到的Person-Employee类和继承的实现进行完善。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2327612.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327612.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2012/01/19/2327609.html</id><title type="text">JavaScript继承详解（一）</title><summary type="text">面向对象与基于对象几乎每个开发人员都有面向对象语言（比如C++、C#、Java）的开发经验。 在传统面向对象的语言中，有两个非常重要的概念 - 类和实例。 类定义了一类事物公共的行为和方法；而实例则是类的一个具体实现。 我们还知道，面向对象编程有三个重要的概念 - 封装、继承和多态。但是在JavaScript的世界中，所有的这一切特性似乎都不存在。 因为JavaScript本身不是面向对象的语言，而是基于对象的语言。 这里面就有一些有趣的特性，比如JavaScript中所有事物都是对象， 包括字符串、数组、日期、数字，甚至是函数，比如下面这个例子：?12345678910// 定义一个函数 -</summary><published>2012-01-19T13:21:00Z</published><updated>2012-01-19T13:21:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327609.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327609.html"/><content type="html">&lt;p&gt;&lt;strong&gt;&lt;strong&gt;面向对象与基于对象&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;几乎每个开发人员都有面向对象语言（比如C++、C#、Java）的开发经验。 在传统面向对象的语言中，有两个非常重要的概念 - 类和实例。 类定义了一类事物公共的行为和方法；而实例则是类的一个具体实现。 我们还知道，面向对象编程有三个重要的概念 - 封装、继承和多态。&lt;/p&gt;&#xD;
&lt;p&gt;但是在JavaScript的世界中，所有的这一切特性似乎都不存在。 因为JavaScript本身不是面向对象的语言，而是基于对象的语言。 这里面就有一些有趣的特性，比如JavaScript中所有事物都是对象， 包括字符串、数组、日期、数字，甚至是函数，比如下面这个例子：&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_274514" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&#xD;
&lt;div &gt;5&lt;/div&gt;&#xD;
&lt;div &gt;6&lt;/div&gt;&#xD;
&lt;div &gt;7&lt;/div&gt;&#xD;
&lt;div &gt;8&lt;/div&gt;&#xD;
&lt;div &gt;9&lt;/div&gt;&#xD;
&lt;div &gt;10&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;// 定义一个函数 - add&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;function&lt;/code&gt; &lt;code &gt;add(a, b) {&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;add.invokeTimes++;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;return&lt;/code&gt; &lt;code &gt;a + b;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;}&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;// 因为函数本身也是对象，这里为函数add定义一个属性，用来记录此函数被调用的次数&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;add.invokeTimes = 0;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;add(1 + 1);&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;add(2 + 3);&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;console.log(add.invokeTimes); &lt;/code&gt;&lt;code &gt;// 2&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;模拟JavaScript中类和继承&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;在面向对象的语言中，我们使用类来创建一个自定义对象。然而JavaScript中所有事物都是对象，&lt;strong&gt;那么用什么办法来创建自定义对象呢？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这就需要引入另外一个概念 - 原型（prototype），我们可以简单的把prototype看做是一个模版，新创建的自定义对象都是这个模版（prototype）的一个拷贝 （实际上不是拷贝而是链接，只不过这种链接是不可见，给人们的感觉好像是拷贝）。&lt;/p&gt;&#xD;
&lt;p&gt;让我们看一下通过prototype创建自定义对象的一个例子：&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_632340" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&#xD;
&lt;div &gt;5&lt;/div&gt;&#xD;
&lt;div &gt;6&lt;/div&gt;&#xD;
&lt;div &gt;7&lt;/div&gt;&#xD;
&lt;div &gt;8&lt;/div&gt;&#xD;
&lt;div &gt;9&lt;/div&gt;&#xD;
&lt;div &gt;10&lt;/div&gt;&#xD;
&lt;div &gt;11&lt;/div&gt;&#xD;
&lt;div &gt;12&lt;/div&gt;&#xD;
&lt;div &gt;13&lt;/div&gt;&#xD;
&lt;div &gt;14&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;// 构造函数&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;function&lt;/code&gt; &lt;code &gt;Person(name, sex) {&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;this&lt;/code&gt;&lt;code &gt;.name = name;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;this&lt;/code&gt;&lt;code &gt;.sex = sex;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;}&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;// 定义Person的原型，原型中的属性可以被自定义对象引用&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;Person.prototype = {&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;getName: &lt;/code&gt;&lt;code &gt;function&lt;/code&gt;&lt;code &gt;() {&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;return&lt;/code&gt; &lt;code &gt;this&lt;/code&gt;&lt;code &gt;.name;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;},&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;getSex: &lt;/code&gt;&lt;code &gt;function&lt;/code&gt;&lt;code &gt;() {&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;return&lt;/code&gt; &lt;code &gt;this&lt;/code&gt;&lt;code &gt;.sex;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;}&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;}&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;这里我们把函数Person称为构造函数，也就是创建自定义对象的函数。可以看出，JavaScript通过构造函数和原型的方式模拟实现了类的功能。 创建自定义对象（实例化类）的代码：&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_808503" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;var&lt;/code&gt; &lt;code &gt;zhang = &lt;/code&gt;&lt;code &gt;new&lt;/code&gt; &lt;code &gt;Person(&lt;/code&gt;&lt;code &gt;"ZhangSan"&lt;/code&gt;&lt;code &gt;, &lt;/code&gt;&lt;code &gt;"man"&lt;/code&gt;&lt;code &gt;);&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;console.log(zhang.getName()); &lt;/code&gt;&lt;code &gt;// "ZhangSan"&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;var&lt;/code&gt; &lt;code &gt;chun = &lt;/code&gt;&lt;code &gt;new&lt;/code&gt; &lt;code &gt;Person(&lt;/code&gt;&lt;code &gt;"ChunHua"&lt;/code&gt;&lt;code &gt;, &lt;/code&gt;&lt;code &gt;"woman"&lt;/code&gt;&lt;code &gt;);&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;console.log(chun.getName()); &lt;/code&gt;&lt;code &gt;// "ChunHua"&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;当代码var zhang = new Person("ZhangSan", "man")执行时，其实内部做了如下几件事情：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;创建一个空白对象（new Object()）。&lt;/li&gt;&#xD;
&lt;li&gt;拷贝Person.prototype中的属性（键值对）到这个空对象中（我们前面提到，内部实现时不是拷贝而是一个隐藏的链接）。&lt;/li&gt;&#xD;
&lt;li&gt;将这个对象通过this关键字传递到构造函数中并执行构造函数。&lt;/li&gt;&#xD;
&lt;li&gt;将这个对象赋值给变量zhang。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;为了证明prototype模版并不是被拷贝到实例化的对象中，而是一种链接的方式，请看如下代码：&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_144878" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&#xD;
&lt;div &gt;5&lt;/div&gt;&#xD;
&lt;div &gt;6&lt;/div&gt;&#xD;
&lt;div &gt;7&lt;/div&gt;&#xD;
&lt;div &gt;8&lt;/div&gt;&#xD;
&lt;div &gt;9&lt;/div&gt;&#xD;
&lt;div &gt;10&lt;/div&gt;&#xD;
&lt;div &gt;11&lt;/div&gt;&#xD;
&lt;div &gt;12&lt;/div&gt;&#xD;
&lt;div &gt;13&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;function&lt;/code&gt; &lt;code &gt;Person(name, sex) {&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;this&lt;/code&gt;&lt;code &gt;.name = name;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;this&lt;/code&gt;&lt;code &gt;.sex = sex;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;}&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;Person.prototype.age = 20;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;var&lt;/code&gt; &lt;code &gt;zhang = &lt;/code&gt;&lt;code &gt;new&lt;/code&gt; &lt;code &gt;Person(&lt;/code&gt;&lt;code &gt;"ZhangSan"&lt;/code&gt;&lt;code &gt;, &lt;/code&gt;&lt;code &gt;"man"&lt;/code&gt;&lt;code &gt;);&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;console.log(zhang.age); &lt;/code&gt;&lt;code &gt;// 20&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;// 覆盖prototype中的age属性&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;zhang.age = 19;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;console.log(zhang.age); &lt;/code&gt;&lt;code &gt;// 19&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;delete&lt;/code&gt; &lt;code &gt;zhang.age;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;// 在删除实例属性age后，此属性值又从prototype中获取&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;console.log(zhang.age); &lt;/code&gt;&lt;code &gt;// 20&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;这种在JavaScript内部实现的隐藏的prototype链接，是JavaScript赖以生存的温润土壤， 也是模拟实现继承的基础。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;如何在JavaScript中实现简单的继承？&lt;/strong&gt; 下面的例子将创建一个雇员类Employee，它从Person继承了原型prototype中的所有属性。&lt;/p&gt;&#xD;
&lt;div&gt;&#xD;
&lt;div id="highlighter_688732" &gt;&#xD;
&lt;div &gt;&lt;span&gt;&lt;a  href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&#xD;
&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;1&lt;/div&gt;&#xD;
&lt;div &gt;2&lt;/div&gt;&#xD;
&lt;div &gt;3&lt;/div&gt;&#xD;
&lt;div &gt;4&lt;/div&gt;&#xD;
&lt;div &gt;5&lt;/div&gt;&#xD;
&lt;div &gt;6&lt;/div&gt;&#xD;
&lt;div &gt;7&lt;/div&gt;&#xD;
&lt;div &gt;8&lt;/div&gt;&#xD;
&lt;div &gt;9&lt;/div&gt;&#xD;
&lt;div &gt;10&lt;/div&gt;&#xD;
&lt;div &gt;11&lt;/div&gt;&#xD;
&lt;div &gt;12&lt;/div&gt;&#xD;
&lt;div &gt;13&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td &gt;&#xD;
&lt;div &gt;&#xD;
&lt;div &gt;&lt;code &gt;function&lt;/code&gt; &lt;code &gt;Employee(name, sex, employeeID) {&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;this&lt;/code&gt;&lt;code &gt;.name = name;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;this&lt;/code&gt;&lt;code &gt;.sex = sex;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;this&lt;/code&gt;&lt;code &gt;.employeeID = employeeID;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;}&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;// 将Employee的原型指向Person的一个实例&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;// 因为Person的实例可以调用Person原型中的方法, 所以Employee的实例也可以调用Person原型中的所有属性。&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;Employee.prototype = &lt;/code&gt;&lt;code &gt;new&lt;/code&gt; &lt;code &gt;Person();&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;Employee.prototype.getEmployeeID = &lt;/code&gt;&lt;code &gt;function&lt;/code&gt;&lt;code &gt;() {&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code &gt;return&lt;/code&gt; &lt;code &gt;this&lt;/code&gt;&lt;code &gt;.employeeID;&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;};&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;var&lt;/code&gt; &lt;code &gt;zhang = &lt;/code&gt;&lt;code &gt;new&lt;/code&gt; &lt;code &gt;Employee(&lt;/code&gt;&lt;code &gt;"ZhangSan"&lt;/code&gt;&lt;code &gt;, &lt;/code&gt;&lt;code &gt;"man"&lt;/code&gt;&lt;code &gt;, &lt;/code&gt;&lt;code &gt;"1234"&lt;/code&gt;&lt;code &gt;);&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;div &gt;&lt;code &gt;console.log(zhang.getName()); &lt;/code&gt;&lt;code &gt;// "ZhangSan&lt;/code&gt;&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;上面关于继承的实现很粗糙，并且存在很多问题：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;在创建Employee构造函数和原型（以后简称类）时，就对Person进行了实例化，这是不合适的。&lt;/li&gt;&#xD;
&lt;li&gt;Employee的构造函数没法调用父类Person的构造函数，导致在Employee构造函数中对name和sex属性的重复赋值。&lt;/li&gt;&#xD;
&lt;li&gt;Employee中的函数会覆盖Person中的同名函数，没有重载的机制（和上一条是一个类型的问题）。&lt;/li&gt;&#xD;
&lt;li&gt;创建JavaScript类的语法过于零散，不如C#/Java中的语法优雅。&lt;/li&gt;&#xD;
&lt;li&gt;实现中有constructor属性的指向错误，这个会在第二篇文章中讨论。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;我们会在第三章完善这个例子。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;JavaScript继承的实现&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;正因为JavaScript本身没有完整的类和继承的实现，并且我们也看到通过手工实现的方式存在很多问题， 因此对于这个富有挑战性的任务网上已经有很多实现了：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
&lt;li&gt;Douglas Crockford - &lt;a href="http://javascript.crockford.com/prototypal.html"&gt;Prototypal Inheritance in JavaScript&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;Douglas Crockford - &lt;a href="http://javascript.crockford.com/inheritance.html"&gt;Classical Inheritance in JavaScript&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;John Resig - &lt;a href="http://ejohn.org/blog/simple-javascript-inheritance/"&gt;Simple JavaScript Inheritance&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;Dean Edwards - &lt;a href="http://dean.edwards.name/weblog/2006/03/base/"&gt;A Base Class for JavaScript Inheritance&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;&lt;a href="http://www.prototypejs.org/learn/class-inheritance"&gt;Prototype&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;&lt;a href="http://mootools.net/docs/core/Class/Class"&gt;Mootools&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;li&gt;&lt;a href="http://extjs.com/"&gt;Extjs&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;这个系列的文章将会逐一深入分析这些实现，最终达到对JavaScript中如何实现类和继承有一个深入的了解。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;下一章我们将会介绍在类实现中的相关知识，比如this、constructor、prototype等。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2327609.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/01/19/2327609.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2011/06/15/2081498.html</id><title type="text">!DOCTYPE声明对JavaScript的影响分析</title><summary type="text">DOCTYPE是document type(文档类型)的简写，在web设计中用来说明你用的XHTML或者HTML是什么版本。要建立符合标准的网页，DOCTYPE声明是必不可少的关键组成部分；除非你的XHTML确定了一个正确的DOCTYPE，否则你的标识和CSS都不会生效；当然对JS的取值也会有影响。 DOCTYPE声明如下：&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</summary><published>2011-06-15T06:44:00Z</published><updated>2011-06-15T06:44:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2011/06/15/2081498.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2011/06/15/2081498.html"/><content type="html">&lt;p&gt;DOCTYPE是document type(文档类型)的简写，在web设计中用来说明你用的XHTML或者HTML是什么版本。&lt;/p&gt;&#xD;
&lt;p&gt;要建立符合标准的网页，DOCTYPE声明是必不可少的关键组成部分；除非你的XHTML确定了一个正确的DOCTYPE，否则你的标识和CSS都不会生效；当然对JS的取值也会有影响。 &lt;/p&gt;&#xD;
&lt;p&gt;DOCTYPE声明如下：&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt; &lt;/p&gt;&#xD;
&lt;p&gt;DOCTYPE声明包括：过渡型（Transitional），严格型（Strict）和框架型（Frameset）。接下来就看一下这三种类型对不同浏览器在获取元素相关信息时的影响（包括不定义DOCTYPE和只将DOCTYPE定义为&amp;lt;!docType&amp;gt;形式），我测试的浏览器分别为：IE6.0、IE7.0、IE8.0（变态模式）、FF、Opera、Chrome和Safari。由于IE5.5已经几乎被淘汰，所以不在测试范围之内。 &lt;/p&gt;&#xD;
&lt;p&gt;以下就是测试结果（滚动条只是元素本身的滚动条并不是窗口的）： &lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;一、过渡型（Transitional）&lt;/strong&gt; &lt;/p&gt;&#xD;
&lt;p&gt;1、测试IE6.0、IE7.0与IE8.0（非变态模式Quirks）的宽度和高度（width，height）&lt;/p&gt;&#xD;
&lt;table width="620" cellpadding="0" cellspacing="0" border="1"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr height="25"&gt;&#xD;
&lt;td align="middle"&gt;offsetWidth，offsetHeight&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;clientWidth，clientHeight&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;currentStyle[&amp;lsquo;width']，currentStyle[&amp;lsquo;width']&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="30"&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;有无滚动条均为：&lt;br /&gt;offsetWidth=&lt;br /&gt;paddingLeft+paddingRight+borderLeft+borderRight&lt;br /&gt;&lt;br /&gt;offsetHeight=&lt;br /&gt;paddingTop+paddingBottom+borderTop+boderBottom&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;1、无滚动条&lt;br /&gt;clientWidth=CSS中定义的宽度+paddingLeft+paddingRight&lt;br /&gt;&lt;br /&gt;clientHeight=CSS中定义的高度+paddingTop+paddingBottom&lt;br /&gt;（均不包括边框宽度）&lt;br /&gt;&lt;br /&gt;2、有滚动条&lt;br /&gt;clientWidth=CSS中定义的宽度-滚动条宽度（17固定值）-边框宽度，&lt;br /&gt;&lt;br /&gt;clientHeight=CSS中定义的高度-滚动条宽度（17固定值）-边框宽度&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;实际真实宽度，不包括padding与边框值&lt;br /&gt;（除IE和Opera以外无此属性）&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="25"&gt;&#xD;
&lt;td align="middle"&gt;offsetLeft，offsetTop&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;clientLeft，clientTop&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;currentSytle[&amp;lsquo;left']，currentStyle[&amp;lsquo;top']&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="30"&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;有无滚动条均为：&lt;br /&gt;offsetLeft=对象的offsetLeft-边框&lt;br /&gt;offsetTop=实际值&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;有无滚动条均为：边框的宽度&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;&#xD;
&lt;p&gt;除IE和Opera以外无此属性&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;p&gt;&lt;strong&gt;2、测试IE8.0变态模式(Quirks)的宽度与高度（width，height）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;table width="620" cellpadding="0" cellspacing="0" border="1"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr height="25"&gt;&#xD;
&lt;td align="middle" width="333"&gt;offsetWidth，offsetHeight&lt;/td&gt;&#xD;
&lt;td align="middle" width="333"&gt;clientWidth，clientHeight&lt;/td&gt;&#xD;
&lt;td align="middle" width="333"&gt;currentStyle[&amp;lsquo;width']，currentStyle[&amp;lsquo;height]&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="30"&gt;&#xD;
&lt;td width="333"&gt;&#xD;
&lt;p&gt;1、（无滚动条）宽度（高度）=当前对象的实际数值&lt;br /&gt;2、（有滚动条）宽度（高度）=CSS中定义的数值&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td width="333"&gt;&#xD;
&lt;p&gt;1、（无滚动条）宽度（高度）=当前对象的实际数值&lt;br /&gt;2、（有滚动条）宽度（高度）=当前对象在CSS中定义的数值-滚动条（17）&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td width="333"&gt;&#xD;
&lt;p&gt;有无滚动条均为：&lt;br /&gt;宽度和高度均为CSS中定义的数值&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="25"&gt;&#xD;
&lt;td align="middle" width="333"&gt;offsetLeft，offsetTop&lt;/td&gt;&#xD;
&lt;td align="middle" width="333"&gt;clientLeft，clientTop&lt;/td&gt;&#xD;
&lt;td align="middle" width="333"&gt;currentSytle[&amp;lsquo;left']，currentStyle[&amp;lsquo;top']&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="30"&gt;&#xD;
&lt;td width="333"&gt;&#xD;
&lt;p&gt;有无滚动条均为： &lt;/p&gt;&#xD;
&lt;p&gt;offsetLeft（offsetTop）=实际值&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td width="333"&gt;&#xD;
&lt;p&gt;有无滚动条均为：边框的宽度&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td align="middle" width="333"&gt;&#xD;
&lt;p&gt;除IE和Opera以外无此属性&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;p&gt;3、FF、Opera、Safari和Chrom中所取得的数值与第一种情况一样。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;二、严格型（Strict）&lt;/strong&gt; &lt;/p&gt;&#xD;
&lt;p&gt;1、测试IE6.0、IE7.0与IE8.0（非变态模式Quirks）的宽度和高度（width，height）&lt;/p&gt;&#xD;
&lt;table width="620" cellpadding="0" cellspacing="0" border="1"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr height="25"&gt;&#xD;
&lt;td align="middle"&gt;offsetWidth，offsetHeight&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;clientWidth，clientHeight&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;currentStyle[&amp;lsquo;width']，currentStyle[&amp;lsquo;width']&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="30"&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;有无滚动条均为：&lt;br /&gt;offsetWidth=&lt;br /&gt;paddingLeft+paddingRight+borderLeft+borderRight&lt;br /&gt;&lt;br /&gt;offsetHeight=&lt;br /&gt;paddingTop+paddingBottom+borderTop+boderBottom&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;1、无滚动条&lt;br /&gt;clientWidth=CSS中定义的宽度+paddingLeft+paddingRight&lt;br /&gt;&lt;br /&gt;clientHeight=CSS中定义的高度+paddingTop+paddingBottom&lt;br /&gt;均不包括边框宽度&lt;br /&gt;&lt;br /&gt;2、有滚动条&lt;br /&gt;clientWidth=CSS中定义的宽度-滚动条宽度（17固定值）-边框宽度，&lt;br /&gt;&lt;br /&gt;clientHeight=CSS中定义的高度-滚动条宽度（17固定值）-边框宽度&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;实际真实宽度，不包括padding与边框值&lt;br /&gt;（除IE和Opera以外无此属性）&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="25"&gt;&#xD;
&lt;td align="middle"&gt;offsetLeft，offsetTop&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;clientLeft，clientTop&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;currentSytle[&amp;lsquo;left']，currentStyle[&amp;lsquo;top']&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="30"&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;有无滚动条均为：&lt;br /&gt;offsetLeft=对象的offsetLeft-边框&lt;br /&gt;offsetTop=实际值&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;有无滚动条均为：边框的宽度&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;&#xD;
&lt;p&gt;除IE和Opera以外无此属性&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;p&gt;&lt;strong&gt;2、测试IE8.0变态模式(Quirks)的宽度与高度（width，height）&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;table width="620" cellpadding="0" cellspacing="0" border="1"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr height="25"&gt;&#xD;
&lt;td align="middle"&gt;offsetWidth，offsetHeight&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;clientWidth，clientHeight&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;currentStyle[&amp;lsquo;width']，currentStyle[&amp;lsquo;height]&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="30"&gt;&#xD;
&lt;td width="333"&gt;&#xD;
&lt;p&gt;1、（无滚动条）宽度（高度）=当前对象的实际数值&lt;br /&gt;2、（有滚动条）宽度（高度）=CSS中定义的数值&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;1、（无滚动条）宽度（高度）=当前对象的实际数值&lt;br /&gt;2、（有滚动条）宽度（高度）=当前对象在CSS中定义的数值-滚动条（17）-边框宽度&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;有无滚动条均为：&lt;br /&gt;宽度和高度均为CSS中定义的数值&lt;br /&gt;（除IE和Opera以外无此属性）&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="25"&gt;&#xD;
&lt;td align="middle"&gt;offsetLeft，offsetTop&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;clientLeft，clientTop&lt;/td&gt;&#xD;
&lt;td align="middle" width="333"&gt;currentSytle[&amp;lsquo;left']，currentStyle[&amp;lsquo;top']&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr height="30"&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;有无滚动条均为： &lt;/p&gt;&#xD;
&lt;p&gt;offsetLeft（offsetTop）=实际值&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td&gt;&#xD;
&lt;p&gt;有无滚动条均为：边框的宽度&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;td align="middle"&gt;&#xD;
&lt;p&gt;除IE和Opera以外无此属性&lt;/p&gt;&#xD;
&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;p&gt;3、FF、Opera、Safari和Chrom中所取得的数值与第一种情况一样。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;三、取消DOCTYPE声明&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;1、IE6.0、IE7.0在没有DOCTYPE声明时与IE8.0中的变态模式（Quirks）中严格型结果相同。&lt;/p&gt;&#xD;
&lt;p&gt;2、FF、Safari、Chrome和IE8.0标准模式是与过渡型结果相同。&lt;/p&gt;&#xD;
&lt;p&gt;3、Opera浏览器中，在元素没有滚动条时offsetHeight与clientHeight的取值是在其当前现实的大小。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;四、将DOCTYPE声明改写为&amp;lt;!docType&amp;gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;1、IE6.0、IE7.0在没有DOCTYPE声明时与IE8.0中的变态模式（Quirks）中严格型结果相同。&lt;/p&gt;&#xD;
&lt;p&gt;2、FF、Safari、Chrome和IE8.0标准模式是与过渡型结果相同。&lt;/p&gt;&#xD;
&lt;p&gt;3、Opera浏览器中，在元素没有滚动条时offsetHeight与clientHeight的取值是在其当前现实的大小。&lt;/p&gt;&#xD;
&lt;p&gt;在Opera中也拥有与IE一样的属性&amp;ldquo;currentStyle&amp;rdquo;，所以在Opera中也可以使用currentStyle来取得元素的精确值。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2081498.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2011/06/15/2081498.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2011/06/15/2081480.html</id><title type="text">(转)浏览器的特征探测（更新 IE9 的判断）</title><summary type="text">【2010.04.18 更新】更加严格地区分出了 IE 9，增强了 Opera 的区分能力，并将 Chrome 的判别特征换成了引擎特性 [via]，这样你就能看出国内哪些浏览器是从 Chrome 的内核改过来的了，大家可以动手试试。 ：）【2010.04.17 更新】IE 9 在我看来改进非常大：有我所关心的对 Mutation Events 的支持，还有事件模型的 W3C 化等等……本文中我就不多冗述，有机会再另文探讨。由于在项目中大量使用特征判断，IE 9 在这方面最显著的变化是：之前那则“最短的 IE 判断法”已经失效，一起被修复的还有一直伴随 IE 的数组 Bug:非空数组字面量的最</summary><published>2011-06-15T06:35:00Z</published><updated>2011-06-15T06:35:00Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><link rel="alternate" href="http://www.cnblogs.com/jenry/archive/2011/06/15/2081480.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2011/06/15/2081480.html"/><content type="html">&lt;p&gt;【2010.04.18 更新】&lt;br /&gt;更加严格地区分出了 IE 9，&lt;br /&gt;增强了 Opera 的区分能力，&lt;br /&gt;并将 Chrome 的判别特征换成了引擎特性 [&lt;a target="_blank" href="http://www.thespanner.co.uk/2009/01/29/detecting-browsers-javascript-hacks/" title="Detecting browsers javascript hacks"&gt;via&lt;/a&gt;]，&lt;br /&gt;这样你就能看出国内哪些浏览器是从 Chrome 的内核改过来的了，&lt;br /&gt;大家可以动手试试。 ：）&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;【2010.04.17 更新】&lt;br /&gt;&lt;a target="_blank" href="http://ie.microsoft.com/testdrive/" title="Internet Explorer 9"&gt;IE 9&lt;/a&gt; 在我看来改进非常大：&lt;br /&gt;有我所关心的对 Mutation Events 的支持，&lt;br /&gt;还有&lt;a target="_blank" href="http://ie.microsoft.com/testdrive/HTML5/50ComparingEventModels/Default.html" title="Internet Explorer 9"&gt;事件模型的 W3C 化&lt;/a&gt;等等&amp;hellip;&amp;hellip;&lt;br /&gt;本文中我就不多冗述，&lt;br /&gt;有机会再另文探讨。&lt;br /&gt;由于在项目中大量使用特征判断，&lt;br /&gt;IE 9 在这方面最显著的变化是：&lt;br /&gt;之前那则&amp;ldquo;最短的 IE 判断法&amp;rdquo;已经失效，&lt;br /&gt;一起被修复的还有一直伴随 IE 的数组 Bug:&lt;br /&gt;非空数组字面量的最后一个元素缺失的问题（即：[null,]），&lt;br /&gt;因此现如今的最短记录保持者（6字节的 !-[1,]）也已然失效。（而且不支持 &lt;a target="_blank" href="http://code.google.com/p/closure-compiler/" title="Closure Compile"&gt;CC&lt;/a&gt; 压缩）&lt;br /&gt;你也不要尝试使用 addEventListener 方法来判别，&lt;br /&gt;因为 IE 9 已经支持了。（事件的改进非常大，这很好）&lt;br /&gt;所以说 IE 已经越来越靠近标准，&lt;br /&gt;如果在项目中有对 IE 进行特殊处理的代码，&lt;br /&gt;你可能需要在 IE9下重新检查一下了。&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;由于我个人依旧是 XP 的忠实拥趸，&lt;br /&gt;而 XP 又很悲剧地无法安装 IE 9，&lt;br /&gt;所以一些测试是在 Haitao Jia 同学的协助下完成，&lt;br /&gt;在此表示感谢。&lt;br /&gt;也正因为如此，&lt;br /&gt;我没有进行覆盖面太广的测试。&lt;br /&gt;不过目前所发现的唯一&amp;ldquo;幸存&amp;rdquo;下来的 IE 系列 Bug 依旧与刚提到的数组 Bug 有关，&lt;br /&gt;微软修复了字面量里的 Bug 却忘了当字符串被 split 成数组后却涛声依旧啊。&lt;br /&gt;虽然判断起来稍微麻烦了点，&lt;br /&gt;但不管怎样这是我目前发现的仅剩不多地可以用来判断 IE 全系列的代码：&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;&lt;code&gt;&lt;span style="color: #000000;"&gt;!','.split(/,/).length&lt;/span&gt; &lt;/code&gt;&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;具体的代码请拖至文末。&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;【2010.03.18 更新】&lt;br /&gt;IE 8 也支持 window.DataTransfer 这个拖拽时方法，&lt;br /&gt;因此判断 gecko 内核的条件有所改动，&lt;br /&gt;你可以在原有判断条件上排除 IE8 ，&lt;br /&gt;或者选择 &lt;a target="_blank" href="https://developer.mozilla.org/en/DOM/window.mozInnerScreenX" title="About window.mozInnerScreenX"&gt;window.mozInnerScreenX&lt;/a&gt; 这类由 FF3.6 开始提供的 gecko 特有属性，&lt;br /&gt;（相对应的获取纵坐标属性 &lt;a target="_blank" href="https://developer.mozilla.org/en/DOM/window.mozInnerScreenY" title="About window.mozInnerScreenX"&gt;window.mozInnerScreenX&lt;/a&gt;）&lt;br /&gt;还有用以检测某个节点是否满足某个选择器规则的方法 &lt;a target="_blank" href="https://developer.mozilla.org/en/DOM%3aNode.mozMatchesSelector" title="About node.mozMatchesSelector"&gt;node.mozMatchesSelector&lt;/a&gt; （很实用）&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;【2010.01.21 更新】&lt;br /&gt;Google 的 Closure Compile 会将 IE 的判断代码&amp;ldquo;压缩&amp;rdquo;成：&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;&lt;code&gt;&lt;span style="color: #000000;"&gt;!+"\u000b1"&lt;/span&gt; &lt;/code&gt;&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;要么压缩后替换回 !+"\v1"，&lt;br /&gt;要么换其它更安全的方法来判断 IE 。&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;【2010.01.03 发表】&lt;br /&gt;在撰写此文之前，&lt;a target="_blank" href="http://www.chencheng.org/blog/" title="云谦"&gt;&lt;br /&gt;陈成&lt;/a&gt;告诉我 &lt;a target="_blank" href="http://www.nczonline.net/blog" title="Nicholas C. Zakas"&gt;Nicholas C. Zakas&lt;/a&gt; 大师几天前刚好写了一篇名为&lt;br /&gt;《&lt;a target="_blank" href="http://www.nczonline.net/blog/2009/12/29/feature-detection-is-not-browser-detection/" title="Feature detection is not browser detection"&gt;特征探测并非浏览器探测 (Feature detection is not browser detection)&lt;/a&gt; 》&lt;br /&gt;的文章。&lt;br /&gt;文章里&amp;ldquo;深刻&amp;rdquo;批判了 &lt;a target="_blank" href="http://mootools.net/" title="MooTools"&gt;MooTools&lt;/a&gt; 所使用的特征探测法，&lt;br /&gt;但真正令人信服的理由似乎在文中也并无体现，&lt;br /&gt;只是说 MooTools 因 Firefox 3.6 的变化而被迫发布了一次升级，&lt;br /&gt;然后说：&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&amp;ldquo;当浏览器（功能）愈发地接近彼此，想从&amp;ldquo;特征&amp;rdquo;去区别它们将变得越来越困难和危险。&lt;br /&gt;(As browsers grow closer together, looking at &amp;ldquo;features&amp;rdquo; to separate them will become more difficult and risky.)&amp;rdquo;&lt;/blockquote&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;当然啦，&lt;br /&gt;不仅是 Javascript ，&lt;br /&gt;服务端想要统计客户端也必须依靠 User-Agent&amp;nbsp; （以下简称 UA），&lt;br /&gt;而对于 UA Spoofs ，&lt;br /&gt;尼古拉斯的看法是：&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;blockquote style='border:2px solid #EFEFEF;color:#333333;padding:5px 10px;'&gt;&amp;ldquo;你必须永远尊重浏览器所告知你的 UA 。&lt;br /&gt;(You should always honor exactly what the browser is reporting as a user-agent.)&amp;rdquo;&lt;/blockquote&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;因此从前后端统一的角度，&lt;br /&gt;我个人还是赞同这一观点的。&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;但从另一方面看，&lt;br /&gt;现在浏览器内核虽稍显得固定，&lt;br /&gt;但集成多种内核出来闯荡江湖的浏览器也不少，&lt;br /&gt;而且它们在中国的&lt;a target="_blank" href="http://www.cnbeta.com/articles/100814.htm" title="国产浏览器占有率"&gt;占有率&lt;/a&gt;都不是一般的高，&lt;br /&gt;（但它们对 UA 的管理则不是一般的糟糕）&lt;br /&gt;我想这是尼大师所没有料到的。&lt;br /&gt;（值得一提的是&lt;a target="_blank" href="http://www.maxthon.com/" title="Maxthon 3"&gt;马桶 3&lt;/a&gt; 的 web-kit 模式这次提供了 UA 特征符）&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;因此我觉得对于特征探测不可一棒子敲死，&lt;br /&gt;而对于 UA 嗅探法也不能一味地捧上天，&lt;br /&gt;能够准确判断出浏览器继而进行正确的 Hack 来确保完整体验才是王道。&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;呃&amp;hellip;&amp;hellip;&lt;br /&gt;写了这么多，&lt;br /&gt;完全是针对尼大师的新文有感而发，&lt;br /&gt;我预想中的正文从这里开始&amp;mdash;&amp;mdash;&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;由于 Firefox 3.6 产生了&lt;a target="_blank" href="https://developer.mozilla.org/en/Firefox_3.6_for_developers" title="Firefox 3.6 Changelog"&gt;巨大的变化&lt;/a&gt;，&lt;br /&gt;（当然不仅是 Javascript 这个层面，&lt;br /&gt;也比如旧的 chrome 注册文件 contents.rdf 也被废止等等）&lt;br /&gt;我们理应将 3.6 版本作为 Firefox 的一个里程碑版本来对待。&lt;br /&gt;对于本文而言，&lt;br /&gt;也就是传说中用来区别 Firefox 的诸多特征已经消失：&lt;br /&gt;比如最令人熟知的 window.getBoxObjectFor() ，&lt;br /&gt;再比如 /a/[-1] == 'a' 这个 trick 。&lt;br /&gt;因此我们必须找一个新的特征来填上这个漏洞，&lt;br /&gt;（想从 Firefox 1.0 找一个延续至今的特征极为困难）&lt;br /&gt;查阅文档后你刚好可以找到一个从 3.6 开始&lt;br /&gt;&lt;span style="color: #000000;"&gt;&lt;del&gt;被 Firefox 用来保存拖拽时数据的方法 window.DataTransfer() ，&lt;/del&gt; （经查 IE8 同样支持）&lt;br /&gt;被 Firefox 提供用以获取可视区域相对窗口横/纵坐标的属性 window.mozInnerScreenX/Y ，&lt;br /&gt;而且经测试在最新的 Firefox 3.7a1pre nightly 也得到支持。&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;因此经过整理汇总，&lt;br /&gt;我个人比较赞同的特征探测方法如下：&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;&lt;code&gt;&lt;span style="color: #000000;"&gt;(function&amp;nbsp;(win,&amp;nbsp;doc)&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;isIE&amp;nbsp;=&amp;nbsp;!','.split(/,/).length, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isIE6&amp;nbsp;=&amp;nbsp;isIE&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;!win.XMLHttpRequest, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isIE8&amp;nbsp;=&amp;nbsp;!!win.XDomainRequest, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isIE9&amp;nbsp;=&amp;nbsp;isIE&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;!!+'\v1',&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;alt:&amp;nbsp;!!-[1,] &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isIE7&amp;nbsp;=&amp;nbsp;isIE&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;!isIE6&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;!isIE8&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;!isIE9, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isFF&amp;nbsp;=&amp;nbsp;!!doc.getBoxObjectFor&amp;nbsp;||&amp;nbsp;'mozInnerScreenX'&amp;nbsp;in&amp;nbsp;win,&amp;nbsp;//&amp;nbsp;gecko &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isOP&amp;nbsp;=&amp;nbsp;!!win.opera&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;!!win.opera.toString().indexOf('Opera'), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isOP9&amp;nbsp;=&amp;nbsp;/^function&amp;nbsp;\(/.test([].sort), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isWK&amp;nbsp;=&amp;nbsp;!!win.devicePixelRatio,&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;web-kit &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isSF&amp;nbsp;=&amp;nbsp;/a/.__proto__&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;&amp;nbsp;&amp;nbsp;//&amp;nbsp;safari &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isCR&amp;nbsp;=&amp;nbsp;/s/.test(/a/.toString);&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;chrome &lt;br /&gt;})(window,&amp;nbsp;document);&lt;/span&gt; &lt;/code&gt;&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;其中值得一提的是 web-kit 内核，&lt;br /&gt;window.devicePixelRatio() 能区分出所有的 web-kit based 浏览器，&lt;br /&gt;其中包括 Maxthon 3 的极速模式和其它尚未发布的类似浏览器。&lt;br /&gt;Chrome 比 Safari 多一个 window.MessageEvent，&lt;br /&gt;但要注意排除 Firefox。&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;最后呢，&lt;br /&gt;我还整理了 Firefox 重大里程碑版本的特征判断法，&lt;br /&gt;不建议被纳入通用的判断方法里，&lt;br /&gt;但是如果你刚好被这些版本的差异所困扰时，&lt;br /&gt;它们应该能有用：&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;&lt;code&gt;&lt;span style="color: #000000;"&gt;(function&amp;nbsp;(win)&amp;nbsp;{ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;aboveFF36&amp;nbsp;=&amp;nbsp;'mozInnerScreenX'&amp;nbsp;in&amp;nbsp;win,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;alt:&amp;nbsp;!!document.body.mozMatchesSelector &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;aboveFF35&amp;nbsp;=&amp;nbsp;!!Object.getPrototypeOf, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;aboveFF2&amp;nbsp;=&amp;nbsp;!!win.globalStorage; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;aboveFF3&amp;nbsp;=&amp;nbsp;upperFF2&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;!!win.MessageEvent,&amp;nbsp;//&amp;nbsp;alt:&amp;nbsp;'reduce'&amp;nbsp;in&amp;nbsp;Array &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;aboveFF15&amp;nbsp;=&amp;nbsp;'some'&amp;nbsp;in&amp;nbsp;Array; &lt;br /&gt;})(window);&lt;/span&gt; &lt;/code&gt;&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;其中 3.0 和 3.6 是我个人觉得变化最大的两个里程碑版本，&lt;br /&gt;指不定什么时候你就能用到。&lt;br /&gt;另外，&lt;br /&gt;Firefox 的重大版本变化几乎总跟随着 Javascript 的版本升级，&lt;br /&gt;因此对于 3.0 版本你还可以用 'reduce' in Array 来判断，&lt;br /&gt;当然前提是你没有对 Array 进行扩展。&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;Ref:&lt;br /&gt;《&lt;a target="_blank" href="http://www.hitigon.com/web/javascript%E6%B5%8F%E8%A7%88%E5%99%A8%E7%BB%88%E6%9E%81%E6%8A%80%E5%B7%A7/" title="Javascript浏览器判断终极技巧"&gt;Javascript浏览器判断终极技巧&lt;/a&gt;》 &lt;br /&gt;《&lt;a target="_blank" href="http://www.cnblogs.com/leadzen/archive/2008/09/06/1285764.html" title="JavaScript 判断浏览器类型及版本"&gt;JavaScript 判断浏览器类型及版本&lt;/a&gt;》 &lt;br /&gt;（以上两文总结得都很不错，&lt;br /&gt;　只是 Firefox 和 Safari 的部分需要更新）&lt;br /&gt;《&lt;a target="_blank" href="http://www.thespanner.co.uk/2009/01/29/detecting-browsers-javascript-hacks/" title="Detecting browsers javascript hacks"&gt;Detecting browsers javascript hacks&lt;/a&gt;》 （推荐）&lt;/p&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;写得很乱很杂，&lt;br /&gt;这次就不照例上图了&amp;hellip;&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2081480.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2011/06/15/2081480.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
