<?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-04-29T07:13:16Z</updated><author><name>jenry(云飞扬)</name><uri>http://www.cnblogs.com/jenry/</uri></author><generator>feed.cnblogs.com</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/04/29/2476194.html</id><title type="text">用UglifyJS解析/压缩/格式化你的Javascript</title><summary type="text">UglifyJS是基于 NodeJS 的Javascript语法解析/压缩/格式化工具，它支持任何CommonJS模块系统的Javascript平台（实现自己的CommonJS平台也非难事）。 UglifyJS通过解析重新生成JS代码的语法树，你可以通过AST以了解更多代码情况，或者自己来做一个不同的实现。UglifyJS解析器是在 parse-js.js 中实现的，它是非常优秀的 parse。非安全转换UglifyJS是基于 NodeJS 的Javascript语法解析/压缩/格式化工具，它支持任何CommonJS模块系统的Javascript平台（实现自己的CommonJS平台也非难事）。</summary><published>2012-04-29T07:10:00Z</published><updated>2012-04-29T07:10: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/04/29/2476194.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/04/29/2476194.html"/><content type="html">&lt;p&gt;&lt;span&gt;UglifyJS是基于 NodeJS 的Javascript语法解析/压缩/格式化工具，它支持任何CommonJS模块系统的Javascript平台（实现自己的CommonJS平台也非难事）。 UglifyJS通过解析重新生成JS代码的语法树，你可以通过AST以了解更多代码情况，或者自己来做一个不同的实现。UglifyJS解析器是在 parse-js.js 中实现的，它是非常优秀的 parse。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;非安全转换&lt;/strong&gt;&lt;/p&gt;&lt;div class="post-content"&gt;&lt;p&gt;UglifyJS是基于 NodeJS 的Javascript语法解析/压缩/格式化工具，它支持任何CommonJS模块系统的Javascript平台（实现自己的CommonJS平台也非难事）。&lt;/p&gt;&lt;p&gt;UglifyJS通过解析重新生成JS代码的语法树，你可以通过AST以了解更多代码情况，或者自己来做一个不同的实现。UglifyJS解析器是在 parse-js.js 中实现的，它是非常优秀的 parse-js Common Lisp Library 的一部分。&lt;/p&gt;&lt;p&gt;（如果你正在查找UglifyJS的Common Lisp版本，点击 这里 ）&lt;/p&gt;&lt;p&gt;UglifyJS的另一个重要部分是在 process.js 实现的，它用于检查并实现解析生成的AST：&amp;nbsp;&lt;/p&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;通过AST进行Javascript代码的重新生成 ：如果你想格式化已经被压缩过的代码，可以选择缩进参数。你也可以打印出无空白（whitespace）的AST，以达到压缩的目的。&lt;/li&gt;&lt;li&gt;缩短变量名 ：UglifyJS通过分析代码并生成新的变量名称，依赖于作用域，这些名称通常被简化为单一字符，并能足够智能的处理全局变量，或者eval()调用及with{}块。换句话说，如果在某个作用域内使用了eval()或with{}，那么该作用域的所有变量及其父作用域的变量都不会被重新命名，并且所有指向这类变量的引用也不会被改变。&lt;/li&gt;&lt;li&gt;以下是一些优化规则会让代码更简洁更高效 ：&amp;nbsp;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;foo["bar"] ==&amp;gt; foo.bar&lt;/li&gt;&lt;li&gt;删除块标记{}&lt;/li&gt;&lt;li&gt;合并变量声明： var a = 10; var b = 20; ==&amp;gt; var a=10,b=20;&lt;/li&gt;&lt;li&gt;计算简单的常量表达式：1 + 2 * 3 ==&amp;gt; 7. UglifyJS只替换计算结果比实际表达式字节更少的情况；比如 1/3 结果为 0.333333333333，因此不会被替换。&lt;/li&gt;&lt;li&gt;连续的语句块会被合并为一个序列；大多情况下，这将保留一个语句，接下来块括号可以被移除。&lt;/li&gt;&lt;li&gt;IF语句的优化 ：&amp;nbsp;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;if (foo) bar(); else baz(); ==&amp;gt; foo?bar():baz();&lt;/li&gt;&lt;li&gt;if (!foo) bar(); else baz(); ==&amp;gt; foo?baz():bar();&lt;/li&gt;&lt;li&gt;if (foo) bar(); ==&amp;gt; foo&amp;amp;&amp;amp;bar();&lt;/li&gt;&lt;li&gt;if (!foo) bar(); ==&amp;gt; foo||bar();&lt;/li&gt;&lt;li&gt;if (foo) return bar(); else return baz(); ==&amp;gt; return foo?bar():baz();&lt;/li&gt;&lt;li&gt;if (foo) return bar(); else something(); ==&amp;gt; {if(foo)return bar();something()}&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;移除不会被用到的代码并会给出警告。&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;非安全转换&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;UglifyJS在保留语义的同时会尽量提高压缩比率，如果经过UglifyJS处理后你的代码逻辑失效了，或对UglifyJS的优化实现有更好的想法，都可有直接联系作者。&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;涉及到全局数组构造函数的调用&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;这时会进行如下转换：&lt;/p&gt;&lt;span class="kwd"&gt;new&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;1&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;2&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;3&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;4&lt;/span&gt;&lt;span class="pun"&gt;)&lt;/span&gt;&lt;span class="pun"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="pun"&gt;[&lt;/span&gt;&lt;span class="lit"&gt;1&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;2&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;3&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;4&lt;/span&gt;&lt;span class="pun"&gt;]&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="pln"&gt;a&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="pln"&gt; b&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="pln"&gt; c&lt;/span&gt;&lt;span class="pun"&gt;)&lt;/span&gt;&lt;span class="pun"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="pun"&gt;[&lt;/span&gt;&lt;span class="pln"&gt;a&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="pln"&gt;b&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="pln"&gt;c&lt;/span&gt;&lt;span class="pun"&gt;]&lt;/span&gt;&lt;span class="kwd"&gt;new&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;5&lt;/span&gt;&lt;span class="pun"&gt;)&lt;/span&gt;&lt;span class="pun"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;5&lt;/span&gt;&lt;span class="pun"&gt;)&lt;/span&gt;&lt;span class="kwd"&gt;new&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="pln"&gt;a&lt;/span&gt;&lt;span class="pun"&gt;)&lt;/span&gt;&lt;span class="pun"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="pln"&gt;a&lt;/span&gt;&lt;span class="pun"&gt;)&lt;/span&gt;&lt;p&gt;在Array没有被重新定义之前，这些转换是安全的。UglifyJS也会对经过用户本地或全局重定义的Array进行处理，但CSSer建议还是不要这么做：&lt;/p&gt;&lt;span class="com"&gt;// case 1.  全局声明&lt;/span&gt;&lt;span class="kwd"&gt;var&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;;&lt;/span&gt;&lt;span class="kwd"&gt;new&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;1&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;2&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="pln"&gt; www&lt;/span&gt;&lt;span class="pun"&gt;.&lt;/span&gt;&lt;span class="pln"&gt;csser&lt;/span&gt;&lt;span class="pun"&gt;.&lt;/span&gt;&lt;span class="pln"&gt;com&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="pln"&gt;a&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="pln"&gt; b&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="com"&gt;// 或者后声明&lt;/span&gt;&lt;span class="kwd"&gt;new&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;1&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;2&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;3&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="kwd"&gt;var&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;;&lt;/span&gt;&lt;span class="com"&gt;// 或者定义为函数&lt;/span&gt;&lt;span class="kwd"&gt;new&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;1&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;2&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;3&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="kwd"&gt;function&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;()&lt;/span&gt;&lt;span class="pun"&gt;{&lt;/span&gt;&lt;span class="pun"&gt;...&lt;/span&gt;&lt;span class="pun"&gt;}&lt;/span&gt;&lt;span class="com"&gt;// case 2. 在函数内部声明&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="kwd"&gt;function&lt;/span&gt;&lt;span class="pun"&gt;(){&lt;/span&gt;&lt;span class="pln"&gt;&lt;br/&gt;    a &lt;/span&gt;&lt;span class="pun"&gt;=&lt;/span&gt;&lt;span class="kwd"&gt;new&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;1&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;2&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;3&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="pln"&gt;&lt;br/&gt;    b &lt;/span&gt;&lt;span class="pun"&gt;=&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;5&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;6&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="kwd"&gt;var&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;;&lt;/span&gt;&lt;span class="pun"&gt;})();&lt;/span&gt;&lt;span class="com"&gt;// 或者&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="kwd"&gt;function&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;){&lt;/span&gt;&lt;span class="kwd"&gt;return&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;5&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;6&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;7&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="pun"&gt;})();&lt;/span&gt;&lt;span class="com"&gt;// 或者&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="kwd"&gt;function&lt;/span&gt;&lt;span class="pun"&gt;(){&lt;/span&gt;&lt;span class="kwd"&gt;return&lt;/span&gt;&lt;span class="kwd"&gt;new&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="lit"&gt;1&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;2&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;3&lt;/span&gt;&lt;span class="pun"&gt;,&lt;/span&gt;&lt;span class="lit"&gt;4&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="kwd"&gt;function&lt;/span&gt;&lt;span class="typ"&gt;Array&lt;/span&gt;&lt;span class="pun"&gt;()&lt;/span&gt;&lt;span class="pun"&gt;{&lt;/span&gt;&lt;span class="pun"&gt;...&lt;/span&gt;&lt;span class="pun"&gt;}&lt;/span&gt;&lt;span class="pun"&gt;})();&lt;/span&gt;&lt;p&gt;// 等等.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;安装&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;通过NPM安装&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;UglifyJS已经可以通过NPM进行安装：&lt;/p&gt;&lt;span class="pln"&gt;npm install uglify&lt;/span&gt;&lt;span class="pun"&gt;-&lt;/span&gt;&lt;span class="pln"&gt;js&lt;/span&gt;&amp;nbsp;&lt;p&gt;&lt;strong&gt;通过GitHub安装最新版本&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;span class="com"&gt;## 克隆仓库&lt;/span&gt;&lt;span class="pln"&gt;&lt;br/&gt;mkdir &lt;/span&gt;&lt;span class="pun"&gt;-&lt;/span&gt;&lt;span class="pln"&gt;p &lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="kwd"&gt;where&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;you&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;wanna&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;put&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;it&lt;br/&gt;cd &lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="kwd"&gt;where&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;you&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;wanna&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;put&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;it&lt;br/&gt;git clone git&lt;/span&gt;&lt;span class="pun"&gt;:&lt;/span&gt;&lt;span class="com"&gt;//github.com/mishoo/UglifyJS.git&lt;/span&gt;&lt;span class="com"&gt;## 让uglify模块对NodeJS有效&lt;/span&gt;&lt;span class="pln"&gt;&lt;br/&gt;mkdir &lt;/span&gt;&lt;span class="pun"&gt;-&lt;/span&gt;&lt;span class="pln"&gt;p &lt;/span&gt;&lt;span class="pun"&gt;~&lt;/span&gt;&lt;span class="str"&gt;/.node_libraries/&lt;/span&gt;&lt;span class="pln"&gt;&lt;br/&gt;cd &lt;/span&gt;&lt;span class="pun"&gt;~&lt;/span&gt;&lt;span class="str"&gt;/.node_libraries/&lt;/span&gt;&lt;span class="pln"&gt;&lt;br/&gt;ln &lt;/span&gt;&lt;span class="pun"&gt;-&lt;/span&gt;&lt;span class="pln"&gt;s &lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="kwd"&gt;where&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;you&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;wanna&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;put&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;it&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="typ"&gt;UglifyJS&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;uglify&lt;/span&gt;&lt;span class="pun"&gt;-&lt;/span&gt;&lt;span class="pln"&gt;js&lt;/span&gt;&lt;span class="pun"&gt;.&lt;/span&gt;&lt;span class="pln"&gt;js&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span class="com"&gt;## 支持命令行的方式调用&lt;/span&gt;&lt;span class="pln"&gt;&lt;br/&gt;mkdir &lt;/span&gt;&lt;span class="pun"&gt;-&lt;/span&gt;&lt;span class="pln"&gt;p &lt;/span&gt;&lt;span class="pun"&gt;~&lt;/span&gt;&lt;span class="str"&gt;/bin&lt;br/&gt;cd ~/&lt;/span&gt;&lt;span class="pln"&gt;bin&lt;br/&gt;ln &lt;/span&gt;&lt;span class="pun"&gt;-&lt;/span&gt;&lt;span class="pln"&gt;s &lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="kwd"&gt;where&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;you&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;wanna&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;put&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;it&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="typ"&gt;UglifyJS&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;bin&lt;/span&gt;&lt;span class="pun"&gt;/&lt;/span&gt;&lt;span class="pln"&gt;uglifyjs&lt;br/&gt;  &lt;/span&gt;&lt;span class="com"&gt;# (然后将 ~/bin 增加到 $PATH)&lt;/span&gt;&lt;p&gt;&lt;strong&gt;如何使用&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;UglifyJS提供了命令行工具，支持shell脚本的操作需要：&lt;/p&gt;&lt;span class="pln"&gt;uglifyjs &lt;/span&gt;&lt;span class="pun"&gt;[&lt;/span&gt;&lt;span class="pun"&gt;选项...&lt;/span&gt;&lt;span class="pun"&gt;]&lt;/span&gt;&lt;span class="pun"&gt;[&lt;/span&gt;&lt;span class="pun"&gt;文件名&lt;/span&gt;&lt;span class="pun"&gt;]&lt;/span&gt;&lt;p&gt;最后一个参数是要处理的JS文件名，如果不指定，则从标准输入（STDIN）读取。&lt;/p&gt;&lt;p&gt;支持的选项 ：&lt;/p&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;-b 或 --beautify - 输出格式化代码，当传入该参数，下面的附加选项用于更美观的控制格式化：&amp;nbsp;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;-i N 或 --indent N - 缩进级别（空格数量）&lt;/li&gt;&lt;li&gt;-q 或 --quote-keys - 是否用引号引起字符串对象的键（默认只会引起不能被正确标志的键名）&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;--ascii -默认 UglifyJS 不处理字符编码而直接输出 Unicode 字符，通过传入该参数将非ASCII编码的字符转化为\cXXXX的序列（输出总按照UTF8编码，但传入该选项能得到ASCII编码的输出）。&lt;/li&gt;&lt;li&gt;-nm 或 --no-mangle - 不改变变量名称&lt;/li&gt;&lt;li&gt;-ns 或 --no-squeeze - 不调用 ast_squeeze() 函数（该函数会做多种优化使得结果更小，可读性略有降低）&lt;/li&gt;&lt;li&gt;-mt 或 --mangle-toplevel - 在顶级作用域打乱变量名称（默认不开启）&lt;/li&gt;&lt;li&gt;--no-seqs - 当调用 ast_squeeze() 将会合并多个语句块为一个语句块，如 "a=10; b=20; foo()" 将被转换为 "a=10,b=20,foo()"&lt;/li&gt;&lt;li&gt;--no-dead-code - 默认 UglifyJS 将会删除不被用到的代码，传入该参数禁用此功能。&lt;/li&gt;&lt;li&gt;-nc 或 --no-copyright - 默认 uglifyjs 会在输出后的代码中添加版权信息等注释代码，传入该参数禁用此功能。&lt;/li&gt;&lt;li&gt;-o 文件名 或 --output 文件名 - 指定输出文件名，如果不指定，则打印到标准输出（STDOUT）&lt;/li&gt;&lt;li&gt;--overwrite - 如果传入的JS代码来自文件而不是标准输入，传入该参数，输出会覆盖该文件。&lt;/li&gt;&lt;li&gt;--ast - 传入该参数会得到抽象的语法树而不是Javascript，对调试或了解内部代码很有用。&lt;/li&gt;&lt;li&gt;-v 或 --verbose - 在标准错误输出一些信息（目前的版本仅输出操作用时）&lt;/li&gt;&lt;li&gt;--extra - 开启附加优化，这些优化并未得到全面的测试。&lt;/li&gt;&lt;li&gt;--unsafe - 开启其他附加优化，这些优化已知在特定情况下并不安全，目前仅支持：&amp;nbsp;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;foo.toString() ==&amp;gt; foo+&amp;rdquo;&amp;rdquo;&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;--max-line-len （默认32K字节） - 在32K字节出增加换行符，传入0禁用此功能。&lt;/li&gt;&lt;li&gt;--reserved-names - 一些类库会依赖一些变量，该参数指定的名称不会被混淆掉，多个用逗号隔开。&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;API&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;要想在Javascript中使用UglifyJS类库，参考下面的示例（以NodeJS为例）：&lt;/p&gt;&lt;span class="kwd"&gt;var&lt;/span&gt;&lt;span class="pln"&gt; jsp &lt;/span&gt;&lt;span class="pun"&gt;=&lt;/span&gt;&lt;span class="kwd"&gt;require&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="str"&gt;"uglify-js"&lt;/span&gt;&lt;span class="pun"&gt;).&lt;/span&gt;&lt;span class="pln"&gt;parser&lt;/span&gt;&lt;span class="pun"&gt;;&lt;/span&gt;&lt;span class="kwd"&gt;var&lt;/span&gt;&lt;span class="pln"&gt; pro &lt;/span&gt;&lt;span class="pun"&gt;=&lt;/span&gt;&lt;span class="kwd"&gt;require&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="str"&gt;"uglify-js"&lt;/span&gt;&lt;span class="pun"&gt;).&lt;/span&gt;&lt;span class="pln"&gt;uglify&lt;/span&gt;&lt;span class="pun"&gt;;&lt;/span&gt;&lt;span class="kwd"&gt;var&lt;/span&gt;&lt;span class="pln"&gt; orig_code &lt;/span&gt;&lt;span class="pun"&gt;=&lt;/span&gt;&lt;span class="str"&gt;"Javascript代码"&lt;/span&gt;&lt;span class="pun"&gt;;&lt;/span&gt;&lt;span class="kwd"&gt;var&lt;/span&gt;&lt;span class="pln"&gt; ast &lt;/span&gt;&lt;span class="pun"&gt;=&lt;/span&gt;&lt;span class="pln"&gt; jsp&lt;/span&gt;&lt;span class="pun"&gt;.&lt;/span&gt;&lt;span class="pln"&gt;parse&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="pln"&gt;orig_code&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="com"&gt;// 解析代码返回初始的AST&lt;/span&gt;&lt;span class="pln"&gt;&lt;br/&gt;ast &lt;/span&gt;&lt;span class="pun"&gt;=&lt;/span&gt;&lt;span class="pln"&gt; pro&lt;/span&gt;&lt;span class="pun"&gt;.&lt;/span&gt;&lt;span class="pln"&gt;ast_mangle&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="pln"&gt;ast&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="com"&gt;// 获取变量名称打乱的AST&lt;/span&gt;&lt;span class="pln"&gt;&lt;br/&gt;ast &lt;/span&gt;&lt;span class="pun"&gt;=&lt;/span&gt;&lt;span class="pln"&gt; pro&lt;/span&gt;&lt;span class="pun"&gt;.&lt;/span&gt;&lt;span class="pln"&gt;ast_squeeze&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="pln"&gt;ast&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="com"&gt;// 获取经过压缩优化的AST&lt;/span&gt;&lt;span class="kwd"&gt;var&lt;/span&gt;&lt;span class="pln"&gt; final_code &lt;/span&gt;&lt;span class="pun"&gt;=&lt;/span&gt;&lt;span class="pln"&gt; pro&lt;/span&gt;&lt;span class="pun"&gt;.&lt;/span&gt;&lt;span class="pln"&gt;gen_code&lt;/span&gt;&lt;span class="pun"&gt;(&lt;/span&gt;&lt;span class="pln"&gt;ast&lt;/span&gt;&lt;span class="pun"&gt;);&lt;/span&gt;&lt;span class="com"&gt;// 压缩后的代码&lt;/span&gt;&lt;p&gt;上面的代码会立刻进行代码的全面压缩，正如你所看到的，这里经历了一系列的步骤，你可有省略某些步骤以满足自己的需求。&lt;/p&gt;&lt;p&gt;这里的函数有一些参数，我们做些介绍：&amp;nbsp;&lt;/p&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;jsp.parse(code, strict_semicolons) - 解析JS代码并返回AST。strict_semicolons是可选的，默认为false，当传入true，解析器会在预期为分号而实际没找到的情况下抛出错误。对于大多数JS代码我们不需要那么做，但严格约束代码很有益处。&lt;/li&gt;&lt;li&gt;pro.ast_mangle(ast, options) - 返回经过变量和函数名称混淆的AST，它支持以下选项：&amp;nbsp;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;toplevel - 混淆顶级作用域的变量和函数名称（默认不开启）。&lt;/li&gt;&lt;li&gt;except - 指定不被压缩的名称的数组&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;pro.ast_squeeze(ast, options) - 开启深度优化以降低代码尺寸，返回新的AST，选项可以是一个hash，支持的参数有：&amp;nbsp;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;make_seqs （默认true） 将多个语句块合并为一个。&lt;/li&gt;&lt;li&gt;dead_code （默认true） 将删除不被使用的代码。&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;pro.gen_code(ast, options) - 通过AST生成JS代码。默认输出压缩代码，但可以通过调整选项参数获得格式化的输出。选项是可选的，如果传入必须为对象，支持以下选项：&amp;nbsp;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;beautify: false - 如果希望得到格式化的输出，传入true&lt;/li&gt;&lt;li&gt;indent_start: 0 （仅当beautify为true时有效） - 初始缩进空格&lt;/li&gt;&lt;li&gt;indent_level: 4 （仅当beautify为true时有效） - 缩进级别，空格数量&lt;/li&gt;&lt;li&gt;quote_keys: false - 传入true将会用引号引起所有文本对象的key&lt;/li&gt;&lt;li&gt;space_colon: false （仅当beautify为true时有效） - 是否在冒号前保留空格&lt;/li&gt;&lt;li&gt;ascii_only: false - 传入true则将编码非ASCII字符到\uXXXX&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;结语&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;UglifyJS在语法上与Google压缩相似，在性能上是一流的，所以建议多多实践！&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2476194.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/04/29/2476194.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2012/03/28/2421849.html</id><title type="text">javascript获取url参数和script标签中获取url参数</title><summary type="text">url paramter：Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--&gt;//lastest:var queryStrings=function() {//get url querystring var params=document.location.search,reg=/(?:^\?|&amp;)(.*?)=(.*?)(?=&amp;|$)/g,temp,args={}; while((temp=reg.exec(params))!=n</summary><published>2012-03-28T11:16:00Z</published><updated>2012-03-28T11:16: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/03/28/2421849.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/03/28/2421849.html"/><content type="html">&lt;p&gt;&lt;span&gt;url paramter：&lt;/span&gt;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--&amp;gt;//lastest:&lt;br/&gt;var queryStrings=function() {//get url querystring&lt;br/&gt;    var params=document.location.search,reg=/(?:^\?|&amp;amp;)(.*?)=(.*?)(?=&amp;amp;|$)/g,temp,args={};&lt;br/&gt;    while((temp=reg.exec(params))!=null) args[temp[1]]=decodeURIComponent(temp[2]);&lt;br/&gt;    return args;&lt;br/&gt;};&lt;br/&gt;//只取一个：&lt;br/&gt;var queryString=function(key){&lt;br/&gt;    return (document.location.search.match(new RegExp("(?:^\\?|&amp;amp;)"+key+"=(.*?)(?=&amp;amp;|$)"))||['',null])[1];&lt;br/&gt;}&lt;br/&gt;var args=getArgs();&lt;br/&gt;alert(args.name+" | "+args.sex+" | "+args.age);&lt;br/&gt;//测试链接：&amp;lt;a href="?name=abc&amp;amp;sex=男&amp;amp;age=12"&amp;gt;test getQueryString&amp;lt;/a&amp;gt;&lt;br/&gt;&lt;/div&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;script paramter:&lt;/span&gt;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;var getScriptArgs=function(){//获取多个参数&lt;br/&gt;    var scripts=document.getElementsByTagName("script"),&lt;br/&gt;    script=scripts[scripts.length-1],//因为当前dom加载时后面的script标签还未加载，所以最后一个就是当前的script&lt;br/&gt;    src=script.src,&lt;br/&gt;    reg=/(?:\?|&amp;amp;)(.*?)=(.*?)(?=&amp;amp;|$)/g,&lt;br/&gt;    temp,res={};&lt;br/&gt;    while((temp=reg.exec(src))!=null) res[temp[1]]=decodeURIComponent(temp[2]);&lt;br/&gt;    return res;&lt;br/&gt;};&lt;br/&gt;var args=getScriptArgs();&lt;br/&gt;alert(args.a+" | "+args.b+" | "+args.c);&lt;br/&gt;//假如上面的js是在这个js1.js的脚本中&amp;lt;script type="text/javascript" src="js1.js?a=abc&amp;amp;b=汉字&amp;amp;c=123"&amp;gt;&amp;lt;/script&amp;gt;&lt;br/&gt;&lt;br/&gt;var getScriptArg=function(key){//获取单个参数&lt;br/&gt;    var scripts=document.getElementsByTagName("script"),&lt;br/&gt;    script=scripts[scripts.length-1],&lt;br/&gt;    src=script.src;&lt;br/&gt;    return (src.match(new RegExp("(?:\\?|&amp;amp;)"+key+"=(.*?)(?=&amp;amp;|$)"))||['',null])[1];&lt;br/&gt;};&lt;br/&gt;alert(getScriptArg("c"));&lt;br/&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;ps:不要在方法中调用此方法，否则可能始终获取的是最后一个js的文件的参数，要在方法中使用，请先放到js加载时就会执行的变量中&amp;nbsp;保存&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2421849.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/03/28/2421849.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/jenry/archive/2012/02/12/2347983.html</id><title type="text">梁友栋-Barsky裁剪算法</title><summary type="text">梁友栋-Barsky裁剪算法 Cyrus和Beck用参数化方法提出了比Cohen-Sutherland更有效的算法。后来梁友栋和Barsky独立地提出了更快的参数化线段裁剪算法，也称为Liany-Barsky（LB）算法。一、梁友栋-Barsky裁剪算法思想： 我们知道，一条两端点为P1（x1，y1）、P2（x2，y2）的线段可以用参数方程形式表示：x= x1+ u·（x2-x1）= x1+ u·Δxy= y1+ u·（y2-y1）= y1+ u·Δy0≤u≤1（1）式中，Δx=x2-x1，Δy=y2-y1，参数u在0～1之间取值，P（x，y）代表了</summary><published>2012-02-12T07:02:00Z</published><updated>2012-02-12T07:02: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/12/2347983.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/jenry/archive/2012/02/12/2347983.html"/><content type="html">&lt;p&gt;&lt;strong&gt;梁友栋-Barsky裁剪算法&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Cyrus和Beck用参数化方法提出了比Cohen-Sutherland更有效的算法。后来梁友栋和Barsky独立地提出了更快的参数化线段裁剪算法，也称为Liany-Barsky（LB）算法。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://course.cug.edu.cn/cugFirst/computer_graphics/class/course/3-2-3-a.htm##"&gt;一、梁友栋-Barsky裁剪算法思想：&lt;/a&gt;&lt;/p&gt;&lt;p&gt;我们知道，一条两端点为P&lt;sub&gt;1&lt;/sub&gt;（x&lt;sub&gt;1&lt;/sub&gt;，y&lt;sub&gt;1&lt;/sub&gt;）、P&lt;sub&gt;2&lt;/sub&gt;（x&lt;sub&gt;2&lt;/sub&gt;，y&lt;sub&gt;2&lt;/sub&gt;）的线段可以用参数方程形式表示：&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div&gt;&lt;table style="float: left;" border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td width="10"&gt;&amp;nbsp;&lt;/td&gt;&lt;td width="273"&gt;&lt;p&gt;x= x&lt;sub&gt;1&lt;/sub&gt;+ u&amp;middot;（x&lt;sub&gt;2&lt;/sub&gt;-x&lt;sub&gt;1&lt;/sub&gt;）= x&lt;sub&gt;1&lt;/sub&gt;+ u&amp;middot;&amp;Delta;x&lt;/p&gt;&lt;p&gt;&amp;nbsp;y= y&lt;sub&gt;1&lt;/sub&gt;+ u&amp;middot;（y&lt;sub&gt;2&lt;/sub&gt;-y&lt;sub&gt;1&lt;/sub&gt;）= y&lt;sub&gt;1&lt;/sub&gt;+ u&amp;middot;&amp;Delta;y　&lt;/p&gt;&lt;/td&gt;&lt;td width="61"&gt;&lt;p&gt;0&amp;le;u&amp;le;1&lt;/p&gt;&lt;/td&gt;&lt;td width="57"&gt;&lt;p align="right"&gt;（1）&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;　&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;式中，&amp;Delta;x=x&lt;sub&gt;2&lt;/sub&gt;-x&lt;sub&gt;1&lt;/sub&gt;，&amp;Delta;y=y&lt;sub&gt;2&lt;/sub&gt;-y&lt;sub&gt;1&lt;/sub&gt;，参数u在0～1之间取值，P（x，y）代表了该线段上的一个点，其值由参数u确定，由公式可知，当u=0时，该点为P&lt;sub&gt;1&lt;/sub&gt;（x&lt;sub&gt;1&lt;/sub&gt;，y&lt;sub&gt;1&lt;/sub&gt;），当u=1时，该点为P&lt;sub&gt;2&lt;/sub&gt;（x&lt;sub&gt;2&lt;/sub&gt;，y&lt;sub&gt;2&lt;/sub&gt;）。如果点P（x，y）位于由坐标（x&lt;sub&gt;min&lt;/sub&gt;，y&lt;sub&gt;min&lt;/sub&gt;）和（x&lt;sub&gt;max&lt;/sub&gt;，y&lt;sub&gt;max&lt;/sub&gt;）所确定的窗口内，那么下式成立：&lt;/p&gt;&lt;div&gt;&lt;table style="float: left;" border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td width="10"&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&lt;p&gt;x&lt;sub&gt;min&lt;/sub&gt;&amp;le;x&lt;sub&gt;1&lt;/sub&gt;+ u&amp;middot;&amp;Delta;x&amp;le;x&lt;sub&gt;max&lt;/sub&gt;&lt;/p&gt;&lt;p&gt;y&lt;sub&gt;min&lt;/sub&gt;&amp;le;y&lt;sub&gt;1&lt;/sub&gt;+ u&amp;middot;&amp;Delta;y&amp;le;y&lt;sub&gt;max&lt;/sub&gt;&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p align="right"&gt;（2）&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;这四个不等式可以表示为：&lt;/p&gt;&lt;div&gt;&lt;table border="0" cellspacing="0" cellpadding="0" align="left"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: left;"&gt;&lt;p&gt;u&amp;middot;p&lt;sub&gt;k&lt;/sub&gt; &amp;le;q&lt;sub&gt;k&lt;/sub&gt; ， k=1，2，3，4&lt;/p&gt;&lt;/td&gt;&lt;td style="text-align: left;"&gt;&lt;p align="right"&gt;（3）&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;其中，p、q定义为：&amp;nbsp;&lt;/p&gt;&lt;div&gt;&lt;table style="float: left;" border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td width="10"&gt;&amp;nbsp;&lt;/td&gt;&lt;td&gt;&lt;p&gt;p&lt;sub&gt;1&lt;/sub&gt;=-&amp;Delta;x， q&lt;sub&gt;1&lt;/sub&gt;=x&lt;sub&gt;1&lt;/sub&gt;-x&lt;sub&gt;min&lt;/sub&gt; p&lt;sub&gt;2&lt;/sub&gt;= &amp;Delta;x， q&lt;sub&gt;2&lt;/sub&gt;=x&lt;sub&gt;max&lt;/sub&gt;-x&lt;sub&gt;1&lt;/sub&gt; p&lt;sub&gt;3&lt;/sub&gt;=-&amp;Delta;y， q&lt;sub&gt;3&lt;/sub&gt;=y&lt;sub&gt;1&lt;/sub&gt;-y&lt;sub&gt;min&lt;/sub&gt; p&lt;sub&gt;4&lt;/sub&gt;= &amp;Delta;y， q&lt;sub&gt;4&lt;/sub&gt;=y&lt;sub&gt;max&lt;/sub&gt;-y&lt;sub&gt;1&lt;/sub&gt;&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p align="right"&gt;（4）&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;从（4）式可以知道：任何平行于窗口某边界的直线，其p&lt;sub&gt;k&lt;/sub&gt;=0，k值对应于相应的边界（k=1，2，3，4对应于左、右、下、上边界）。如果还满足q&lt;sub&gt;k&lt;/sub&gt;&amp;lt;0，则线段完全在边界外，应舍弃该线段。如果p&lt;sub&gt;k&lt;/sub&gt;=0并且q&lt;sub&gt;k&lt;/sub&gt;&amp;ge;0，则线段平行于窗口某边界并在窗口内，见图中所示。公式（4）式还告诉我们：&lt;/p&gt;&lt;p&gt;1、当p&lt;sub&gt;k&lt;/sub&gt;&amp;lt;0时，线段从裁剪边界延长线的外部延伸到内部；&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2、当p&lt;sub&gt;k&lt;/sub&gt;&amp;gt;0时，线段从裁剪边界延长线的内部延伸到外部；&lt;/p&gt;&lt;p&gt;&lt;img src="http://hi.csdn.net/attachment/201005/6/0_1273154767Nhxn.gif" alt="" width="171" height="161" /&gt;&lt;/p&gt;&lt;p&gt;例如，当&amp;Delta;x&amp;ge;0时，对于左边界p&lt;sub&gt;1&lt;/sub&gt;&amp;lt;0（p&lt;sub&gt;1&lt;/sub&gt;=-&amp;Delta;x），线段从左边界的外部到内部；&lt;/p&gt;&lt;p&gt; 对于右边界p&lt;sub&gt;2&lt;/sub&gt;&amp;gt;0（p&lt;sub&gt;2&lt;/sub&gt;=&amp;Delta;x），线段从右边界的内部到外部。&lt;/p&gt;&lt;p&gt;　当&amp;Delta;y&amp;lt;0时，对于下边界p&lt;sub&gt;3&lt;/sub&gt;&amp;gt;0（p&lt;sub&gt;3&lt;/sub&gt;=-&amp;Delta;y），线段从下边界的内部到外部；&lt;/p&gt;&lt;p&gt;对于上边界p&lt;sub&gt;4&lt;/sub&gt;&amp;lt;0（p&lt;sub&gt;4&lt;/sub&gt;=&amp;Delta;y），线段从上边界的外部到内部。&lt;/p&gt;&lt;p&gt;　当p&lt;sub&gt;K&lt;/sub&gt;&amp;ne;0时，可以计算出参数u的值，它对应于无限延伸的直线与延伸的窗口边界k的交点，即：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div&gt;&lt;table style="float: left;" border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;u = q&lt;sub&gt;k&lt;/sub&gt;/p&lt;sub&gt;k&lt;/sub&gt;&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p align="right"&gt;（5）&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;对于每条直线，可以计算出参数u&lt;sub&gt;1&lt;/sub&gt;和u&lt;sub&gt;2&lt;/sub&gt;，该值定义了位于窗口内的线段部分：&lt;/p&gt;&lt;p&gt;1、u&lt;sub&gt;1&lt;/sub&gt;的值由线段从外到内遇到的矩形边界所决定（p&lt;sub&gt;k&lt;/sub&gt;&amp;lt;0），对这些边界计算r&lt;sub&gt;k&lt;/sub&gt;=q&lt;sub&gt;k&lt;/sub&gt;/p&lt;sub&gt;k&lt;/sub&gt;，u&lt;sub&gt;1&lt;/sub&gt;取0和各个r值之中的最大值。&lt;/p&gt;&lt;p&gt;2、u&lt;sub&gt;2&lt;/sub&gt;的值由线段从内到外遇到的矩形边界所决定（p&lt;sub&gt;k&lt;/sub&gt;&amp;gt;0），对这些边界计算r&lt;sub&gt;k&lt;/sub&gt;=q&lt;sub&gt;k&lt;/sub&gt;/p&lt;sub&gt;k&lt;/sub&gt;，u&lt;sub&gt;2&lt;/sub&gt;取0和各个r值之中的最小值。&lt;/p&gt;&lt;p&gt;3、如果u&lt;sub&gt;1&lt;/sub&gt;&amp;gt;u&lt;sub&gt;2&lt;/sub&gt;，则线段完全落在裁剪窗口之外，应当被舍弃；否则，被裁剪线段的端点可以由u&lt;sub&gt;1&lt;/sub&gt;和u&lt;sub&gt;2&lt;/sub&gt;计算出来。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://course.cug.edu.cn/cugFirst/computer_graphics/class/course/3-2-3-a.htm##"&gt;二、梁友栋-Barsky裁剪算法实现：&lt;/a&gt;&lt;/p&gt;&lt;p&gt;1、初始化线段交点的参数：u&lt;sub&gt;1&lt;/sub&gt;=0，u&lt;sub&gt;2&lt;/sub&gt;=1；&lt;/p&gt;&lt;p&gt;2、计算出各个裁剪边界的p、q值；&lt;/p&gt;&lt;p&gt;3、根据p、q来判断：是舍弃线段还是改变交点的参数。&lt;/p&gt;&lt;p&gt;　（1） 当p&amp;lt;0时，参数r用于更新u&lt;sub&gt;1&lt;/sub&gt;； 　（u&lt;sub&gt;1&lt;/sub&gt;=max{u&lt;sub&gt;1&lt;/sub&gt;，&amp;hellip;，r&lt;sub&gt;k&lt;/sub&gt;}）&lt;/p&gt;&lt;p&gt;　（2） 当p&amp;gt;0时，参数r用于更新u&lt;sub&gt;2&lt;/sub&gt;。 　（u&lt;sub&gt;2&lt;/sub&gt;=min{u&lt;sub&gt;2&lt;/sub&gt;，&amp;hellip;，r&lt;sub&gt;k&lt;/sub&gt;}）&lt;/p&gt;&lt;p&gt;　（3）如果更新了u&lt;sub&gt;1&lt;/sub&gt;或u&lt;sub&gt;2&lt;/sub&gt;后，使u&lt;sub&gt;1&lt;/sub&gt;&amp;gt;u&lt;sub&gt;2&lt;/sub&gt;，则舍弃该线段。&lt;/p&gt;&lt;p&gt;　（4）当p=0且q&amp;lt;0时，因为线段平行于边界并且位于边界之外，则舍弃该线段。见下图所示。&lt;/p&gt;&lt;p&gt;&lt;img src="http://hi.csdn.net/attachment/201005/6/0_1273154772EYLI.gif" alt="" width="171" height="161" /&gt;&lt;/p&gt;&lt;p&gt;4、p、q的四个值经判断后，如果该线段未被舍弃，则裁剪线段的端点坐标由参数u&lt;sub&gt;1&lt;/sub&gt;和u&lt;sub&gt;2&lt;/sub&gt;的值决定。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;三、算法代码&lt;/p&gt;&lt;p&gt;1.C++&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;bool _rtPruneLB(RtVector&amp;amp; vector, RtRect rect)&lt;br/&gt;{&lt;br/&gt;    RtVector dest;&lt;br/&gt;    bool flag = false;&lt;br/&gt;    float u1 = 0, u2 =1;&lt;br/&gt;    int p[4], q[4];&lt;br/&gt;    float r;&lt;br/&gt;    p[0] = vector.sp.x - vector.ep.x;&lt;br/&gt;    p[1] =   vector.ep.x - vector.sp.x;&lt;br/&gt;    p[2] = vector.sp.y - vector.ep.y;&lt;br/&gt;    p[3] = -vector.sp.y + vector.ep.y;&lt;br/&gt;&lt;br/&gt;    q[0] = vector.sp.x - rect.x;&lt;br/&gt;    q[1] = rect.x + rect.w - vector.sp.x;&lt;br/&gt;    q[2] = vector.sp.y - rect.y;&lt;br/&gt;    q[3] = rect.y + rect.h - vector.sp.y;&lt;br/&gt;&lt;br/&gt;    for(int i = 0; i &amp;lt; 4; i++)&lt;br/&gt;    {&lt;br/&gt;        r = (float)q[i] / (float)p[i];&lt;br/&gt;        if(p[i] &amp;lt; 0)&lt;br/&gt;        {&lt;br/&gt;            u1 = max(u1,r);&lt;br/&gt;            if(u1 &amp;gt; u2)&lt;br/&gt;            {&lt;br/&gt;                flag = true;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;        if(p[i] &amp;gt; 0)&lt;br/&gt;        {&lt;br/&gt;            u2 = min(u2, r);&lt;br/&gt;            if(u1 &amp;gt; u2)&lt;br/&gt;            {&lt;br/&gt;                flag = true;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;        if(p[i] == 0 &amp;amp;&amp;amp; p[i] &amp;lt; 0)&lt;br/&gt;        {&lt;br/&gt;            flag = true;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;if ( flag )&lt;br/&gt;{&lt;br/&gt;return;&lt;br/&gt;}&lt;br/&gt;    dest.sp.x = vector.sp.x - u1 *(vector.sp.x - vector.ep.x);&lt;br/&gt;    dest.sp.y = vector.sp.y - u1 *(vector.sp.y - vector.ep.y);&lt;br/&gt;    dest.ep.x = vector.sp.x - u2 *(vector.sp.x - vector.ep.x);&lt;br/&gt;    dest.ep.y = vector.sp.y - u2 *(vector.sp.y - vector.ep.y);&lt;br/&gt;vector =dest;&lt;br/&gt;}&lt;br/&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;2.javascript&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;/*****************************************&lt;br/&gt;* Liang - Barsky裁剪算法&lt;br/&gt;*****************************************/&lt;br/&gt;/**&lt;br/&gt;验证是否需要裁剪，计算斜率&lt;br/&gt;@return boolean 是否裁减&lt;br/&gt;*/&lt;br/&gt;function ClipTest(p, q) {&lt;br/&gt;    var flag = true;&lt;br/&gt;    var r;&lt;br/&gt;    if (p &amp;lt; 0.0) {&lt;br/&gt;        r = q / p;&lt;br/&gt;        if (r &amp;gt; u2) {&lt;br/&gt;            flag = false;&lt;br/&gt;        }&lt;br/&gt;        else if (r &amp;gt; u1) {&lt;br/&gt;            u1 = r;&lt;br/&gt;            flag = true;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    else if (p &amp;gt; 0) {&lt;br/&gt;        r = q / p;&lt;br/&gt;        if (r &amp;lt; u1) {&lt;br/&gt;            flag = false;&lt;br/&gt;        }&lt;br/&gt;        else if (r &amp;lt; u2) {&lt;br/&gt;            u2 = r;&lt;br/&gt;            flag = true;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    else if (q &amp;lt; 0) {&lt;br/&gt;        flag = false;&lt;br/&gt;    }&lt;br/&gt;    return flag;&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;var u1, u2;&lt;br/&gt;/**&lt;br/&gt;根据矩形裁剪直线段&lt;br/&gt;@param int wxl 左上角X坐标&lt;br/&gt;@param int wxr 右下角X坐标&lt;br/&gt;@param int wyt 右上角Y坐标&lt;br/&gt;@param int wyb 右下角Y坐标&lt;br/&gt;@param {X,Y} start 起点&lt;br/&gt;@param {X,Y} stop 终点&lt;br/&gt;@return  Sring 坐标字符串，如'2323,4343,5454,6563' &lt;br/&gt;*/&lt;br/&gt;function ClipLine(wxl, wxr, wyt, wyb, start, stop) { &lt;br/&gt;    var dx, dy;&lt;br/&gt;    u1 = 0;&lt;br/&gt;    u2 = 1;&lt;br/&gt;    dx = stop.X - start.X;&lt;br/&gt;    if (ClipTest(-dx, start.X - wxl))&lt;br/&gt;    {&lt;br/&gt;        if (ClipTest(dx, wxr - start.X))&lt;br/&gt;        {&lt;br/&gt;            dy = stop.Y - start.Y;&lt;br/&gt;            if (ClipTest(-dy, start.Y - wyt))&lt;br/&gt;            {&lt;br/&gt;                if (ClipTest(dy, wyb - start.Y))&lt;br/&gt;                {&lt;br/&gt;                    var arrCoords = [];&lt;br/&gt;                    arrCoords[0] = parseInt(start.X) + parseInt(u1 * dx);&lt;br/&gt;                    arrCoords[1] = parseInt(start.Y) + parseInt(u1 * dy);&lt;br/&gt;                    arrCoords[2] = parseInt(start.X) + parseInt(u2 * dx);&lt;br/&gt;                    arrCoords[3] = parseInt(start.Y) + parseInt(u2 * dy);&lt;br/&gt;                    return arrCoords;&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    return [];&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/**&lt;br/&gt;根据矩形裁剪折线&lt;br/&gt;@param int wxl 左上角X坐标&lt;br/&gt;@param int wxr 右下角X坐标&lt;br/&gt;@param int wyt 右上角Y坐标&lt;br/&gt;@param int wyb 右下角Y坐标&lt;br/&gt;@param String coords 折线的坐标串(用,隔开)&lt;br/&gt;@return Array 矩形内的折线段，如['2323,343,545,656,766,878','122,898,232,4545','778,4545,2321,1434']&lt;br/&gt;*/&lt;br/&gt;function ClipPolyLineByRect(wxl, wxr, wyt, wyb, coords) {&lt;br/&gt;    var arrCoords = coords.split(',');&lt;br/&gt;    var clipLines = [];&lt;br/&gt;    for (var i = 0; i &amp;lt; (arrCoords.length - 2); i = i + 2) {&lt;br/&gt;        var temp = ClipLine(wxl, wxr, wyt, wyb, { X: arrCoords[i], Y: arrCoords[i + 1] }, { X: arrCoords[i + 2], Y: arrCoords[i + 3] });&lt;br/&gt;        if (temp.length &amp;gt; 0) {&lt;br/&gt;            if (clipLines.length &amp;gt; 0) {&lt;br/&gt;                //这里先拿上次最后一个点和现在第一个比较，如果相同，则他们属于同一条线，将坐标追加到后面，否则存入新的线&lt;br/&gt;                var lastCoords = clipLines[clipLines.length - 1];&lt;br/&gt;                var re = new RegExp(temp[0]+','+temp[1]+'$', 'i');&lt;br/&gt;                if (re.test(lastCoords)) {&lt;br/&gt;                    clipLines[clipLines.length - 1] = lastCoords + ',' + temp[2] + ',' + temp[3];&lt;br/&gt;                }&lt;br/&gt;                else {&lt;br/&gt;                    clipLines[clipLines.length] = temp.join(',');&lt;br/&gt;                }&lt;br/&gt;            } else {&lt;br/&gt;                clipLines[clipLines.length] = temp.join(',');&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    return clipLines;&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;/**&lt;br/&gt;根据当前屏幕裁剪折线&lt;br/&gt;@param String coords 折线的坐标串(用,隔开)&lt;br/&gt;@return Array 裁减后的多条线段&lt;br/&gt;*/&lt;br/&gt;function ClipPolyLineByCurrentRegion(coords) {&lt;br/&gt;    var p1, p2;&lt;br/&gt;    p1 = { X: GetEGISPosXFromWin(0), Y: GetEGISPosYFromWin(0) };&lt;br/&gt;    p2 = { X: GetEGISPosXFromWin(MapWidth), Y: GetEGISPosYFromWin(MapHeight) };&lt;br/&gt;    return ClipPolyLineByRect(p1.X, p2.X, p1.Y, p2.Y, coords);    &lt;br/&gt;}&lt;br/&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/jenry/aggbug/2347983.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/jenry/archive/2012/02/12/2347983.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><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 class="magplus" 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;&lt;h1 id="intro"&gt;简介（Intro）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;JavaScript秘密花园&lt;/strong&gt;是一个不断更新的文档，主要关心JavaScript一些古怪用法。 对于如何避免常见的错误，难以发现的问题，以及性能问题和不好的实践给出建议， 初学者可以籍此深入了解JavaScript的语言特性。&lt;/p&gt;&lt;p&gt;JavaScript秘密花园&lt;strong&gt;不是&lt;/strong&gt;用来教你JavaScript。为了更好的理解这篇文章的内容， 你需要事先学习JavaScript的基础知识。在Mozilla开发者网络中有一系列非常棒的JavaScript学习&lt;a href="https://developer.mozilla.org/en/JavaScript/Guide"&gt;向导&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;关于作者（The authors）&lt;/strong&gt;&lt;/p&gt;&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;&lt;p&gt;&lt;strong&gt;贡献者（Contributors）&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/caio"&gt;Caio Rom?o&lt;/a&gt; (拼写检查)&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/blixt"&gt;Andreas Blixt&lt;/a&gt; (语言修正)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;许可（License）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;JavaScript花园在&lt;a href="https://github.com/BonsaiDen/JavaScript-Garden/blob/next/LICENSE"&gt;MIT license&lt;/a&gt;许可协议下发布，并存放在开源社区&lt;a href="https://github.com/BonsaiDen/JavaScript-Garden"&gt;GitHub&lt;/a&gt;。 如果你发现错误或者打字错误，请&lt;a href="https://github.com/BonsaiDen/JavaScript-Garden/issues"&gt;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;&lt;p&gt;&lt;strong&gt;中文翻译（Chinese Translation）&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://bonsaiden.github.com/JavaScript-Garden/"&gt;JavaScript Garden - 原文&lt;/a&gt;&lt;/li&gt;&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;&lt;li&gt;&lt;a href="http://sanshi.me/"&gt;译作者：三生石上&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;本中文翻译由&lt;a href="http://sanshi.me/"&gt;三上石上&lt;/a&gt;原创，&lt;a href="http://cnblogs.com/sanshi/"&gt;博客园&lt;/a&gt;首发，转载请注明出处。&lt;/p&gt;&lt;h2 id="objects"&gt;对象（Objects） &lt;a href="#intro"&gt;#top&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&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;&lt;div&gt;&lt;div id="highlighter_36298" class="syntaxhighlighter  js ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="js plain"&gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;false&lt;/code&gt;&lt;code class="js plain"&gt;.toString() &lt;/code&gt;&lt;code class="js comments"&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;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;一个常见的误解是数字的字面值（literal）不是对象。这是因为JavaScript解析器的一个错误， 它试图将&lt;em&gt;点操作符&lt;/em&gt;解析为浮点数字面值的一部分。&lt;/p&gt;&lt;div&gt;&lt;div id="highlighter_798355" class="syntaxhighlighter  js ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="js plain"&gt;&amp;lt;code&amp;gt;2.toString(); &lt;/code&gt;&lt;code class="js comments"&gt;// 出错：SyntaxError&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;有很多变通方法可以让数字的字面值看起来像对象。&lt;/p&gt;&lt;div&gt;&lt;div id="highlighter_545266" class="syntaxhighlighter  js ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="js plain"&gt;&amp;lt;code&amp;gt;2..toString(); &lt;/code&gt;&lt;code class="js comments"&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;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;对象作为数据类型（Objects as a data type）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;JavaScript的对象可以作为&lt;a href="http://en.wikipedia.org/wiki/Hashmap"&gt;&lt;em&gt;哈希表&lt;/em&gt;&lt;/a&gt;使用，主要用来保存命名的键与值的对应关系。&lt;/p&gt;&lt;p&gt;使用对象的字面语法 - &lt;code&gt;{}&lt;/code&gt; - 可以创建一个简单对象。这个新创建的对象从&lt;code&gt;Object.prototype&lt;/code&gt;&lt;a href="#prototype"&gt;继承&lt;/a&gt;下面，没有任何&lt;a href="#hasownproperty"&gt;自定义属性&lt;/a&gt;。&lt;/p&gt;&lt;div&gt;&lt;div id="highlighter_690210" class="syntaxhighlighter  js ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="js plain"&gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;var&lt;/code&gt; &lt;code class="js plain"&gt;foo = {}; &lt;/code&gt;&lt;code class="js comments"&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;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;访问属性（Accessing properties）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;有两种方式来访问对象的属性，点操作符或者中括号操作符。&lt;/p&gt;&lt;div&gt;&lt;div id="highlighter_334986" class="syntaxhighlighter  js ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="js plain"&gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;var&lt;/code&gt; &lt;code class="js plain"&gt;foo = {name: &lt;/code&gt;&lt;code class="js string"&gt;'Kitten'&lt;/code&gt;&lt;code class="js plain"&gt;}&amp;lt;br&amp;gt;foo.name; &lt;/code&gt;&lt;code class="js comments"&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;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;两种语法是等价的，但是中括号操作符在下面两种情况下依然有效 - 动态设置属性 - 属性名不是一个有效的变量名（译者注：比如属性名中包含空格，或者属性名是JS的关键词） （译者注：在&lt;a href="http://www.jslint.com/"&gt;JSLint&lt;/a&gt;语法检测工具中，点操作符是推荐做法）&lt;/p&gt;&lt;p&gt;&lt;strong&gt;删除属性（Deleting properties）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;删除属性的唯一方法是使用&lt;code&gt;delete&lt;/code&gt;操作符；设置属性为&lt;code&gt;undefined&lt;/code&gt;或者&lt;code&gt;null&lt;/code&gt;并不能真正的删除属性， 而&lt;strong&gt;仅仅&lt;/strong&gt;是移除了属性和值的关联。&lt;/p&gt;&lt;div&gt;&lt;div id="highlighter_144515" class="syntaxhighlighter  js ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="js plain"&gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;var&lt;/code&gt; &lt;code class="js plain"&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 class="js keyword"&gt;null&lt;/code&gt;&lt;code class="js plain"&gt;;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;delete&lt;/code&gt; &lt;code class="js plain"&gt;obj.baz;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;for&lt;/code&gt;&lt;code class="js plain"&gt;(&lt;/code&gt;&lt;code class="js keyword"&gt;var&lt;/code&gt; &lt;code class="js plain"&gt;i &lt;/code&gt;&lt;code class="js keyword"&gt;in&lt;/code&gt; &lt;code class="js plain"&gt;obj) {&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code class="js keyword"&gt;if&lt;/code&gt; &lt;code class="js plain"&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 class="js string"&gt;''&lt;/code&gt; &lt;code class="js plain"&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;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;上面的输出结果有&lt;code&gt;bar undefined&lt;/code&gt;和&lt;code&gt;foo null&lt;/code&gt; - 只有&lt;code&gt;baz&lt;/code&gt;被真正的删除了，所以从输出结果中消失。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;属性名的语法（Notation of keys）&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;div id="highlighter_480959" class="syntaxhighlighter  js ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="js plain"&gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;var&lt;/code&gt; &lt;code class="js plain"&gt;test = {&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code class="js string"&gt;'case'&lt;/code&gt;&lt;code class="js plain"&gt;: &lt;/code&gt;&lt;code class="js string"&gt;'I am a keyword so I must be notated as a string'&lt;/code&gt;&lt;code class="js plain"&gt;,&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code class="js keyword"&gt;delete&lt;/code&gt;&lt;code class="js plain"&gt;: &lt;/code&gt;&lt;code class="js string"&gt;'I am a keyword too so me'&lt;/code&gt; &lt;code class="js comments"&gt;// 出错：SyntaxError&amp;lt;br&amp;gt;};&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;对象的属性名可以使用字符串或者普通字符声明。但是由于JavaScript解析器的另一个错误设计， 上面的第二种声明方式在ECMAScript 5之前会抛出&lt;code&gt;SyntaxError&lt;/code&gt;的错误。&lt;/p&gt;&lt;p&gt;这个错误的原因是&lt;code&gt;delete&lt;/code&gt;是JavaScript语言的一个&lt;em&gt;关键词&lt;/em&gt;；因此为了在更低版本的JavaScript引擎下也能正常运行， 必须使用&lt;em&gt;字符串字面值&lt;/em&gt;声明方式。&lt;/p&gt;&lt;h2 id="prototype"&gt;原型（The prototype） &lt;a href="#intro"&gt;#top&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;JavaScript不包含传统的类继承模型，而是使用&lt;em&gt;prototypical&lt;/em&gt;原型模型。&lt;/p&gt;&lt;p&gt;虽然这经常被当作是JavaScript的缺点被提及，其实基于原型的继承模型比传统的类继承还要强大。 实现传统的类继承模型是很简单，但是实现JavaScript中的原型继承则要困难的多。 （It is for example fairly trivial to build a classic model on top of it, while the other way around is a far more difficult task.）&lt;/p&gt;&lt;p&gt;由于JavaScript是唯一一个被广泛使用的基于原型继承的语言，所以理解两种继承模式的差异是需要一定时间的。&lt;/p&gt;&lt;p&gt;第一个不同之处在于JavaScript使用&lt;em&gt;原型链&lt;/em&gt;的继承方式。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; 简单的使用&lt;code&gt;Bar.prototype = Foo.prototype&lt;/code&gt;将会导致两个对象共享&lt;strong&gt;相同&lt;/strong&gt;的原型。 因此，改变任意一个对象的原型都会影响到另一个对象的原型，在大多数情况下这不是希望的结果。&lt;/p&gt;&lt;div&gt;&lt;div id="highlighter_984413" class="syntaxhighlighter  js ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="js plain"&gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;function&lt;/code&gt; &lt;code class="js plain"&gt;Foo() {&amp;lt;br&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/code&gt;&lt;code class="js keyword"&gt;this&lt;/code&gt;&lt;code class="js plain"&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 class="js keyword"&gt;function&lt;/code&gt;&lt;code class="js plain"&gt;() {}&amp;lt;br&amp;gt;};&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;function&lt;/code&gt; &lt;code class="js plain"&gt;Bar() {}&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;code class="js comments"&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;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;上面的例子中，&lt;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;&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;&lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt; &lt;strong&gt;不要&lt;/strong&gt;使用&lt;code&gt;Bar.prototype = Foo&lt;/code&gt;，因为这不会执行&lt;code&gt;Foo&lt;/code&gt;的原型，而是指向函数&lt;code&gt;Foo&lt;/code&gt;。 因此原型链将会回溯到&lt;code&gt;Function.prototype&lt;/code&gt;而不是&lt;code&gt;Foo.prototype&lt;/code&gt;，因此&lt;code&gt;method&lt;/code&gt;将不会在Bar的原型链上。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;属性查找（Property lookup）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;当查找一个对象的属性时，JavaScript会&lt;strong&gt;向上&lt;/strong&gt;遍历原型链，直到找到给定名称的属性为止。&lt;/p&gt;&lt;p&gt;到查找到达原型链的顶部 - 也就是&lt;code&gt;Object.prototype&lt;/code&gt; - 但是仍然没有找到指定的属性，就会返回&lt;a href="#undefined"&gt;undefined&lt;/a&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;原型属性（The prototype property）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;当原型属性用来创建原型链时，可以把&lt;strong&gt;任何&lt;/strong&gt;类型的值赋给它（prototype）。 然而将原子类型赋给prototype的操作将会被忽略。&lt;/p&gt;&lt;div&gt;&lt;div id="highlighter_915035" class="syntaxhighlighter  js ie"&gt;&lt;div class="toolbar"&gt;&lt;span&gt;&lt;a class="toolbar_item command_help help" href="#"&gt;?&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;table border="0" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;div class="line number1 index0 alt2"&gt;1&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="container"&gt;&lt;div class="line number1 index0 alt2"&gt;&lt;code class="js plain"&gt;&amp;lt;code&amp;gt;&lt;/code&gt;&lt;code class="js keyword"&gt;function&lt;/code&gt; &lt;code class="js plain"&gt;Foo() {}&amp;lt;br&amp;gt;Foo.prototype = 1; &lt;/code&gt;&lt;code class="js comments"&gt;// no effect&amp;lt;br&amp;gt;&amp;lt;/code&amp;gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;而将对象赋值给prototype，正如上面的例子所示，将会动态的创建原型链。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;性能（Performance）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;如果一个属性在原型链的上端，则对于查找时间将带来不利影响。特别的，试图获取一个不存在的属性将会遍历整个原型链。&lt;/p&gt;&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;&lt;p&gt;&lt;strong&gt;扩展内置类型的原型（Extension of native prototypes）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;一个错误特性被经常使用，那就是扩展&lt;code&gt;Object.prototype&lt;/code&gt;或者其他内置类型的原型对象。&lt;/p&gt;&lt;p&gt;这种技术被称之为&lt;a href="http://en.wikipedia.org/wiki/Monkey_patch"&gt;monkey patching&lt;/a&gt;并且会破坏&lt;em&gt;封装&lt;/em&gt;。虽然它被广泛的应用到一些JS类库中比如&lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt;,但是我仍然不认为为内置类型添加一些&lt;em&gt;非标准&lt;/em&gt;的函数是个好主意。&lt;/p&gt;&lt;p&gt;扩展内置类型的&lt;strong&gt;唯一&lt;/strong&gt;理由是为了和新的JavaScript保持一致，比如&lt;a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach"&gt;&lt;code&gt;Array.forEach&lt;/code&gt;&lt;/a&gt;。 （译者注：这是编程领域常用的一种方式，称之为&lt;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;&lt;p&gt;&lt;strong&gt;总结（In conclusion）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在写复杂的JavaScript应用之前，充分理解原型链继承的工作方式是每个JavaScript程序员&lt;strong&gt;必修&lt;/strong&gt;的功课。 要提防原型链过长带来的性能问题，并知道如何通过缩短原型链来提高性能。 更进一步，绝对&lt;strong&gt;不要&lt;/strong&gt;扩展内置类型的原型，除非是为了和新的JavaScript引擎兼容。&lt;/p&gt;&lt;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;&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;&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;&lt;/p&gt;        var Class = {&lt;br/&gt;            // Class.create仅仅返回另外一个函数，此函数执行时将调用原型方法initialize&lt;br/&gt;            create: function() {&lt;br/&gt;                return function() {&lt;br/&gt;                    this.initialize.apply(this, arguments);&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;        };&lt;br/&gt;        &lt;br/&gt;        // 对象的扩展&lt;br/&gt;        Object.extend = function(destination, source) {&lt;br/&gt;            for (var property in source) {&lt;br/&gt;                destination[property] = source[property];&lt;br/&gt;            }&lt;br/&gt;            return destination;&lt;br/&gt;        };&lt;br/&gt;        &lt;p&gt;调用方式：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;        var Person = Class.create();&lt;br/&gt;        Person.prototype = {&lt;br/&gt;            initialize: function(name) {&lt;br/&gt;                this.name = name;&lt;br/&gt;            },&lt;br/&gt;            getName: function(prefix) {&lt;br/&gt;                return prefix + this.name;&lt;br/&gt;            }&lt;br/&gt;        };&lt;br/&gt;&lt;br/&gt;        var Employee = Class.create();&lt;br/&gt;        Employee.prototype = Object.extend(new Person(), {&lt;br/&gt;            initialize: function(name, employeeID) {&lt;br/&gt;                this.name = name;&lt;br/&gt;                this.employeeID = employeeID;&lt;br/&gt;            },&lt;br/&gt;            getName: function() {&lt;br/&gt;                return "Employee name: " + this.name;&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;        var zhang = new Employee("ZhangSan", "1234");&lt;br/&gt;        console.log(zhang.getName());   // "Employee name: ZhangSan"&lt;br/&gt;        &lt;p&gt;很原始的感觉对吧，在子类函数中没有提供调用父类函数的途径。&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Prototypejs 1.6以后的继承实现&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&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;&lt;/p&gt;        // 通过Class.create创建一个新类&lt;br/&gt;        var Person = Class.create({&lt;br/&gt;            // initialize是构造函数&lt;br/&gt;            initialize: function(name) {&lt;br/&gt;                this.name = name;&lt;br/&gt;            },&lt;br/&gt;            getName: function(prefix) {&lt;br/&gt;                return prefix + this.name;&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;        &lt;br/&gt;        // Class.create的第一个参数是要继承的父类&lt;br/&gt;        var Employee = Class.create(Person, {&lt;br/&gt;            // 通过将子类函数的第一个参数设为$super来引用父类的同名函数&lt;br/&gt;            // 比较有创意，不过内部实现应该比较复杂，至少要用一个闭包来设置$super的上下文this指向当前对象&lt;br/&gt;            initialize: function($super, name, employeeID) {&lt;br/&gt;                $super(name);&lt;br/&gt;                this.employeeID = employeeID;&lt;br/&gt;            },&lt;br/&gt;            getName: function($super) {&lt;br/&gt;                return $super("Employee name: ");&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;        var zhang = new Employee("ZhangSan", "1234");&lt;br/&gt;        console.log(zhang.getName());   // "Employee name: ZhangSan"&lt;br/&gt;        &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;        var Prototype = {&lt;br/&gt;            emptyFunction: function() { }&lt;br/&gt;        };&lt;br/&gt;&lt;br/&gt;        var Class = {&lt;br/&gt;            create: function() {&lt;br/&gt;                var parent = null, properties = $A(arguments);&lt;br/&gt;                if (Object.isFunction(properties[0]))&lt;br/&gt;                    parent = properties.shift();&lt;br/&gt;&lt;br/&gt;                function klass() {&lt;br/&gt;                    this.initialize.apply(this, arguments);&lt;br/&gt;                }&lt;br/&gt;&lt;br/&gt;                Object.extend(klass, Class.Methods);&lt;br/&gt;                klass.superclass = parent;&lt;br/&gt;                klass.subclasses = [];&lt;br/&gt;&lt;br/&gt;                if (parent) {&lt;br/&gt;                    var subclass = function() { };&lt;br/&gt;                    subclass.prototype = parent.prototype;&lt;br/&gt;                    klass.prototype = new subclass;&lt;br/&gt;                    parent.subclasses.push(klass);&lt;br/&gt;                }&lt;br/&gt;&lt;br/&gt;                for (var i = 0; i &amp;lt; properties.length; i++)&lt;br/&gt;                    klass.addMethods(properties[i]);&lt;br/&gt;&lt;br/&gt;                if (!klass.prototype.initialize)&lt;br/&gt;                    klass.prototype.initialize = Prototype.emptyFunction;&lt;br/&gt;&lt;br/&gt;                klass.prototype.constructor = klass;&lt;br/&gt;&lt;br/&gt;                return klass;&lt;br/&gt;            }&lt;br/&gt;        };&lt;br/&gt;&lt;br/&gt;        Class.Methods = {&lt;br/&gt;            addMethods: function(source) {&lt;br/&gt;                var ancestor = this.superclass &amp;amp;&amp;amp; this.superclass.prototype;&lt;br/&gt;                var properties = Object.keys(source);&lt;br/&gt;&lt;br/&gt;                if (!Object.keys({ toString: true }).length)&lt;br/&gt;                    properties.push("toString", "valueOf");&lt;br/&gt;&lt;br/&gt;                for (var i = 0, length = properties.length; i &amp;lt; length; i++) {&lt;br/&gt;                    var property = properties[i], value = source[property];&lt;br/&gt;                    if (ancestor &amp;amp;&amp;amp; Object.isFunction(value) &amp;amp;&amp;amp; value.argumentNames().first() == "$super") {&lt;br/&gt;                        var method = value;&lt;br/&gt;                        value = (function(m) {&lt;br/&gt;                            return function() { return ancestor[m].apply(this, arguments) };&lt;br/&gt;                        })(property).wrap(method);&lt;br/&gt;&lt;br/&gt;                        value.valueOf = method.valueOf.bind(method);&lt;br/&gt;                        value.toString = method.toString.bind(method);&lt;br/&gt;                    }&lt;br/&gt;                    this.prototype[property] = value;&lt;br/&gt;                }&lt;br/&gt;&lt;br/&gt;                return this;&lt;br/&gt;            }&lt;br/&gt;        };&lt;br/&gt;&lt;br/&gt;        Object.extend = function(destination, source) {&lt;br/&gt;            for (var property in source)&lt;br/&gt;                destination[property] = source[property];&lt;br/&gt;            return destination;&lt;br/&gt;        };&lt;br/&gt;&lt;br/&gt;        function $A(iterable) {&lt;br/&gt;            if (!iterable) return [];&lt;br/&gt;            if (iterable.toArray) return iterable.toArray();&lt;br/&gt;            var length = iterable.length || 0, results = new Array(length);&lt;br/&gt;            while (length--) results[length] = iterable[length];&lt;br/&gt;            return results;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        Object.extend(Object, {&lt;br/&gt;            keys: function(object) {&lt;br/&gt;                var keys = [];&lt;br/&gt;                for (var property in object)&lt;br/&gt;                    keys.push(property);&lt;br/&gt;                return keys;&lt;br/&gt;            },&lt;br/&gt;            isFunction: function(object) {&lt;br/&gt;                return typeof object == "function";&lt;br/&gt;            },&lt;br/&gt;            isUndefined: function(object) {&lt;br/&gt;                return typeof object == "undefined";&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;&lt;br/&gt;        Object.extend(Function.prototype, {&lt;br/&gt;            argumentNames: function() {&lt;br/&gt;                var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(',');&lt;br/&gt;                return names.length == 1 &amp;amp;&amp;amp; !names[0] ? [] : names;&lt;br/&gt;            },&lt;br/&gt;            bind: function() {&lt;br/&gt;                if (arguments.length &amp;lt; 2 &amp;amp;&amp;amp; Object.isUndefined(arguments[0])) return this;&lt;br/&gt;                var __method = this, args = $A(arguments), object = args.shift();&lt;br/&gt;                return function() {&lt;br/&gt;                    return __method.apply(object, args.concat($A(arguments)));&lt;br/&gt;                }&lt;br/&gt;            },&lt;br/&gt;            wrap: function(wrapper) {&lt;br/&gt;                var __method = this;&lt;br/&gt;                return function() {&lt;br/&gt;                    return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;&lt;br/&gt;        Object.extend(Array.prototype, {&lt;br/&gt;            first: function() {&lt;br/&gt;                return this[0];&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;首先，我们需要先解释下Prototypejs中一些方法的定义。&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;ul&gt;&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;                function init($super, name, employeeID) {&lt;br/&gt;                }&lt;br/&gt;                console.log(init.argumentNames().join(",")); // "$super,name,employeeID"&lt;br/&gt;                &lt;/li&gt;&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;                var name = "window";&lt;br/&gt;                var p = {&lt;br/&gt;                    name: "Lisi",&lt;br/&gt;                    getName: function() {&lt;br/&gt;                        return this.name;&lt;br/&gt;                    }&lt;br/&gt;                };&lt;br/&gt;&lt;br/&gt;                console.log(p.getName());   // "Lisi"&lt;br/&gt;                console.log(p.getName.bind(window)());  // "window"&lt;br/&gt;                &lt;/li&gt;&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;                var name = "window";&lt;br/&gt;                var p = {&lt;br/&gt;                    name: "Lisi",&lt;br/&gt;                    getName: function() {&lt;br/&gt;                        return this.name;&lt;br/&gt;                    }&lt;br/&gt;                };&lt;br/&gt;&lt;br/&gt;                function wrapper(originalFn) {&lt;br/&gt;                    return "Hello: " + originalFn();&lt;br/&gt;                }&lt;br/&gt;&lt;br/&gt;                console.log(p.getName());   // "Lisi"&lt;br/&gt;                console.log(p.getName.bind(window)());  // "window"&lt;br/&gt;                console.log(p.getName.wrap(wrapper)()); // "Hello: window"&lt;br/&gt;                console.log(p.getName.wrap(wrapper).bind(p)()); // "Hello: Lisi"&lt;br/&gt;                有一点绕口，对吧。这里要注意的是wrap和bind调用返回的都是函数，把握住这个原则，就很容易看清本质了。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&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;        var Class = {&lt;br/&gt;            create: function() {&lt;br/&gt;                // 如果第一个参数是函数，则作为父类&lt;br/&gt;                var parent = null, properties = $A(arguments);&lt;br/&gt;                if (Object.isFunction(properties[0]))&lt;br/&gt;                    parent = properties.shift();&lt;br/&gt;&lt;br/&gt;                // 子类构造函数的定义&lt;br/&gt;                function klass() {&lt;br/&gt;                    this.initialize.apply(this, arguments);&lt;br/&gt;                }&lt;br/&gt;                &lt;br/&gt;                // 为子类添加原型方法Class.Methods.addMethods&lt;br/&gt;                Object.extend(klass, Class.Methods);&lt;br/&gt;                // 不仅为当前类保存父类的引用，同时记录了所有子类的引用&lt;br/&gt;                klass.superclass = parent;&lt;br/&gt;                klass.subclasses = [];&lt;br/&gt;&lt;br/&gt;                if (parent) {&lt;br/&gt;                    // 核心代码 - 如果父类存在，则实现原型的继承&lt;br/&gt;                    // 这里为创建类时不调用父类的构造函数提供了一种新的途径&lt;br/&gt;                    // - 使用一个中间过渡类，这和我们以前使用全局initializing变量达到相同的目的，&lt;br/&gt;                    // - 但是代码更优雅一点。&lt;br/&gt;                    var subclass = function() { };&lt;br/&gt;                    subclass.prototype = parent.prototype;&lt;br/&gt;                    klass.prototype = new subclass;&lt;br/&gt;                    parent.subclasses.push(klass);&lt;br/&gt;                }&lt;br/&gt;&lt;br/&gt;                // 核心代码 - 如果子类拥有父类相同的方法，则特殊处理，将会在后面详解&lt;br/&gt;                for (var i = 0; i &amp;lt; properties.length; i++)&lt;br/&gt;                    klass.addMethods(properties[i]);&lt;br/&gt;&lt;br/&gt;                if (!klass.prototype.initialize)&lt;br/&gt;                    klass.prototype.initialize = Prototype.emptyFunction;&lt;br/&gt;                &lt;br/&gt;                // 修正constructor指向错误&lt;br/&gt;                klass.prototype.constructor = klass;&lt;br/&gt;&lt;br/&gt;                return klass;&lt;br/&gt;            }&lt;br/&gt;        };&lt;br/&gt;        &lt;p&gt;再来看addMethods做了哪些事情：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;        Class.Methods = {&lt;br/&gt;            addMethods: function(source) {&lt;br/&gt;                // 如果父类存在，ancestor指向父类的原型对象&lt;br/&gt;                var ancestor = this.superclass &amp;amp;&amp;amp; this.superclass.prototype;&lt;br/&gt;                var properties = Object.keys(source);&lt;br/&gt;                // Firefox和Chrome返回1，IE8返回0，所以这个地方特殊处理&lt;br/&gt;                if (!Object.keys({ toString: true }).length)&lt;br/&gt;                    properties.push("toString", "valueOf");&lt;br/&gt;&lt;br/&gt;                // 循环子类原型定义的所有属性，对于那些和父类重名的函数要重新定义&lt;br/&gt;                for (var i = 0, length = properties.length; i &amp;lt; length; i++) {&lt;br/&gt;                    // property为属性名，value为属性体（可能是函数，也可能是对象）&lt;br/&gt;                    var property = properties[i], value = source[property];&lt;br/&gt;                    // 如果父类存在，并且当前当前属性是函数，并且此函数的第一个参数为 $super&lt;br/&gt;                    if (ancestor &amp;amp;&amp;amp; Object.isFunction(value) &amp;amp;&amp;amp; value.argumentNames().first() == "$super") {&lt;br/&gt;                        var method = value;&lt;br/&gt;                        // 下面三行代码是精华之所在，大概的意思：&lt;br/&gt;                        // - 首先创建一个自执行的匿名函数返回另一个函数，此函数用于执行父类的同名函数&lt;br/&gt;                        // - （因为这是在循环中，我们曾多次指出循环中的函数引用局部变量的问题）&lt;br/&gt;                        // - 其次把这个自执行的匿名函数的作为method的第一个参数（也就是对应于形参$super）&lt;br/&gt;                        // 不过，窃以为这个地方作者有点走火入魔，完全没必要这么复杂，后面我会详细分析这段代码。&lt;br/&gt;                        value = (function(m) {&lt;br/&gt;                            return function() { return ancestor[m].apply(this, arguments) };&lt;br/&gt;                        })(property).wrap(method);&lt;br/&gt;&lt;br/&gt;                        value.valueOf = method.valueOf.bind(method);&lt;br/&gt;                        // 因为我们改变了函数体，所以重新定义函数的toString方法&lt;br/&gt;                        // 这样用户调用函数的toString方法时，返回的是原始的函数定义体&lt;br/&gt;                        value.toString = method.toString.bind(method);&lt;br/&gt;                    }&lt;br/&gt;                    this.prototype[property] = value;&lt;br/&gt;                }&lt;br/&gt;&lt;br/&gt;                return this;&lt;br/&gt;            }&lt;br/&gt;        };&lt;br/&gt;        &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;        value = (function(m) {&lt;br/&gt;            return function() { return ancestor[m].apply(this, arguments) };&lt;br/&gt;        })(property).wrap(method);&lt;br/&gt;        &lt;p&gt;其实这段代码和下面的效果一样：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;        value = ancestor[property].wrap(method);&lt;br/&gt;        &lt;p&gt;我们把wrap函数展开就能看的更清楚了：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;        value = (function(fn, wrapper) {&lt;br/&gt;            var __method = fn;&lt;br/&gt;            return function() {&lt;br/&gt;                return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));&lt;br/&gt;            }&lt;br/&gt;        })(ancestor[property], method);&lt;br/&gt;        &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;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;我们对Prototypejs继承的重实现&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&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; &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;        // 注意：这是我们自己实现的类似Prototypejs继承方式的代码，可以直接拷贝下来使用&lt;br/&gt;        &lt;br/&gt;        // 这个方法是借用Prototypejs中的定义&lt;br/&gt;        function argumentNames(fn) {&lt;br/&gt;            var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(',');&lt;br/&gt;            return names.length == 1 &amp;amp;&amp;amp; !names[0] ? [] : names;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;        function jClass(baseClass, prop) {&lt;br/&gt;            // 只接受一个参数的情况 - jClass(prop)&lt;br/&gt;            if (typeof (baseClass) === "object") {&lt;br/&gt;                prop = baseClass;&lt;br/&gt;                baseClass = null;&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            // 本次调用所创建的类（构造函数）&lt;br/&gt;            function F() {&lt;br/&gt;                // 如果父类存在，则实例对象的baseprototype指向父类的原型&lt;br/&gt;                // 这就提供了在实例对象中调用父类方法的途径&lt;br/&gt;                if (baseClass) {&lt;br/&gt;                    this.baseprototype = baseClass.prototype;&lt;br/&gt;                }&lt;br/&gt;                this.initialize.apply(this, arguments);&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            // 如果此类需要从其它类扩展&lt;br/&gt;            if (baseClass) {&lt;br/&gt;                var middleClass = function() {};&lt;br/&gt;                middleClass.prototype = baseClass.prototype;&lt;br/&gt;                F.prototype = new middleClass();&lt;br/&gt;                F.prototype.constructor = F;&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            // 覆盖父类的同名函数&lt;br/&gt;            for (var name in prop) {&lt;br/&gt;                if (prop.hasOwnProperty(name)) {&lt;br/&gt;                    // 如果此类继承自父类baseClass并且父类原型中存在同名函数name&lt;br/&gt;                    if (baseClass &amp;amp;&amp;amp;&lt;br/&gt;                        typeof (prop[name]) === "function" &amp;amp;&amp;amp;&lt;br/&gt;                        argumentNames(prop[name])[0] === "$super") {&lt;br/&gt;                        // 重定义子类的原型方法prop[name]&lt;br/&gt;                        // - 这里面有很多JavaScript方面的技巧，如果阅读有困难的话，可以参阅我前面关于JavaScript Tips and Tricks的系列文章&lt;br/&gt;                        // - 比如$super封装了父类方法的调用，但是调用时的上下文指针要指向当前子类的实例对象&lt;br/&gt;                        // - 将$super作为方法调用的第一个参数&lt;br/&gt;                        F.prototype[name] = (function(name, fn) {&lt;br/&gt;                            return function() {&lt;br/&gt;                                var that = this;&lt;br/&gt;                                $super = function() {&lt;br/&gt;                                    return baseClass.prototype[name].apply(that, arguments);&lt;br/&gt;                                };&lt;br/&gt;                                return fn.apply(this, Array.prototype.concat.apply($super, arguments));&lt;br/&gt;                            };&lt;br/&gt;                        })(name, prop[name]);&lt;br/&gt;                        &lt;br/&gt;                    } else {&lt;br/&gt;                        F.prototype[name] = prop[name];&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            return F;&lt;br/&gt;        };&lt;br/&gt;        &lt;p&gt;调用方式和Prototypejs的调用方式保持一致：&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;        var Person = jClass({&lt;br/&gt;            initialize: function(name) {&lt;br/&gt;                this.name = name;&lt;br/&gt;            },&lt;br/&gt;            getName: function() {&lt;br/&gt;                return this.name;&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;&lt;br/&gt;        var Employee = jClass(Person, {&lt;br/&gt;            initialize: function($super, name, employeeID) {&lt;br/&gt;                $super(name);&lt;br/&gt;                this.employeeID = employeeID;&lt;br/&gt;            },&lt;br/&gt;            getEmployeeID: function() {&lt;br/&gt;                return this.employeeID;&lt;br/&gt;            },&lt;br/&gt;            getName: function($super) {&lt;br/&gt;                return "Employee name: " + $super();&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;        var zhang = new Employee("ZhangSan", "1234");&lt;br/&gt;        console.log(zhang.getName());   // "Employee name: ZhangSan"&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&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;&amp;nbsp;&lt;/p&gt;&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;&lt;p&gt;&lt;strong&gt;调用方式&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;首先让我们看下使用Crockford式继承的调用方式： 注意：代码中的method、inherits、uber都是自定义的对象，我们会在后面的代码分析中详解。&lt;/p&gt;        // 定义Person类&lt;br/&gt;        function Person(name) {&lt;br/&gt;            this.name = name;&lt;br/&gt;        }&lt;br/&gt;        // 定义Person的原型方法&lt;br/&gt;        Person.method("getName", function() {&lt;br/&gt;            return this.name;&lt;br/&gt;        });  &lt;br/&gt;        &lt;br/&gt;        // 定义Employee类&lt;br/&gt;        function Employee(name, employeeID) {&lt;br/&gt;            this.name = name;&lt;br/&gt;            this.employeeID = employeeID;&lt;br/&gt;        }&lt;br/&gt;        // 指定Employee类从Person类继承&lt;br/&gt;        Employee.inherits(Person);&lt;br/&gt;        // 定义Employee的原型方法&lt;br/&gt;        Employee.method("getEmployeeID", function() {&lt;br/&gt;            return this.employeeID;&lt;br/&gt;        });&lt;br/&gt;        Employee.method("getName", function() {&lt;br/&gt;            // 注意，可以在子类中调用父类的原型方法&lt;br/&gt;            return "Employee name: " + this.uber("getName");&lt;br/&gt;        });&lt;br/&gt;        // 实例化子类&lt;br/&gt;        var zhang = new Employee("ZhangSan", "1234");&lt;br/&gt;        console.log(zhang.getName());   // "Employee name: ZhangSan"&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;这里面有几处不得不提的硬伤：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;子类从父类继承的代码必须在子类和父类都定义好之后进行，并且必须在子类原型方法定义之前进行。&lt;/li&gt;&lt;li&gt;虽然子类方法体中可以调用父类的方法，但是子类的构造函数无法调用父类的构造函数。&lt;/li&gt;&lt;li&gt;代码的书写不够优雅，比如原型方法的定义以及调用父类的方法（不直观）。&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;当然Crockford的实现还支持子类中的方法调用带参数的父类方法，如下例子：&lt;/p&gt;        function Person(name) {&lt;br/&gt;            this.name = name;&lt;br/&gt;        }&lt;br/&gt;        Person.method("getName", function(prefix) {&lt;br/&gt;            return prefix + this.name;&lt;br/&gt;        });&lt;br/&gt;&lt;br/&gt;        function Employee(name, employeeID) {&lt;br/&gt;            this.name = name;&lt;br/&gt;            this.employeeID = employeeID;&lt;br/&gt;        }&lt;br/&gt;        Employee.inherits(Person);&lt;br/&gt;        Employee.method("getName", function() {&lt;br/&gt;            // 注意，uber的第一个参数是要调用父类的函数名称，后面的参数都是此函数的参数&lt;br/&gt;            // 个人觉得这样方式不如这样调用来的直观：this.uber("Employee name: ")&lt;br/&gt;            return this.uber("getName", "Employee name: ");&lt;br/&gt;        });&lt;br/&gt;        var zhang = new Employee("ZhangSan", "1234");&lt;br/&gt;        console.log(zhang.getName());   // "Employee name: ZhangSan"&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;代码分析&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;首先method函数的定义就很简单了：&lt;/p&gt;        Function.prototype.method = function(name, func) {&lt;br/&gt;            // this指向当前函数，也即是typeof(this) === "function"&lt;br/&gt;            this.prototype[name] = func;&lt;br/&gt;            return this;&lt;br/&gt;        };&lt;br/&gt;        &lt;p&gt;要特别注意这里this的指向。当我们看到this时，不能仅仅关注于当前函数，而应该想到当前函数的调用方式。 比如这个例子中的method我们不会通过new的方式调用，所以method中的this指向的是当前函数。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;inherits函数的定义有点复杂：&lt;/p&gt;        Function.method('inherits', function (parent) {&lt;br/&gt;            // 关键是这一段：this.prototype = new parent()，这里实现了原型的引用&lt;br/&gt;            var d = {}, p = (this.prototype = new parent());&lt;br/&gt;            &lt;br/&gt;            // 只为子类的原型增加uber方法，这里的Closure是为了在调用uber函数时知道当前类的父类的原型（也即是变量 - v）&lt;br/&gt;            this.method('uber', function uber(name) {&lt;br/&gt;                // 这里考虑到如果name是存在于Object.prototype中的函数名的情况&lt;br/&gt;                // 比如 "toString" in {} === true&lt;br/&gt;                if (!(name in d)) {&lt;br/&gt;                    // 通过d[name]计数，不理解具体的含义&lt;br/&gt;                    d[name] = 0;&lt;br/&gt;                }        &lt;br/&gt;                var f, r, t = d[name], v = parent.prototype;&lt;br/&gt;                if (t) {&lt;br/&gt;                    while (t) {&lt;br/&gt;                        v = v.constructor.prototype;&lt;br/&gt;                        t -= 1;&lt;br/&gt;                    }&lt;br/&gt;                    f = v[name];&lt;br/&gt;                } else {&lt;br/&gt;                    // 个人觉得这段代码有点繁琐，既然uber的含义就是父类的函数，那么f直接指向v[name]就可以了&lt;br/&gt;                    f = p[name];&lt;br/&gt;                    if (f == this[name]) {&lt;br/&gt;                        f = v[name];&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;                d[name] += 1;&lt;br/&gt;                // 执行父类中的函数name，但是函数中this指向当前对象&lt;br/&gt;                // 同时注意使用Array.prototype.slice.apply的方式对arguments进行截断（因为arguments不是标准的数组，没有slice方法）&lt;br/&gt;                r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));&lt;br/&gt;                d[name] -= 1;&lt;br/&gt;                return r;&lt;br/&gt;            });&lt;br/&gt;            return this;&lt;br/&gt;        });&lt;br/&gt;        &lt;p&gt;注意，在inherits函数中还有一个小小的BUG，那就是没有重定义constructor的指向，所以会发生如下的错误：&lt;/p&gt;        var zhang = new Employee("ZhangSan", "1234");&lt;br/&gt;        console.log(zhang.getName());   // "Employee name: ZhangSan"&lt;br/&gt;        console.log(zhang.constructor === Employee);    // false&lt;br/&gt;        console.log(zhang.constructor === Person);      // true&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;改进建议&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;根据前面的分析，个人觉得method函数必要性不大，反而容易混淆视线。 而inherits方法可以做一些瘦身（因为Crockford可能考虑更多的情况，原文中介绍了好几种使用inherits的方式，而我们只关注其中的一种）， 并修正了constructor的指向错误。&lt;/p&gt;        Function.prototype.inherits = function(parent) {&lt;br/&gt;            this.prototype = new parent();&lt;br/&gt;            this.prototype.constructor = this;&lt;br/&gt;            this.prototype.uber = function(name) {&lt;br/&gt;                f = parent.prototype[name];&lt;br/&gt;                return f.apply(this, Array.prototype.slice.call(arguments, 1));&lt;br/&gt;            };&lt;br/&gt;        };&lt;br/&gt;        &lt;p&gt;调用方式：&lt;/p&gt;        function Person(name) {&lt;br/&gt;            this.name = name;&lt;br/&gt;        }&lt;br/&gt;        Person.prototype.getName = function(prefix) {&lt;br/&gt;            return prefix + this.name;&lt;br/&gt;        };&lt;br/&gt;        function Employee(name, employeeID) {&lt;br/&gt;            this.name = name;&lt;br/&gt;            this.employeeID = employeeID;&lt;br/&gt;        }&lt;br/&gt;        Employee.inherits(Person);&lt;br/&gt;        Employee.prototype.getName = function() {&lt;br/&gt;            return this.uber("getName", "Employee name: ");&lt;br/&gt;        };&lt;br/&gt;        var zhang = new Employee("ZhangSan", "1234");&lt;br/&gt;        console.log(zhang.getName());   // "Employee name: ZhangSan"&lt;br/&gt;        console.log(zhang.constructor === Employee);   // true&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;有点意思&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在文章的结尾，Crockford居然放出了这样的话：&lt;/p&gt;&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;&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;&amp;nbsp;&lt;/p&gt;&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;&lt;p&gt;&lt;strong&gt;调用方式&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;调用方式非常优雅： 注意：代码中的Class、extend、_super都是自定义的对象，我们会在后面的代码分析中详解。&lt;/p&gt;        var Person = Class.extend({&lt;br/&gt;            // init是构造函数&lt;br/&gt;            init: function(name) {&lt;br/&gt;                this.name = name;&lt;br/&gt;            },&lt;br/&gt;            getName: function() {&lt;br/&gt;                return this.name;&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;        // Employee类从Person类继承&lt;br/&gt;        var Employee = Person.extend({&lt;br/&gt;            // init是构造函数&lt;br/&gt;            init: function(name, employeeID) {&lt;br/&gt;                //  在构造函数中调用父类的构造函数&lt;br/&gt;                this._super(name);&lt;br/&gt;                this.employeeID = employeeID;&lt;br/&gt;            },&lt;br/&gt;            getEmployeeID: function() {&lt;br/&gt;                return this.employeeID;&lt;br/&gt;            },&lt;br/&gt;            getName: function() {&lt;br/&gt;                //  调用父类的方法&lt;br/&gt;                return "Employee name: " + this._super();&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;&lt;br/&gt;        var zhang = new Employee("ZhangSan", "1234");&lt;br/&gt;        console.log(zhang.getName());   // "Employee name: ZhangSan"&lt;br/&gt;        &lt;p&gt;说实话，对于完成本系列文章的目标-继承-而言，真找不到什么缺点。方法一如jQuery一样简洁明了。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;代码分析&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;为了一个漂亮的调用方式，内部实现的确复杂了很多，不过这些也是值得的 - 一个人的思考带给了无数程序员快乐的微笑 - 嘿嘿，有点肉麻。 不过其中的一段代码的确迷惑我一段时间：&lt;/p&gt;        fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; &lt;br/&gt;        &lt;p&gt;我曾在几天前的博客中写过一篇文章专门阐述这个问题，有兴趣可以向前翻一翻。&lt;/p&gt;        // 自执行的匿名函数创建一个上下文，避免引入全局变量&lt;br/&gt;        (function() {&lt;br/&gt;            // initializing变量用来标示当前是否处于类的创建阶段，&lt;br/&gt;            // - 在类的创建阶段是不能调用原型方法init的&lt;br/&gt;            // - 我们曾在本系列的第三篇文章中详细阐述了这个问题&lt;br/&gt;            // fnTest是一个正则表达式，可能的取值为（/\b_super\b/ 或 /.*/）&lt;br/&gt;            // - 对 /xyz/.test(function() { xyz; }) 的测试是为了检测浏览器是否支持test参数为函数的情况&lt;br/&gt;            // - 不过我对IE7.0,Chrome2.0,FF3.5进行了测试，此测试都返回true。&lt;br/&gt;            // - 所以我想这样对fnTest赋值大部分情况下也是对的：fnTest = /\b_super\b/;&lt;br/&gt;            var initializing = false, fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;&lt;br/&gt;            // 基类构造函数&lt;br/&gt;            // 这里的this是window，所以这整段代码就向外界开辟了一扇窗户 - window.Class&lt;br/&gt;            this.Class = function() { };&lt;br/&gt;            // 继承方法定义&lt;br/&gt;            Class.extend = function(prop) {&lt;br/&gt;                // 这个地方很是迷惑人，还记得我在本系列的第二篇文章中提到的么&lt;br/&gt;                // - this具体指向什么不是定义时能决定的，而是要看此函数是怎么被调用的&lt;br/&gt;                // - 我们已经知道extend肯定是作为方法调用的，而不是作为构造函数&lt;br/&gt;                // - 所以这里this指向的不是Object，而是Function（即是Class），那么this.prototype就是父类的原型对象&lt;br/&gt;                // - 注意：_super指向父类的原型对象，我们会在后面的代码中多次碰见这个变量&lt;br/&gt;                var _super = this.prototype;&lt;br/&gt;                // 通过将子类的原型指向父类的一个实例对象来完成继承&lt;br/&gt;                // - 注意：this是基类构造函数（即是Class）&lt;br/&gt;                initializing = true;&lt;br/&gt;                var prototype = new this();&lt;br/&gt;                initializing = false;&lt;br/&gt;                // 我觉得这段代码是经过作者优化过的，所以读起来非常生硬，我会在后面详解&lt;br/&gt;                for (var name in prop) {&lt;br/&gt;                    prototype[name] = typeof prop[name] == "function" &amp;amp;&amp;amp;&lt;br/&gt;                        typeof _super[name] == "function" &amp;amp;&amp;amp; fnTest.test(prop[name]) ?&lt;br/&gt;                        (function(name, fn) {&lt;br/&gt;                            return function() {&lt;br/&gt;                                var tmp = this._super;&lt;br/&gt;                                this._super = _super[name];&lt;br/&gt;                                var ret = fn.apply(this, arguments);&lt;br/&gt;                                this._super = tmp;&lt;br/&gt;                                return ret;&lt;br/&gt;                            };&lt;br/&gt;                        })(name, prop[name]) :&lt;br/&gt;                        prop[name];&lt;br/&gt;                }&lt;br/&gt;                // 这个地方可以看出，Resig很会伪装哦&lt;br/&gt;                // - 使用一个同名的局部变量来覆盖全局变量，很是迷惑人&lt;br/&gt;                // - 如果你觉得拗口的话，完全可以使用另外一个名字，比如function F()来代替function Class()&lt;br/&gt;                // - 注意：这里的Class不是在最外层定义的那个基类构造函数&lt;br/&gt;                function Class() {&lt;br/&gt;                    // 在类的实例化时，调用原型方法init&lt;br/&gt;                    if (!initializing &amp;amp;&amp;amp; this.init)&lt;br/&gt;                        this.init.apply(this, arguments);&lt;br/&gt;                }&lt;br/&gt;                // 子类的prototype指向父类的实例（完成继承的关键）&lt;br/&gt;                Class.prototype = prototype;&lt;br/&gt;                // 修正constructor指向错误&lt;br/&gt;                Class.constructor = Class;&lt;br/&gt;                // 子类自动获取extend方法，arguments.callee指向当前正在执行的函数&lt;br/&gt;                Class.extend = arguments.callee;&lt;br/&gt;                return Class;&lt;br/&gt;            };&lt;br/&gt;        })();&lt;br/&gt;        &lt;p&gt;下面我会对其中的for-in循环进行解读，把自执行的匿名方法用一个局部函数来替换， 这样有利于我们看清真相：&lt;/p&gt;        (function() {&lt;br/&gt;            var initializing = false, fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;&lt;br/&gt;            this.Class = function() { };&lt;br/&gt;            Class.extend = function(prop) {&lt;br/&gt;                var _super = this.prototype;&lt;br/&gt;                initializing = true;&lt;br/&gt;                var prototype = new this();&lt;br/&gt;                initializing = false;&lt;br/&gt;&lt;br/&gt;                // 如果父类和子类有同名方法，并且子类中此方法（name）通过_super调用了父类方法&lt;br/&gt;                // - 则重新定义此方法&lt;br/&gt;                function fn(name, fn) {&lt;br/&gt;                    return function() {&lt;br/&gt;                        // 将实例方法_super保护起来。&lt;br/&gt;                        // 个人觉得这个地方没有必要，因为每次调用这样的函数时都会对this._super重新定义。&lt;br/&gt;                        var tmp = this._super;&lt;br/&gt;                        // 在执行子类的实例方法name时，添加另外一个实例方法_super，此方法指向父类的同名方法&lt;br/&gt;                        this._super = _super[name];&lt;br/&gt;                        // 执行子类的方法name，注意在方法体内this._super可以调用父类的同名方法&lt;br/&gt;                        var ret = fn.apply(this, arguments);&lt;br/&gt;                        this._super = tmp;&lt;br/&gt;                        &lt;br/&gt;                        // 返回执行结果&lt;br/&gt;                        return ret;&lt;br/&gt;                    };&lt;br/&gt;                }&lt;br/&gt;                // 拷贝prop中的所有属性到子类原型中&lt;br/&gt;                for (var name in prop) {&lt;br/&gt;                    // 如果prop和父类中存在同名的函数，并且此函数中使用了_super方法，则对此方法进行特殊处理 - fn&lt;br/&gt;                    // 否则将此方法prop[name]直接赋值给子类的原型&lt;br/&gt;                    if (typeof prop[name] === "function" &amp;amp;&amp;amp;&lt;br/&gt;                            typeof _super[name] === "function" &amp;amp;&amp;amp; fnTest.test(prop[name])) {&lt;br/&gt;                        prototype[name] = fn(name, prop[name]);&lt;br/&gt;                    } else {&lt;br/&gt;                        prototype[name] = prop[name];&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;&lt;br/&gt;                function Class() {&lt;br/&gt;                    if (!initializing &amp;amp;&amp;amp; this.init) {&lt;br/&gt;                        this.init.apply(this, arguments);&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;                Class.prototype = prototype;&lt;br/&gt;                Class.constructor = Class;&lt;br/&gt;                Class.extend = arguments.callee;&lt;br/&gt;                return Class;&lt;br/&gt;            };&lt;br/&gt;        })();&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;写到这里，大家是否觉得Resig的实现和我们在第三章一步一步实现的jClass很类似。 其实在写这一系列的文章之前，我已经对prototype、mootools、extjs、 jQuery-Simple-Inheritance、Crockford-Classical-Inheritance这些实现有一定的了解，并且大部分都在实际项目中使用过。 在第三章中实现jClass也参考了Resig的实现，在此向Resig表示感谢。 下来我们就把jClass改造成和这里的Class具有相同的行为。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;我们的实现&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;将我们在第三章实现的jClass改造成目前John Resig所写的形式相当简单，只需要修改其中的两三行就行了：&lt;/p&gt;        (function() {&lt;br/&gt;            // 当前是否处于创建类的阶段&lt;br/&gt;            var initializing = false;&lt;br/&gt;            jClass = function() { };&lt;br/&gt;            jClass.extend = function(prop) {&lt;br/&gt;                // 如果调用当前函数的对象（这里是函数）不是Class，则是父类&lt;br/&gt;                var baseClass = null;&lt;br/&gt;                if (this !== jClass) {&lt;br/&gt;                    baseClass = this;&lt;br/&gt;                }&lt;br/&gt;                // 本次调用所创建的类（构造函数）&lt;br/&gt;                function F() {&lt;br/&gt;                    // 如果当前处于实例化类的阶段，则调用init原型函数&lt;br/&gt;                    if (!initializing) {&lt;br/&gt;                        // 如果父类存在，则实例对象的baseprototype指向父类的原型&lt;br/&gt;                        // 这就提供了在实例对象中调用父类方法的途径&lt;br/&gt;                        if (baseClass) {&lt;br/&gt;                            this._superprototype = baseClass.prototype;&lt;br/&gt;                        }&lt;br/&gt;                        this.init.apply(this, arguments);&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;                // 如果此类需要从其它类扩展&lt;br/&gt;                if (baseClass) {&lt;br/&gt;                    initializing = true;&lt;br/&gt;                    F.prototype = new baseClass();&lt;br/&gt;                    F.prototype.constructor = F;&lt;br/&gt;                    initializing = false;&lt;br/&gt;                }&lt;br/&gt;                // 新创建的类自动附加extend函数&lt;br/&gt;                F.extend = arguments.callee;&lt;br/&gt;&lt;br/&gt;                // 覆盖父类的同名函数&lt;br/&gt;                for (var name in prop) {&lt;br/&gt;                    if (prop.hasOwnProperty(name)) {&lt;br/&gt;                        // 如果此类继承自父类baseClass并且父类原型中存在同名函数name&lt;br/&gt;                        if (baseClass &amp;amp;&amp;amp;&lt;br/&gt;                        typeof (prop[name]) === "function" &amp;amp;&amp;amp;&lt;br/&gt;                        typeof (F.prototype[name]) === "function" &amp;amp;&amp;amp;&lt;br/&gt;                        /\b_super\b/.test(prop[name])) {&lt;br/&gt;                            // 重定义函数name - &lt;br/&gt;                            // 首先在函数上下文设置this._super指向父类原型中的同名函数&lt;br/&gt;                            // 然后调用函数prop[name]，返回函数结果&lt;br/&gt;                            // 注意：这里的自执行函数创建了一个上下文，这个上下文返回另一个函数，&lt;br/&gt;                            // 此函数中可以应用此上下文中的变量，这就是闭包（Closure）。&lt;br/&gt;                            // 这是JavaScript框架开发中常用的技巧。&lt;br/&gt;                            F.prototype[name] = (function(name, fn) {&lt;br/&gt;                                return function() {&lt;br/&gt;                                    this._super = baseClass.prototype[name];&lt;br/&gt;                                    return fn.apply(this, arguments);&lt;br/&gt;                                };&lt;br/&gt;                            })(name, prop[name]);&lt;br/&gt;                        } else {&lt;br/&gt;                            F.prototype[name] = prop[name];&lt;br/&gt;                        }&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;                return F;&lt;br/&gt;            };&lt;br/&gt;        })();&lt;br/&gt;        // 经过改造的jClass&lt;br/&gt;        var Person = jClass.extend({&lt;br/&gt;            init: function(name) {&lt;br/&gt;                this.name = name;&lt;br/&gt;            },&lt;br/&gt;            getName: function(prefix) {&lt;br/&gt;                return prefix + this.name;&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;        var Employee = Person.extend({&lt;br/&gt;            init: function(name, employeeID) {&lt;br/&gt;                //  调用父类的方法&lt;br/&gt;                this._super(name);&lt;br/&gt;                this.employeeID = employeeID;&lt;br/&gt;            },&lt;br/&gt;            getEmployeeIDName: function() {&lt;br/&gt;                // 注意：我们还可以通过这种方式调用父类中的其他函数&lt;br/&gt;                var name = this._superprototype.getName.call(this, "Employee name: ");&lt;br/&gt;                return name + ", Employee ID: " + this.employeeID;&lt;br/&gt;            },&lt;br/&gt;            getName: function() {&lt;br/&gt;                //  调用父类的方法&lt;br/&gt;                return this._super("Employee name: ");&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;&lt;br/&gt;        var zhang = new Employee("ZhangSan", "1234");&lt;br/&gt;        console.log(zhang.getName());   // "Employee name: ZhangSan"&lt;br/&gt;        console.log(zhang.getEmployeeIDName()); // "Employee name: ZhangSan, Employee ID: 1234"&lt;br/&gt;        &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;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注：本章中的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;&lt;p&gt;首先让我们来回顾一下第一章中介绍的例子：&lt;/p&gt; function Person(name) { &lt;br/&gt;this.name = name; &lt;br/&gt;} &lt;br/&gt;Person.prototype = { &lt;br/&gt;getName: function() { &lt;br/&gt;return this.name; &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt; &lt;br/&gt;function Employee(name, employeeID) { &lt;br/&gt;this.name = name; &lt;br/&gt;this.employeeID = employeeID; &lt;br/&gt;} &lt;br/&gt;Employee.prototype = new Person(); &lt;br/&gt;Employee.prototype.getEmployeeID = function() { &lt;br/&gt;return this.employeeID; &lt;br/&gt;}; &lt;br/&gt;var zhang = new Employee("ZhangSan", "1234"); &lt;br/&gt;console.log(zhang.getName()); // "ZhangSan"  &lt;br/&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;修正constructor的指向错误&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;从上一篇文章中关于constructor的描述，我们知道Employee实例的constructor会有一个指向错误，如下所示：&lt;/p&gt; var zhang = new Employee("ZhangSan", "1234"); &lt;br/&gt;console.log(zhang.constructor === Employee); // false &lt;br/&gt;console.log(zhang.constructor === Object); // true  &lt;br/&gt;&lt;p&gt;我们需要简单的修正：&lt;/p&gt; function Employee(name, employeeID) { &lt;br/&gt;this.name = name; &lt;br/&gt;this.employeeID = employeeID; &lt;br/&gt;} &lt;br/&gt;Employee.prototype = new Person(); &lt;br/&gt;Employee.prototype.constructor = Employee; &lt;br/&gt;Employee.prototype.getEmployeeID = function() { &lt;br/&gt;return this.employeeID; &lt;br/&gt;}; &lt;br/&gt;var zhang = new Employee("ZhangSan", "1234"); &lt;br/&gt;console.log(zhang.constructor === Employee); // true &lt;br/&gt;console.log(zhang.constructor === Object); // false &lt;br/&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;创建Employee类时实例化Person是不合适的&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;但另一方面，我们又必须依赖于这种机制来实现继承。 解决办法是不在构造函数中初始化数据，而是提供一个原型方法（比如init）来初始化数据。&lt;/p&gt; // 空的构造函数 &lt;br/&gt;function Person() { &lt;br/&gt;} &lt;br/&gt;Person.prototype = { &lt;br/&gt;init: function(name) { &lt;br/&gt;this.name = name; &lt;br/&gt;}, &lt;br/&gt;getName: function() { &lt;br/&gt;return this.name; &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;// 空的构造函数 &lt;br/&gt;function Employee() { &lt;br/&gt;} &lt;br/&gt;// 创建类的阶段不会初始化父类的数据，因为Person是一个空的构造函数 &lt;br/&gt;Employee.prototype = new Person(); &lt;br/&gt;Employee.prototype.constructor = Employee; &lt;br/&gt;Employee.prototype.init = function(name, employeeID) { &lt;br/&gt;this.name = name; &lt;br/&gt;this.employeeID = employeeID; &lt;br/&gt;}; &lt;br/&gt;Employee.prototype.getEmployeeID = function() { &lt;br/&gt;return this.employeeID; &lt;br/&gt;}; &lt;br/&gt;&lt;p&gt;这种方式下，必须在实例化一个对象后手工调用init函数，如下：&lt;/p&gt; var zhang = new Employee(); &lt;br/&gt;zhang.init("ZhangSan", "1234"); &lt;br/&gt;console.log(zhang.getName()); // "ZhangSan" &lt;br/&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;如何自动调用init函数？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;必须达到两个效果，构造类时不要调用init函数和实例化对象时自动调用init函数。看来我们需要在调用空的构造函数时有一个状态标示。&lt;/p&gt; // 创建一个全局的状态标示 - 当前是否处于类的构造阶段 &lt;br/&gt;var initializing = false; &lt;br/&gt;function Person() { &lt;br/&gt;if (!initializing) { &lt;br/&gt;this.init.apply(this, arguments); &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;Person.prototype = { &lt;br/&gt;init: function(name) { &lt;br/&gt;this.name = name; &lt;br/&gt;}, &lt;br/&gt;getName: function() { &lt;br/&gt;return this.name; &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;function Employee() { &lt;br/&gt;if (!initializing) { &lt;br/&gt;this.init.apply(this, arguments); &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;// 标示当前进入类的创建阶段，不会调用init函数 &lt;br/&gt;initializing = true; &lt;br/&gt;Employee.prototype = new Person(); &lt;br/&gt;Employee.prototype.constructor = Employee; &lt;br/&gt;initializing = false; &lt;br/&gt;Employee.prototype.init = function(name, employeeID) { &lt;br/&gt;this.name = name; &lt;br/&gt;this.employeeID = employeeID; &lt;br/&gt;}; &lt;br/&gt;Employee.prototype.getEmployeeID = function() { &lt;br/&gt;return this.employeeID; &lt;br/&gt;}; &lt;br/&gt; &lt;br/&gt;// 初始化类实例时，自动调用类的原型函数init，并向init中传递参数 &lt;br/&gt;var zhang = new Employee("ZhangSan", "1234"); &lt;br/&gt;console.log(zhang.getName()); // "ZhangSan" &lt;br/&gt;&lt;p&gt;但是这样就必须引入全局变量，这是一个不好的信号。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;如何避免引入全局变量initializing？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;我们需要引入一个全局的函数来简化类的创建过程，同时封装内部细节避免引入全局变量。&lt;/p&gt; // 当前是否处于创建类的阶段 &lt;br/&gt;var initializing = false; &lt;br/&gt;function jClass(baseClass, prop) { &lt;br/&gt;// 只接受一个参数的情况 - jClass(prop) &lt;br/&gt;if (typeof (baseClass) === "object") { &lt;br/&gt;prop = baseClass; &lt;br/&gt;baseClass = null; &lt;br/&gt;} &lt;br/&gt;// 本次调用所创建的类（构造函数） &lt;br/&gt;function F() { &lt;br/&gt;// 如果当前处于实例化类的阶段，则调用init原型函数 &lt;br/&gt;if (!initializing) { &lt;br/&gt;this.init.apply(this, arguments); &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;// 如果此类需要从其它类扩展 &lt;br/&gt;if (baseClass) { &lt;br/&gt;initializing = true; &lt;br/&gt;F.prototype = new baseClass(); &lt;br/&gt;F.prototype.constructor = F; &lt;br/&gt;initializing = false; &lt;br/&gt;} &lt;br/&gt;// 覆盖父类的同名函数 &lt;br/&gt;for (var name in prop) { &lt;br/&gt;if (prop.hasOwnProperty(name)) { &lt;br/&gt;F.prototype[name] = prop[name]; &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;return F; &lt;br/&gt;}; &lt;br/&gt;&lt;p&gt;使用jClass函数来创建类和继承类的方法：&lt;/p&gt; var Person = jClass({ &lt;br/&gt;init: function(name) { &lt;br/&gt;this.name = name; &lt;br/&gt;}, &lt;br/&gt;getName: function() { &lt;br/&gt;return this.name; &lt;br/&gt;} &lt;br/&gt;}); &lt;br/&gt;var Employee = jClass(Person, { &lt;br/&gt;init: function(name, employeeID) { &lt;br/&gt;this.name = name; &lt;br/&gt;this.employeeID = employeeID; &lt;br/&gt;}, &lt;br/&gt;getEmployeeID: function() { &lt;br/&gt;return this.employeeID; &lt;br/&gt;} &lt;br/&gt;}); &lt;br/&gt; &lt;br/&gt;var zhang = new Employee("ZhangSan", "1234"); &lt;br/&gt;console.log(zhang.getName()); // "ZhangSan" &lt;br/&gt;&lt;p&gt;OK，现在创建类和实例化类的方式看起来优雅多了。 但是这里面还存在明显的瑕疵，Employee的初始化函数init无法调用父类的同名方法。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;如何调用父类的同名方法？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;我们可以通过为实例化对象提供一个base的属性，来指向父类（构造函数）的原型，如下：&lt;/p&gt; // 当前是否处于创建类的阶段 &lt;br/&gt;var initializing = false; &lt;br/&gt;function jClass(baseClass, prop) { &lt;br/&gt;// 只接受一个参数的情况 - jClass(prop) &lt;br/&gt;if (typeof (baseClass) === "object") { &lt;br/&gt;prop = baseClass; &lt;br/&gt;baseClass = null; &lt;br/&gt;} &lt;br/&gt;// 本次调用所创建的类（构造函数） &lt;br/&gt;function F() { &lt;br/&gt;// 如果当前处于实例化类的阶段，则调用init原型函数 &lt;br/&gt;if (!initializing) { &lt;br/&gt;// 如果父类存在，则实例对象的base指向父类的原型 &lt;br/&gt;// 这就提供了在实例对象中调用父类方法的途径 &lt;br/&gt;if (baseClass) { &lt;br/&gt;this.base = baseClass.prototype; &lt;br/&gt;} &lt;br/&gt;this.init.apply(this, arguments); &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;// 如果此类需要从其它类扩展 &lt;br/&gt;if (baseClass) { &lt;br/&gt;initializing = true; &lt;br/&gt;F.prototype = new baseClass(); &lt;br/&gt;F.prototype.constructor = F; &lt;br/&gt;initializing = false; &lt;br/&gt;} &lt;br/&gt;// 覆盖父类的同名函数 &lt;br/&gt;for (var name in prop) { &lt;br/&gt;if (prop.hasOwnProperty(name)) { &lt;br/&gt;F.prototype[name] = prop[name]; &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;return F; &lt;br/&gt;}; &lt;br/&gt;&lt;p&gt;调用方式：&lt;/p&gt; var Person = jClass({ &lt;br/&gt;init: function(name) { &lt;br/&gt;this.name = name; &lt;br/&gt;}, &lt;br/&gt;getName: function() { &lt;br/&gt;return this.name; &lt;br/&gt;} &lt;br/&gt;}); &lt;br/&gt;var Employee = jClass(Person, { &lt;br/&gt;init: function(name, employeeID) { &lt;br/&gt;// 调用父类的原型函数init，注意使用apply函数修改init的this指向 &lt;br/&gt;this.base.init.apply(this, [name]); &lt;br/&gt;this.employeeID = employeeID; &lt;br/&gt;}, &lt;br/&gt;getEmployeeID: function() { &lt;br/&gt;return this.employeeID; &lt;br/&gt;}, &lt;br/&gt;getName: function() { &lt;br/&gt;// 调用父类的原型函数getName &lt;br/&gt;return "Employee name: " + this.base.getName.apply(this); &lt;br/&gt;} &lt;br/&gt;}); &lt;br/&gt; &lt;br/&gt;var zhang = new Employee("ZhangSan", "1234"); &lt;br/&gt;console.log(zhang.getName()); // "Employee name: ZhangSan" &lt;br/&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;目前为止，我们已经修正了在第一章手工实现继承的种种弊端。 通过我们自定义的jClass函数来创建类和子类，通过原型方法init初始化数据， 通过实例属性base来调用父类的原型函数。&lt;/p&gt;&lt;p&gt;唯一的缺憾是调用父类的代码太长，并且不好理解， 如果能够按照如下的方式调用岂不是更妙：&lt;/p&gt; var Employee = jClass(Person, { &lt;br/&gt;init: function(name, employeeID) { &lt;br/&gt;// 如果能够这样调用，就再好不过了 &lt;br/&gt;this.base(name); &lt;br/&gt;this.employeeID = employeeID; &lt;br/&gt;} &lt;br/&gt;}); &lt;br/&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;优化jClass函数&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt; // 当前是否处于创建类的阶段 &lt;br/&gt;var initializing = false; &lt;br/&gt;function jClass(baseClass, prop) { &lt;br/&gt;// 只接受一个参数的情况 - jClass(prop) &lt;br/&gt;if (typeof (baseClass) === "object") { &lt;br/&gt;prop = baseClass; &lt;br/&gt;baseClass = null; &lt;br/&gt;} &lt;br/&gt;// 本次调用所创建的类（构造函数） &lt;br/&gt;function F() { &lt;br/&gt;// 如果当前处于实例化类的阶段，则调用init原型函数 &lt;br/&gt;if (!initializing) { &lt;br/&gt;// 如果父类存在，则实例对象的baseprototype指向父类的原型 &lt;br/&gt;// 这就提供了在实例对象中调用父类方法的途径 &lt;br/&gt;if (baseClass) { &lt;br/&gt;this.baseprototype = baseClass.prototype; &lt;br/&gt;} &lt;br/&gt;this.init.apply(this, arguments); &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;// 如果此类需要从其它类扩展 &lt;br/&gt;if (baseClass) { &lt;br/&gt;initializing = true; &lt;br/&gt;F.prototype = new baseClass(); &lt;br/&gt;F.prototype.constructor = F; &lt;br/&gt;initializing = false; &lt;br/&gt;} &lt;br/&gt;// 覆盖父类的同名函数 &lt;br/&gt;for (var name in prop) { &lt;br/&gt;if (prop.hasOwnProperty(name)) { &lt;br/&gt;// 如果此类继承自父类baseClass并且父类原型中存在同名函数name &lt;br/&gt;if (baseClass &amp;amp;&amp;amp; &lt;br/&gt;typeof (prop[name]) === "function" &amp;amp;&amp;amp; &lt;br/&gt;typeof (F.prototype[name]) === "function") { &lt;br/&gt; &lt;br/&gt;// 重定义函数name -  &lt;br/&gt;// 首先在函数上下文设置this.base指向父类原型中的同名函数 &lt;br/&gt;// 然后调用函数prop[name]，返回函数结果 &lt;br/&gt; &lt;br/&gt;// 注意：这里的自执行函数创建了一个上下文，这个上下文返回另一个函数， &lt;br/&gt;// 此函数中可以应用此上下文中的变量，这就是闭包（Closure）。 &lt;br/&gt;// 这是JavaScript框架开发中常用的技巧。 &lt;br/&gt;F.prototype[name] = (function(name, fn) { &lt;br/&gt;return function() { &lt;br/&gt;this.base = baseClass.prototype[name]; &lt;br/&gt;return fn.apply(this, arguments); &lt;br/&gt;}; &lt;br/&gt;})(name, prop[name]); &lt;br/&gt; &lt;br/&gt;} else { &lt;br/&gt;F.prototype[name] = prop[name]; &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;} &lt;br/&gt;return F; &lt;br/&gt;}; &lt;br/&gt;&lt;p&gt;此时，创建类与子类以及调用方式都显得非常优雅，请看：&lt;/p&gt; var Person = jClass({ &lt;br/&gt;init: function(name) { &lt;br/&gt;this.name = name; &lt;br/&gt;}, &lt;br/&gt;getName: function() { &lt;br/&gt;return this.name; &lt;br/&gt;} &lt;br/&gt;}); &lt;br/&gt;var Employee = jClass(Person, { &lt;br/&gt;init: function(name, employeeID) { &lt;br/&gt;this.base(name); &lt;br/&gt;this.employeeID = employeeID; &lt;br/&gt;}, &lt;br/&gt;getEmployeeID: function() { &lt;br/&gt;return this.employeeID; &lt;br/&gt;}, &lt;br/&gt;getName: function() { &lt;br/&gt;return "Employee name: " + this.base(); &lt;br/&gt;} &lt;br/&gt;}); &lt;br/&gt; &lt;br/&gt;var zhang = new Employee("ZhangSan", "1234"); &lt;br/&gt;console.log(zhang.getName()); // "Employee name: ZhangSan" &lt;br/&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;至此，我们已经创建了一个完善的函数jClass， 帮助我们在JavaScript中以比较优雅的方式实现类和继承。&lt;/p&gt;&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的例子： &lt;script type="text/javascript"&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;&lt;p&gt;&lt;strong&gt;this&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;this表示当前对象，如果在全局作用范围内使用this，则指代当前页面对象window； 如果在函数中使用this，则this指代什么是根据运行时此函数在什么对象上被调用。 我们还可以使用apply和call两个全局方法来改变函数中this的具体指向。&lt;/p&gt;&lt;p&gt;先看一个在全局作用范围内使用this的例子：&lt;/p&gt;        &amp;lt;script type="text/javascript"&amp;gt;&lt;br/&gt;            console.log(this === window);  // true&lt;br/&gt;            console.log(window.alert === this.alert);  // true&lt;br/&gt;            console.log(this.parseInt("021", 10));  // 10&lt;br/&gt;        &amp;lt;/script&amp;gt;&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;函数中的this是在运行时决定的，而不是函数定义时，如下：&lt;/p&gt;        // 定义一个全局函数&lt;br/&gt;        function foo() {&lt;br/&gt;            console.log(this.fruit);&lt;br/&gt;        }&lt;br/&gt;        // 定义一个全局变量，等价于window.fruit = "apple";&lt;br/&gt;        var fruit = "apple";&lt;br/&gt;        // 此时函数foo中this指向window对象&lt;br/&gt;        // 这种调用方式和window.foo();是完全等价的&lt;br/&gt;        foo();  // "apple"&lt;br/&gt;&lt;br/&gt;        // 自定义一个对象，并将此对象的属性foo指向全局函数foo&lt;br/&gt;        var pack = {&lt;br/&gt;            fruit: "orange",&lt;br/&gt;            foo: foo&lt;br/&gt;        };&lt;br/&gt;        // 此时函数foo中this指向window.pack对象&lt;br/&gt;        pack.foo(); // "orange"&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;全局函数apply和call可以用来改变函数中this的指向，如下：&lt;/p&gt;        // 定义一个全局函数&lt;br/&gt;        function foo() {&lt;br/&gt;            console.log(this.fruit);&lt;br/&gt;        }&lt;br/&gt;        &lt;br/&gt;        // 定义一个全局变量&lt;br/&gt;        var fruit = "apple";&lt;br/&gt;        // 自定义一个对象&lt;br/&gt;        var pack = {&lt;br/&gt;            fruit: "orange"&lt;br/&gt;        };&lt;br/&gt;        &lt;br/&gt;        // 等价于window.foo();&lt;br/&gt;        foo.apply(window);  // "apple"&lt;br/&gt;        // 此时foo中的this === pack&lt;br/&gt;        foo.apply(pack);    // "orange"&lt;br/&gt;        &lt;p&gt;注：apply和call两个函数的作用相同，唯一的区别是两个函数的参数定义不同。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;因为在JavaScript中函数也是对象，所以我们可以看到如下有趣的例子：&lt;/p&gt;        // 定义一个全局函数&lt;br/&gt;        function foo() {&lt;br/&gt;            if (this === window) {&lt;br/&gt;                console.log("this is window.");&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;        &lt;br/&gt;        // 函数foo也是对象，所以可以定义foo的属性boo为一个函数&lt;br/&gt;        foo.boo = function() {&lt;br/&gt;            if (this === foo) {&lt;br/&gt;                console.log("this is foo.");&lt;br/&gt;            } else if (this === window) {&lt;br/&gt;                console.log("this is window.");&lt;br/&gt;            }&lt;br/&gt;        };&lt;br/&gt;        // 等价于window.foo();&lt;br/&gt;        foo();  // this is window.&lt;br/&gt;        &lt;br/&gt;        // 可以看到函数中this的指向调用函数的对象&lt;br/&gt;        foo.boo();  // this is foo.&lt;br/&gt;        &lt;br/&gt;        // 使用apply改变函数中this的指向&lt;br/&gt;        foo.boo.apply(window);  // this is window.&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;prototype&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;我们已经在第一章中使用prototype模拟类和继承的实现。 prototype本质上还是一个JavaScript对象。 并且每个函数都有一个默认的prototype属性。 如果这个函数被用在创建自定义对象的场景中，我们称这个函数为构造函数。 比如下面一个简单的场景：&lt;/p&gt;        // 构造函数&lt;br/&gt;        function Person(name) {&lt;br/&gt;            this.name = name;&lt;br/&gt;        }&lt;br/&gt;        // 定义Person的原型，原型中的属性可以被自定义对象引用&lt;br/&gt;        Person.prototype = {&lt;br/&gt;            getName: function() {&lt;br/&gt;                return this.name;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;        var zhang = new Person("ZhangSan");&lt;br/&gt;        console.log(zhang.getName());   // "ZhangSan"&lt;br/&gt;        &lt;p&gt;作为类比，我们考虑下JavaScript中的数据类型 - 字符串（String）、数字（Number）、数组（Array）、对象（Object）、日期（Date）等。 我们有理由相信，在JavaScript内部这些类型都是作为构造函数来实现的，比如：&lt;/p&gt;        // 定义数组的构造函数，作为JavaScript的一种预定义类型&lt;br/&gt;        function Array() {&lt;br/&gt;            // ...&lt;br/&gt;        }&lt;br/&gt;        &lt;br/&gt;        // 初始化数组的实例&lt;br/&gt;        var arr1 = new Array(1, 56, 34, 12);&lt;br/&gt;        // 但是，我们更倾向于如下的语法定义：&lt;br/&gt;        var arr2 = [1, 56, 34, 12];&lt;br/&gt;        &lt;p&gt;同时对数组操作的很多方法（比如concat、join、push）应该也是在prototype属性中定义的。 实际上，JavaScript所有的固有数据类型都具有只读的prototype属性（这是可以理解的：因为如果修改了这些类型的prototype属性，则哪些预定义的方法就消失了）， 但是我们可以向其中添加自己的扩展方法。&lt;/p&gt;        // 向JavaScript固有类型Array扩展一个获取最小值的方法&lt;br/&gt;        Array.prototype.min = function() {&lt;br/&gt;            var min = this[0];&lt;br/&gt;            for (var i = 1; i &amp;lt; this.length; i++) {&lt;br/&gt;                if (this[i] &amp;lt; min) {&lt;br/&gt;                    min = this[i];&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;            return min;&lt;br/&gt;        };&lt;br/&gt;        &lt;br/&gt;        // 在任意Array的实例上调用min方法&lt;br/&gt;        console.log([1, 56, 34, 12].min());  // 1&lt;br/&gt;        &lt;p&gt;注意：这里有一个陷阱，向Array的原型中添加扩展方法后，当使用for-in循环数组时，这个扩展方法也会被循环出来。 下面的代码说明这一点（假设已经向Array的原型中扩展了min方法）：&lt;/p&gt;        var arr = [1, 56, 34, 12];&lt;br/&gt;        var total = 0;&lt;br/&gt;        for (var i in arr) {&lt;br/&gt;            total += parseInt(arr[i], 10);&lt;br/&gt;        }&lt;br/&gt;        console.log(total);   // NaN&lt;br/&gt;        &lt;br/&gt;        &lt;p&gt;解决方法也很简单：&lt;/p&gt;        var arr = [1, 56, 34, 12];&lt;br/&gt;        var total = 0;&lt;br/&gt;        for (var i in arr) {&lt;br/&gt;            if (arr.hasOwnProperty(i)) {&lt;br/&gt;                total += parseInt(arr[i], 10);&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;        console.log(total);   // 103&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;constructor&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;constructor始终指向创建当前对象的构造函数。比如下面例子：&lt;/p&gt;        // 等价于 var foo = new Array(1, 56, 34, 12);&lt;br/&gt;        var arr = [1, 56, 34, 12];&lt;br/&gt;        console.log(arr.constructor === Array); // true&lt;br/&gt;        // 等价于 var foo = new Function();&lt;br/&gt;        var Foo = function() { };&lt;br/&gt;        console.log(Foo.constructor === Function); // true&lt;br/&gt;        // 由构造函数实例化一个obj对象&lt;br/&gt;        var obj = new Foo();&lt;br/&gt;        console.log(obj.constructor === Foo); // true&lt;br/&gt;        &lt;br/&gt;        // 将上面两段代码合起来，就得到下面的结论&lt;br/&gt;        console.log(obj.constructor.constructor === Function); // true&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;但是当constructor遇到prototype时，有趣的事情就发生了。 我们知道每个函数都有一个默认的属性prototype，而这个prototype的constructor默认指向这个函数。如下例所示：&lt;/p&gt;        function Person(name) {&lt;br/&gt;            this.name = name;&lt;br/&gt;        };&lt;br/&gt;        Person.prototype.getName = function() {&lt;br/&gt;            return this.name;&lt;br/&gt;        };&lt;br/&gt;        var p = new Person("ZhangSan");&lt;br/&gt;        &lt;br/&gt;        console.log(p.constructor === Person);  // true&lt;br/&gt;        console.log(Person.prototype.constructor === Person); // true&lt;br/&gt;        // 将上两行代码合并就得到如下结果&lt;br/&gt;        console.log(p.constructor.prototype.constructor === Person); // true&lt;br/&gt;        &lt;p&gt;当时当我们重新定义函数的prototype时（注意：和上例的区别，这里不是修改而是覆盖）， constructor的行为就有点奇怪了，如下示例：&lt;/p&gt;        function Person(name) {&lt;br/&gt;            this.name = name;&lt;br/&gt;        };&lt;br/&gt;        Person.prototype = {&lt;br/&gt;            getName: function() {&lt;br/&gt;                return this.name;&lt;br/&gt;            }&lt;br/&gt;        };&lt;br/&gt;        var p = new Person("ZhangSan");&lt;br/&gt;        console.log(p.constructor === Person);  // false&lt;br/&gt;        console.log(Person.prototype.constructor === Person); // false&lt;br/&gt;        console.log(p.constructor.prototype.constructor === Person); // false&lt;br/&gt;        &lt;p&gt;为什么呢？ 原来是因为覆盖Person.prototype时，等价于进行如下代码操作：&lt;/p&gt;        Person.prototype = new Object({&lt;br/&gt;            getName: function() {&lt;br/&gt;                return this.name;&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;        &lt;p&gt;而constructor始终指向创建自身的构造函数，所以此时Person.prototype.constructor === Object，即是：&lt;/p&gt;        function Person(name) {&lt;br/&gt;            this.name = name;&lt;br/&gt;        };&lt;br/&gt;        Person.prototype = {&lt;br/&gt;            getName: function() {&lt;br/&gt;                return this.name;&lt;br/&gt;            }&lt;br/&gt;        };&lt;br/&gt;        var p = new Person("ZhangSan");&lt;br/&gt;        console.log(p.constructor === Object);  // true&lt;br/&gt;        console.log(Person.prototype.constructor === Object); // true&lt;br/&gt;        console.log(p.constructor.prototype.constructor === Object); // true&lt;br/&gt;        &lt;p&gt;怎么修正这种问题呢？方法也很简单，重新覆盖Person.prototype.constructor即可：&lt;/p&gt;        function Person(name) {&lt;br/&gt;            this.name = name;&lt;br/&gt;        };&lt;br/&gt;        Person.prototype = new Object({&lt;br/&gt;            getName: function() {&lt;br/&gt;                return this.name;&lt;br/&gt;            }&lt;br/&gt;        });&lt;br/&gt;        Person.prototype.constructor = Person;&lt;br/&gt;        var p = new Person("ZhangSan");&lt;br/&gt;        console.log(p.constructor === Person);  // true&lt;br/&gt;        console.log(Person.prototype.constructor === Person); // true&lt;br/&gt;        console.log(p.constructor.prototype.constructor === Person); // true&lt;br/&gt;        &lt;p&gt;&amp;nbsp;&lt;/p&gt;&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></feed>
