<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href='http://feed.army8735.org/styles/temp01.xsl' type='text/xsl' ?><!--这是一个由Feedsy提供技术支持的Feed，为了提高读者阅读的体验，以及满足用户美化自己Feed的需要，我们设计了多种精美的Feed模板，提供给大家选择，所有最终呈现出来的样式，皆由用户自愿选择使用，未经许可，任何团体和个人，请不要擅自修改样式或者盗用，这是对于用户选择权的尊重。--><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:fs="http://www.feedsky.com/namespace/feed" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link href="http://feed.army8735.org" type="application/rss+xml" rel="self"></atom:link><fs:self_link href="http://feed.feedsky.com/army8735" type="application/rss+xml"></fs:self_link><lastBuildDate>Tue, 02 Feb 2010 06:11:39 GMT</lastBuildDate><title>army8735</title><description>做一颗可爱的小土豆～ ^ ^</description><link>http://army8735.org</link><sy:updatePeriod>hourly</sy:updatePeriod><sy:updateFrequency>1</sy:updateFrequency><language>en</language><pubDate>Wed, 03 Feb 2010 04:39:11 GMT</pubDate><item><title>web端语法高亮原理：走进jssc的世界（四）</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650279/5961215/1/item.html</link><content:encoded>&lt;h3&gt;内嵌解析&lt;/h3&gt;
&lt;p&gt;很容易遇到这样的情况：在需要高亮的代码中还混淆着其它语言种类的代码（最常见的例子为Html内嵌css和js，以下也将以此为例）。这是一件让人头疼的事情，因为无论采用何种方法，内嵌的语言和原本语言的规则一定是不同的。这意味着必须将它们区分开来对待。从这一点出发，自然而然就能引出问题的关键所在——如何区分？&lt;/p&gt;
&lt;h3&gt;剥离内容&lt;/h3&gt;
&lt;p&gt;先来考虑最简单的情况：&lt;/p&gt;
&lt;pre class=&quot;brush:html&quot;&gt;&amp;lt;head&amp;gt;
&amp;lt;script&amp;gt;var i = 0;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;&lt;/pre&gt;
&lt;p&gt;假如没有第2行的script标签以及其内部的代码，那么整个就是纯html代码高亮，这个没有什么难度（如果已经完全理解前三篇的此法分析的话）。然而不凑巧的是，关键点就在于script标签中会出现js代码，html的此法分析中并没有js的词法规则，两者不能等同。那么怎么办呢？&lt;/p&gt;
&lt;p&gt;答案是将它们剥离出来。上例是最简单的例子，我们在对html进行此法分析的时候，一旦读到了&amp;lt;script&amp;gt;开始标签，接着便去寻找&amp;lt;/script&amp;gt;结束标签（一般会使用String.indexOf()来查找），然后将标签里面的内容单独提取出来。这是第一步，如果做完的话，此时html的高亮结果应该是：除了js代码没有高亮（即默认颜色）以外，其它的html代码均被正确高亮了。&lt;/p&gt;
&lt;h3&gt;更复杂的情况&lt;/h3&gt;
&lt;p&gt;剥离到此还没有结束，因为剥离要考虑其它一些复杂的元素。看以下代码：&lt;/p&gt;
&lt;pre class=&quot;brush:html&quot;&gt;&amp;lt;head&amp;gt;
&amp;lt;script&amp;gt;var i = 0;
//&amp;lt;/script&amp;gt;
&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;&lt;/pre&gt;
&lt;p&gt;代码的第3行中，出现了单行注释，其中有被注释掉的script结束标签。这点需要格外注意。假若使用String.indexOf()来查询script结束标签的话，那么就会在第3行结束。这样就错了，因为第3行实际上是个注释，真正的结束符在第4行。&lt;/p&gt;
&lt;p&gt;以此延伸，除了上面的情况以外，引号中的字符串、多行注释、正则里面均会出现类似情况。因此，单纯的String.indexOf()是肯定不行的。我们必须对js代码部分进行预处理。&lt;/p&gt;
&lt;h3&gt;预处理&lt;/h3&gt;
&lt;p&gt;在as的解析部分，实际上主要分为两大块：词法分析和存储结果。词法分析即是前面几篇一直在讲解的内容；存储结果即是将分析出来的代码链接起来，说白了就是简单的字符串拼接。&lt;/p&gt;
&lt;p&gt;在html中的js代码可能会出现混淆script结束标签的情况之下，唯一解决的办法就是对js也进行&lt;strong&gt;简单的词法分析预处理&lt;/strong&gt;，但不存储结果。因为分析是“读”，而存储是“写”。写的耗时要比读多多了，而且预处理只是为了防止注释、字符串和正则的混淆，不需要真正地进行解析，实现复杂读也比较低。等完全将js代码从html中剥离后，再交给js解析器来做真正的工作，如此也保证了代码的不重复。&lt;/p&gt;
&lt;pre class=&quot;brush:html&quot;&gt;&amp;lt;head&amp;gt;
&amp;lt;script&amp;gt;
var i = 0;
//&amp;lt;/script&amp;gt;
&quot;&amp;lt;/script&amp;gt;&quot;
/*&amp;lt;/script&amp;gt;*/
/[&amp;lt;/script&amp;gt;]/
&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;&lt;/pre&gt;
&lt;p&gt;html中的预处理，至少要保证能将3到7行的js代码完全剥离出来交给js解析器处理，其它html代码则由本身来完成高亮。&lt;/p&gt;
&lt;h3&gt;状态区分&lt;/h3&gt;
&lt;p&gt;接下来的难点可能还是在如何在html解析的时候完成区分上面。在这里，我设置了一个state变量用以标识状态。仔细考虑下html，无非发现它主要有以下几种状态：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;html节点：即&amp;lt;&amp;gt;内的tag，还有随之的一些属性内容。如：&amp;lt;img width=&amp;#8221;100px&amp;#8221;/&amp;gt;。&lt;/li&gt;
&lt;li&gt;text节点：文本内容，段落p中最常见到。&lt;/li&gt;
&lt;li&gt;css节点：style中的css代码。&lt;/li&gt;
&lt;li&gt;script节点：script中的js代码。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在默认最开始的时候，是文本节点。一旦遇到了左尖括号，并且随后跟的是个正确的节点名（&amp;lt;x&amp;gt;绝对不是个正确的节点，所以不能当成节点来处理），那么就进入节点状态来解析；当节点解析完了之后，返回文本状态。css和script节点是个两个特殊的节点，因为在它们的开始标签结束之后，要进行预处理查找结束标签。实际上会做其中的一个，另外一个也就懂了。&lt;/p&gt;
&lt;p&gt;值得注意的是，html标签中有单个类型的存在，比如&amp;lt;br/&amp;gt;，它不需要成对出现，甚至可以写成&amp;lt;br&amp;gt;。省略/的又是另外一种自闭合类型。它们在处理起来有点麻烦，特别是涉及到深度折叠的时候。解决的办法也是设置状态变量，标识当前节点属于那种类型，以此来区分判断。&lt;/p&gt;
&lt;p&gt;这篇写得可能有点简单，因为的确是比较抽象的东西。我也偷偷懒，相信能做到前章所提的词法分析的情况下，纯理论来读本篇也不是什么难事了。可能直接读我的源代码反而会更容易些。在下一篇当中，我会介绍as和js的交互以及jssc的大概处理流程，它将作为结束篇章。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650279/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650279/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650279/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650279/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2010/02/02/608.html/feed</wfw:commentRss><slash:comments>0</slash:comments><description>内嵌解析
很容易遇到这样的情况：在需要高亮的代码中还混淆着其它语言种类的代码（最常见的例子为Html内嵌css和js，以下也将以此为例）。这是一件让人头疼的事情，因为无论采用何种方法，内嵌的语言和原本语言的规则一定是不同的。这意味着必须将它们区分开来对待。从这一点出发，自然而然就能引出问题的关键所在——如何区分？
剥离内容
先来考虑最简单的情况：
&amp;#60;head&amp;#62;
&amp;#60;script&amp;#62;var i = 0;&amp;#60;/script&amp;#62;
&amp;#60;/head&amp;#62;
假如没有第2行的script标签以及其内部的代码，那么整个就是纯html代码高亮，这个没有什么难度（如果已经完全理解前三篇的此法分析的话）。然而不凑巧的是，关键点就在于script标签中会出现js代码，html的此法分析中并没有js的词法规则，两者不能等同。那么怎么办呢？
答案是将它们剥离出来。上例是最简单的例子，我们在对html进行此法分析的时候，一旦读到了&amp;#60;script&amp;#62;开始标签，接着便去寻找&amp;#60;/script&amp;#62;结束标签（一般会使用String.indexOf()来查找），然后将标签里面的内容单独提取出来。这是第一步，如果做完的话，此时html的高亮结果应该是：除了js代码没有高亮（即默认颜色）以外，其它的html代码均被正确高亮了。
更复杂的情况
剥离到此还没有结束，因为剥离要考虑其它一些复杂的元素。看以下代码：
&amp;#60;head&amp;#62;
&amp;#60;script&amp;#62;var i = 0;
//&amp;#60;/script&amp;#62;
&amp;#60;/script&amp;#62;
&amp;#60;/head&amp;#62;
代码的第3行中，出现了单行注释，其中有被注释掉的script结束标签。这点需要格外注意。假若使用String.indexOf()来查询script结束标签的话，那么就会在第3行结束。这样就错了，因为第3行实际上是个注释，真正的结束符在第4行。
以此延伸，除了上面的情况以外，引号中的字符串、多行注释、正则里面均会出现类似情况。因此，单纯的String.indexOf()是肯定不行的。我们必须对js代码部分进行预处理。
预处理
在as的解析部分，实际上主要分为两大块：词法分析和存储结果。词法分析即是前面几篇一直在讲解的内容；存储结果即是将分析出来的代码链接起来，说白了就是简单的字符串拼接。
在html中的js代码可能会出现混淆script结束标签的情况之下，唯一解决的办法就是对js也进行简单的词法分析预处理，但不存储结果。因为分析是“读”，而存储是“写”。写的耗时要比读多多了，而且预处理只是为了防止注释、字符串和正则的混淆，不需要真正地进行解析，实现复杂读也比较低。等完全将js代码从html中剥离后，再交给js解析器来做真正的工作，如此也保证了代码的不重复。
&amp;#60;head&amp;#62;
&amp;#60;script&amp;#62;
var i = 0;
//&amp;#60;/script&amp;#62;
&quot;&amp;#60;/script&amp;#62;&quot;
/*&amp;#60;/script&amp;#62;*/
/[&amp;#60;/script&amp;#62;]/
&amp;#60;/script&amp;#62;
&amp;#60;/head&amp;#62;
html中的预处理，至少要保证能将3到7行的js代码完全剥离出来交给js解析器处理，其它html代码则由本身来完成高亮。
状态区分
接下来的难点可能还是在如何在html解析的时候完成区分上面。在这里，我设置了一个state变量用以标识状态。仔细考虑下html，无非发现它主要有以下几种状态：

html节点：即&amp;#60;&amp;#62;内的tag，还有随之的一些属性内容。如：&amp;#60;img width=&amp;#8221;100px&amp;#8221;/&amp;#62;。
text节点：文本内容，段落p中最常见到。
css节点：style中的css代码。
script节点：script中的js代码。

在默认最开始的时候，是文本节点。一旦遇到了左尖括号，并且随后跟的是个正确的节点名（&amp;#60;x&amp;#62;绝对不是个正确的节点，所以不能当成节点来处理），那么就进入节点状态来解析；当节点解析完了之后，返回文本状态。css和script节点是个两个特殊的节点，因为在它们的开始标签结束之后，要进行预处理查找结束标签。实际上会做其中的一个，另外一个也就懂了。
值得注意的是，html标签中有单个类型的存在，比如&amp;#60;br/&amp;#62;，它不需要成对出现，甚至可以写成&amp;#60;br&amp;#62;。省略/的又是另外一种自闭合类型。它们在处理起来有点麻烦，特别是涉及到深度折叠的时候。解决的办法也是设置状态变量，标识当前节点属于那种类型，以此来区分判断。
这篇写得可能有点简单，因为的确是比较抽象的东西。我也偷偷懒，相信能做到前章所提的词法分析的情况下，纯理论来读本篇也不是什么难事了。可能直接读我的源代码反而会更容易些。在下一篇当中，我会介绍as和js的交互以及jssc的大概处理流程，它将作为结束篇章。&lt;img src=&quot;http://www1.feedsky.com/t1/328650279/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650279/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650279/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650279/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>jssc</category><category>语法高亮原理</category><category>系列文章</category><pubDate>Tue, 02 Feb 2010 14:11:39 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2010/02/02/608.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=608</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2010/02/02/608.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650279/5961215</fs:itemid></item><item><title>YY一下各种土豆</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650281/5961215/1/item.html</link><content:encoded>&lt;p&gt;首先还是感谢下&lt;a href=&quot;http://www.mikkolee.com/&quot; target=&quot;_blank&quot;&gt;小麦&lt;/a&gt;，让偶有机会加入&lt;a href=&quot;http://www.tudou.com&quot; target=&quot;_blank&quot;&gt;土豆大家庭&lt;/a&gt;。作为新土豆，来胡乱YY一下假想中的各种土豆吧，我只知道“&lt;a href=&quot;http://hd.tudou.com&quot; target=&quot;_blank&quot;&gt;黑豆&lt;/a&gt;”是高清……&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;红豆：SNS的代表，红色是社交的颜色，符合这个主题。&lt;/li&gt;
&lt;li&gt;绿豆：健康的代表，绿豆本身就是很有营养价值的豆子，不都说绿色食品么。&lt;/li&gt;
&lt;li&gt;金豆：理财投资的代表，这个没必要解释了吧。&lt;/li&gt;
&lt;li&gt;紫豆：女性专题的代表，貌似粉豆也差不多。&lt;/li&gt;
&lt;li&gt;蓝豆：科技的代表，科技是比较新潮梦幻的，蓝色也是梦幻的。&lt;/li&gt;
&lt;li&gt;黄豆：城市消费？想不出来该是啥……&lt;/li&gt;
&lt;li&gt;魔豆：没想好该是什么……&lt;/li&gt;
&lt;/ol&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650281/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650281/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650281/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650281/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2010/02/01/606.html/feed</wfw:commentRss><slash:comments>0</slash:comments><description>首先还是感谢下小麦，让偶有机会加入土豆大家庭。作为新土豆，来胡乱YY一下假想中的各种土豆吧，我只知道“黑豆”是高清……

红豆：SNS的代表，红色是社交的颜色，符合这个主题。
绿豆：健康的代表，绿豆本身就是很有营养价值的豆子，不都说绿色食品么。
金豆：理财投资的代表，这个没必要解释了吧。
紫豆：女性专题的代表，貌似粉豆也差不多。
蓝豆：科技的代表，科技是比较新潮梦幻的，蓝色也是梦幻的。
黄豆：城市消费？想不出来该是啥……
魔豆：没想好该是什么……&lt;img src=&quot;http://www1.feedsky.com/t1/328650281/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650281/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650281/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650281/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>其它</category><pubDate>Mon, 01 Feb 2010 14:54:07 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2010/02/01/606.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=606</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2010/02/01/606.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650281/5961215</fs:itemid></item><item><title>如何访问作为命名空间的函数内部变量</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650283/5961215/1/item.html</link><content:encoded>&lt;p&gt;原文标题：《Functions as Namespaces, and How to Peek Inside》&lt;/p&gt;
&lt;p&gt;原文地址：&lt;a href=&quot;http://www.davidflanagan.com/2009/11/functions-as-na.html&quot;&gt;http://www.davidflanagan.com/2009/11/functions-as-na.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;很精彩的技巧，通过闭包和eval()的使用，达到标题中的功能。&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var value = (function() {  // Wrapper function creates a local scope or namespace
	// your code goes here
	return value;  // Export a value from the namespace
})();  // Invoke the wrapper function to run your code&lt;/pre&gt;
&lt;p&gt;以上代码是我们经常用到的技巧：赋值时通过执行一个匿名函数来防止一些代码变量污染全局空间，并且函数内部可以写很复杂的实现而无需担心对外部的影响。&lt;/p&gt;
&lt;p&gt;但是有些情况下我们得到的js代码是一串字符串——例如，用xhr读取到的js代码。倘若想要使用它，可以用eval()，还可以用更方便的Function()构造器。&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var code = &quot;alert(1);&quot;;  // A string of JS code to evaluate
var f = new Function(code);   // Wrap it in a function
f();    // And run the function

//army注：这段代码和下面是完全等同的：
function f() {
	alert(1);
}
f();&lt;/pre&gt;
&lt;p&gt;所以，通过这种技巧，哪怕是从一段js字符串源代码来赋值，也是可行的：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var code = &quot;return 3;&quot;;
var f = new Function(code);
var i = f(); //i是3&lt;/pre&gt;
&lt;p&gt;但是这里却有一个问题。因为是由Function()构造函数而来的，相当于创造了一个密封的命名空间（所有代码都在一个匿名function内执行），我们无法从外部访问它。倘若里面有一些定义的类或者函数之类的东西，那就难办了。比如这样：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var code = &quot;function Test() {};&quot;;
var f = new Function(code);
f();
//这相当于执行了以下方法：
function f() {
	function Test() {
	}
};
f();&lt;/pre&gt;
&lt;p&gt;内部定义了一个Test类，我们很难访问到它。不过这里有个技巧——这也是本篇要介绍的主角——可以通过&lt;strong&gt;闭包+eval()&lt;/strong&gt;结合使用来绕过这种限制。&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var code = &quot;function Test() { alert('a test'); };&quot;;
var f = new Function(code + &quot;return function(s) { return eval(s); };&quot;)(); //关键！还有后面的括号！
var Test = f(&quot;Test&quot;);
new Test();&lt;/pre&gt;
&lt;p&gt;如何？内部的Test类成功从外部创建了。关键就在于第2行，这里有个小小限制，第3行传入的参数需和要使用的内部变量名相等。外部的Test其实相当于密封函数内的Test的一个copy，没想明白的话根据代码倒着走一遍就ok了。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650283/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650283/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650283/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650283/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2010/01/26/591.html/feed</wfw:commentRss><slash:comments>3</slash:comments><description>原文标题：《Functions as Namespaces, and How to Peek Inside》
原文地址：http://www.davidflanagan.com/2009/11/functions-as-na.html
很精彩的技巧，通过闭包和eval()的使用，达到标题中的功能。
var value = (function() {  // Wrapper function creates a local scope or namespace
	// your code goes here
	return value;  // Export a value from the namespace
})();  // Invoke the wrapper function to run your code
以上代码是我们经常用到的技巧：赋值时通过执行一个匿名函数来防止一些代码变量污染全局空间，并且函数内部可以写很复杂的实现而无需担心对外部的影响。
但是有些情况下我们得到的js代码是一串字符串——例如，用xhr读取到的js代码。倘若想要使用它，可以用eval()，还可以用更方便的Function()构造器。
var code = &quot;alert(1);&quot;;  // A string of JS code [...]&lt;img src=&quot;http://www1.feedsky.com/t1/328650283/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650283/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650283/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650283/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>前端开发</category><category>翻译</category><pubDate>Tue, 26 Jan 2010 09:28:06 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2010/01/26/591.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=591</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2010/01/26/591.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650283/5961215</fs:itemid></item><item><title>ecmascript中的命令链</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650285/5961215/1/item.html</link><content:encoded>&lt;p&gt;有人好奇JAse中的undo和redo是怎么做的，并且给出自己的设计做法也各有特色。我最初采用的是全文保存方法（这一方法也是最简单有效、使用最广泛的），后来被人痛批一顿换成了命令链。命令链其实是基于一种叫做“命令”的设计模式的，关键思想在于&lt;strong&gt;面向接口编程&lt;/strong&gt;。这个东西随便搜搜有很多例子，我再重复制造一下轮子吧。&lt;/p&gt;
&lt;h3&gt;AS3中的命令链&lt;/h3&gt;
&lt;p&gt;as3对OOP的支持已经比较完善，所以可以据此写出很好的命令链，先从简单的例子来说起（以下代码均被简化）。假如我们要做一个Dog类，Dog的“说话”方式是bark（狗叫）：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;class Dog {
	public function bark():void {
		trace(&quot;汪汪！&quot;);
	}
}&lt;/pre&gt;
&lt;p&gt;这很简单，没有什么特殊之处。可是奇怪的事情发生了，中学英语课本中有个闻名的澳洲野狗Dingo，它的叫声和普通的狗不一样：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;class Dingo extends Dog {
	public override function bark():void {
		trace(&quot;呜呜——&quot;);
	}
}&lt;/pre&gt;
&lt;p&gt;也很简单，Dingo毕竟还是一只狗，只需继承并覆盖即可。好了现在需求来了：我们想听听这些动物的叫声是什么样的，只需要调用下相应的方法即可。&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;public class Test {
	public function Test():void {
		var dog:Dog = new Dog();
		dog.bark();

		var dingo:Dog = new Dingo();
		dingo.bark();
	}
}&lt;/pre&gt;
&lt;p&gt;目前为止一切顺利，可惜未来总是不像我们想象的那样。动物中又多了只公鸡，它的叫法完全不一样，是打鸣。&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;class Cock {
	public function crow():void {
		trace(&quot;喔喔——&quot;);
	}
}&lt;/pre&gt;
&lt;p&gt;这下坏了，我们在听叫声的时候得记住，公鸡的叫法和狗不一样：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;public class Test {
	public function Test():void {
		var dog:Dog = new Dog();
		dog.bark();

		var dingo:Dog = new Dingo();
		dingo.bark();

		var cock:Cock = new Cock();
		cock.crow();
	}
}&lt;/pre&gt;
&lt;p&gt;倘若把这些动物排成一列，让它们依次各自叫一下的话就更麻烦了：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;public class Test {
	public function Test():void {
		var list:Array = [new Dog(), new Dingo(), new Cock()];
		for (var i:int = 0; i &amp;lt; list.length; i++) {
			var item = list[i];
			if (item is Dog) {
				item.bark();
			}
			else if (item is Cock) {
				item.crow();
			}
		}
	}
}&lt;/pre&gt;
&lt;p&gt;我们得对每种类型做单独判断，可所谓不胜其烦，动物的种类是不可预知的，倘若再来一只猫，还要继续增加判别吗？当然不能。&lt;/p&gt;
&lt;p&gt;显然，关于叫法这里我们需要解耦。不管你是什么动物，这些叫声其实都可以概括为“说话”（动物们也有自己的语言和说话方式）。在主调程序中，我们并不想关心动物是“怎么说话”、“说些什么”的，我们只想调用一个命令，动物对象就能自动按照自己的方式来“说话”，甚至我们不用关心这个动物到底是什么。通过分离做什么和怎么做来实现这个目标的方式，就称为&lt;strong&gt;命令模式&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;好了，首先是命令接口：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;public interface Command {
	function say():void;
}&lt;/pre&gt;
&lt;p&gt;所有动物只要实现了这个接口就行，各自具体的执行方式自己来定：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;class Dog implements Commad {
	public function say():void {
		bark();
	}
	protected function bark():void {
		trace(&quot;汪汪！&quot;);
	}
}

class Dingo extends Dog {
	protected override function bark():void {
		trace(&quot;呜呜——&quot;&quot;);
	}
}

class Cock implements Commad {
	public function say():void {
		crow();
	}
	private function crow():void {
		trace(&quot;喔喔——&quot;);
	}
}&lt;/pre&gt;
&lt;p&gt;这样的话，在主调程序中，管它排成一列的动物有哪些，统统视作Command接口的实现即可：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;public class Test {
	public function Test():void {
		var list:Array = [new Dog(), new Dingo(), new Cock()];
		for (var i:int = 0; i &amp;lt; list.length; i++) {
			(list[i] as Command).say();
		}
	}
}&lt;/pre&gt;
&lt;p&gt;哪怕再多出来一只猫，我们只需要增加猫的这一类别，列表中多出只猫来即可，主调程序无需关心具体实现。甚至猫的方式更加复杂有感情：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;class Cat implements Command {
	private var isHappy:Boolean = true;

	public function say():void {
		if (isHappy) {
			purr();
		}
		else {
			mew();
		}
	}

	private function purr():void {
		trace(&quot;咕噜……&quot;);
	}
	private function mew():void {
		trace(&quot;喵——&quot;);
	}
}

public class Test {
	public function Test():void {
		var list:Array = [new Dog(), new Dingo(), new Cock(), new Cat()];
		for (var i:int = 0; i &amp;lt; list.length; i++) {
			(list[i] as Command).say();
		}
	}
}&lt;/pre&gt;
&lt;h3&gt;undo、redo的命令链&lt;/h3&gt;
&lt;p&gt;熟悉了这些后，undo和redo的做法同理：只需实现了命令接口，每个操作各是独自的命令，互不干涉，具体实现自己决定，最大程度上解耦。&lt;/p&gt;
&lt;p&gt;定义命令接口（以下代码均被简化）：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;package command {

	public interface ICommand {
		function redo():void;
		function undo():void;
	}

}&lt;/pre&gt;
&lt;p&gt;输入命令——也就是在编辑器中敲入代码时：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;package command {
	import flash.text.*;

	public class InputCommand implements ICommand {
		private var tf:TextField;
		private var index:int;
		private var text:String;

		public function InputCommand(tf:TextField, index:int, text:String):void {
			this.tf = tf;
			this.index = index;
			this.text = text;
		}

		public function redo():void {
			tf.setSelection(index + text.length, index + text.length);
		}
		public function undo():void {
			tf.replaceText(index, index + text.length, &quot;&quot;);
		}
	}
}&lt;/pre&gt;
&lt;p&gt;删除命令——当按Delete键将编辑器里的内容删除时：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;package command {
	import flash.text.*;

	public class DeleteCommand implements ICommand {
		private var tf:TextField;
		private var index:int;
		private var end:int;
		private var text:String;

		public function DeleteCommand(tf:TextField, index:int, end:int, text:String):void {
			this.tf = tf;
			this.index = index;
			this.end = end;
			this.text = text;
		}

		public function redo():void {
			tf.replaceText(index, index + text.length, &quot;&quot;);
		}
		public function undo():void {
			tf.replaceText(index, index, text);
		}
	}
}&lt;/pre&gt;
&lt;p&gt;于是，每当我们输入一个字符时，向一个记录命令步骤的数组里存入一个输入命令；而在按下Delete键时，存入一个删除命令；其它多一个命令多建一个类实现，这样一个链连下来就是&lt;strong&gt;命令链&lt;/strong&gt;。&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;package command {
	import flash.text.*;
	import edit.*;

	public class CommandList {
		private var undoList:Array, redoList:Array;

		public function CommandList():void {
			clear();
		}

		public function addCommand(cmd:ICommand):void {
			//超过最大命令链长度需先出队列一个
			if (undoList.length &amp;gt; Editor.UNDO_SIZE) {
				undoList.shift();
			}
			//每添加一次命令，清空redoList
			if(redoList.length) {
				redoList = new Array();
			}
			undoList.push(cmd);
		}
		public function undo():Boolean {
			//undoList中有命令则执行，并将相应命令出栈存入redoList中
			if(undoList.length) {
				var cmd:ICommand = undoList.pop() as ICommand;
				cmd.undo();
				redoList.push(cmd);
				return true;
			}
			//为空返回false
			else {
				return false;
			}
		}
		public function redo():Boolean {
			//redoList中有命令则执行，并将相应命令出栈存入undoList中
			if (redoList.length) {
				var cmd:ICommand = redoList.pop() as ICommand;
				cmd.redo();
				undoList.push(cmd);
				return true;
			}
			//为空返回false
			else {
				return false;
			}
		}
		public function clear():void {
			undoList = new Array();
			redoList = new Array();
		}
	}

}&lt;/pre&gt;
&lt;p&gt;其实基于fp10中as3的新特性，有个叫Vector的类，它是存储命令链数组的更好的替代者，因为它规定数组里所有元素的类型必须是相同的。这样在编译期间便能防止出错，于是undoList和redoList更好的定义方式是这样：&lt;/p&gt;
&lt;pre class=&quot;brush:as3&quot;&gt;undoList = new Vector.&amp;lt;ICommand&amp;gt;();
redoList = new Vector.&amp;lt;ICommand&amp;gt;();&lt;/pre&gt;
&lt;p&gt;熟悉Java的很容易理解：这就是Java5+中的泛型。&lt;/p&gt;
&lt;h3&gt;js中的命令链&lt;/h3&gt;
&lt;p&gt;扯了这么远才到js，而且篇幅也不会多长，真是愧对这标题。&lt;/p&gt;
&lt;p&gt;js中并不存在接口，所以也就无从说起面向接口编程。然而以此就下定论说“不能使用命令链”尚为时过早。的确，js由于其本身特性原因，不能像传统OOP语言那样使用，但命令链模式其实就存在于日常生活当中：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;function Dog() {
}
Dog.prototype.say = function() {
	this.bark();
}
Dog.prototype.bark = function() {
	alert(&quot;汪汪！&quot;);
}

function Dingo() {
}
Dingo.prototype = new Dog();
Dingo.prototype.bark = function() {
	alert(&quot;呜呜——&quot;);
}

function Cock() {
}
Cock.prototype.say = function() {
	this.crow();
}
Cock.prototype.crow = function() {
	alert(&quot;喔喔——&quot;);
}

function Cat() {
	this.bHappy = true;
}
Cat.prototype.say = function() {
	if(this.bHappy) {
		this.purr();
	}
	else {
		this.mew();
	}
}
Cat.prototype.purr = function() {
	alert(&quot;咕噜……&quot;);
}
Cat.prototype.mew = function() {
	alert(&quot;喵——&quot;);
}

var aList = [new Dog(), new Dingo(), new Cock(), new Cat()];
for(var i = 0; i &amp;lt; aList.length; i++) {
	aList[i].say();
}&lt;/pre&gt;
&lt;p&gt;基于弱类型，每个类都可以拥有一个同名方法，广义上说这也可以看作是实现了一个“通用接口”。js没有编译过程，因此无法在前期（编译期）实现检查，只能靠后期（运行时）来确定。网上也有很多例子模拟出js的接口和命令链，使得一个类在实现接口但却没有实现接口定义的方法时抛出异常。这样做的好处是在运行出错时有详尽的异常信息供使用者检查，但在目前firebug等工具的情况下所提供的能效有限，在大型的开发中才会有很好的帮助。因此我们目前大部分情况下——似乎最好的办法就是&lt;strong&gt;相信别人&lt;/strong&gt;。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650285/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650285/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650285/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650285/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2010/01/25/567.html/feed</wfw:commentRss><slash:comments>2</slash:comments><description>有人好奇JAse中的undo和redo是怎么做的，并且给出自己的设计做法也各有特色。我最初采用的是全文保存方法（这一方法也是最简单有效、使用最广泛的），后来被人痛批一顿换成了命令链。命令链其实是基于一种叫做“命令”的设计模式的，关键思想在于面向接口编程。这个东西随便搜搜有很多例子，我再重复制造一下轮子吧。
AS3中的命令链
as3对OOP的支持已经比较完善，所以可以据此写出很好的命令链，先从简单的例子来说起（以下代码均被简化）。假如我们要做一个Dog类，Dog的“说话”方式是bark（狗叫）：
class Dog {
	public function bark():void {
		trace(&quot;汪汪！&quot;);
	}
}
这很简单，没有什么特殊之处。可是奇怪的事情发生了，中学英语课本中有个闻名的澳洲野狗Dingo，它的叫声和普通的狗不一样：
class Dingo extends Dog {
	public override function bark():void {
		trace(&quot;呜呜——&quot;);
	}
}
也很简单，Dingo毕竟还是一只狗，只需继承并覆盖即可。好了现在需求来了：我们想听听这些动物的叫声是什么样的，只需要调用下相应的方法即可。
public class Test {
	public function Test():void {
		var dog:Dog = new Dog();
		dog.bark();

		var dingo:Dog = new Dingo();
		dingo.bark();
	}
}
目前为止一切顺利，可惜未来总是不像我们想象的那样。动物中又多了只公鸡，它的叫法完全不一样，是打鸣。
class Cock {
	public function crow():void {
		trace(&quot;喔喔——&quot;);
	}
}
这下坏了，我们在听叫声的时候得记住，公鸡的叫法和狗不一样：
public class Test {
	public function Test():void {
		var dog:Dog = new Dog();
		dog.bark();

		var dingo:Dog = new Dingo();
		dingo.bark();

		var cock:Cock = new Cock();
		cock.crow();
	}
}
倘若把这些动物排成一列，让它们依次各自叫一下的话就更麻烦了：
public class Test {
	public function [...]&lt;img src=&quot;http://www1.feedsky.com/t1/328650285/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650285/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650285/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650285/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>命令</category><category>设计模式</category><category>前端开发</category><category>命令链</category><pubDate>Mon, 25 Jan 2010 11:10:28 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2010/01/25/567.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=567</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2010/01/25/567.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650285/5961215</fs:itemid></item><item><title>jssc 5.0 beta5</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650288/5961215/1/item.html</link><content:encoded>&lt;p&gt;下载页面：&lt;a href=&quot;http://code.google.com/p/jssc/downloads/list&quot;&gt;http://code.google.com/p/jssc/downloads/list&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;源码地址：&lt;a href=&quot;http://jssc.googlecode.com/svn/trunk/&quot;&gt;http://jssc.googlecode.com/svn/trunk/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;预览效果：&lt;a href=&quot;http://army8735.org/wp-content/uploads/jssc/&quot;&gt;http://army8735.org/wp-content/uploads/jssc/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;全部是细节方面的调整。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;性能有所略微的提升，体积稍微减少一点，可能整体没啥感觉。因为性能瓶颈主要在两方面：as的分析阶段和js的显示阶段。分析阶段中主要是词法分析阶段和字符串拼接阶段，调整的是词法分析阶段，而这个部分占整体所耗时间不是最多的，所以提升不明显。&lt;/p&gt;
&lt;p&gt;接口稍微改了下，具体的看源代码。php由原本的纯php代码变为了内嵌显示方式，像html那样。&lt;/p&gt;
&lt;p&gt;这个可能是最后一个beta版了，rc如果放出基本都是针对bug的修补，另外会全面更新wiki的使用方法。&lt;/p&gt;
&lt;p&gt;说下5.x系列的计划吧：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先是增加语言：诸如jsp、ruby、csharp等等。语系的增加不会增加子版本号，如5.1，而是后缀的小版本号——5.0.1。&lt;/li&gt;
&lt;li&gt;新特性增加将以子版本号形式出现，譬如5.1版本首先考虑的是缓存输出优化（针对代码行上万的高亮显示）。&lt;/li&gt;
&lt;li&gt;也是以前考虑实现而没有实现的特性：自动格式化，这个可能难一点，在以后的计划之内吧。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;暂且这么多，有想到新的或者别人的想法再列入计划里。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650288/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650288/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650288/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650288/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2010/01/22/560.html/feed</wfw:commentRss><slash:comments>9</slash:comments><description>下载页面：http://code.google.com/p/jssc/downloads/list
源码地址：http://jssc.googlecode.com/svn/trunk/
预览效果：http://army8735.org/wp-content/uploads/jssc/
全部是细节方面的调整。
性能有所略微的提升，体积稍微减少一点，可能整体没啥感觉。因为性能瓶颈主要在两方面：as的分析阶段和js的显示阶段。分析阶段中主要是词法分析阶段和字符串拼接阶段，调整的是词法分析阶段，而这个部分占整体所耗时间不是最多的，所以提升不明显。
接口稍微改了下，具体的看源代码。php由原本的纯php代码变为了内嵌显示方式，像html那样。
这个可能是最后一个beta版了，rc如果放出基本都是针对bug的修补，另外会全面更新wiki的使用方法。
说下5.x系列的计划吧：

首先是增加语言：诸如jsp、ruby、csharp等等。语系的增加不会增加子版本号，如5.1，而是后缀的小版本号——5.0.1。
新特性增加将以子版本号形式出现，譬如5.1版本首先考虑的是缓存输出优化（针对代码行上万的高亮显示）。
也是以前考虑实现而没有实现的特性：自动格式化，这个可能难一点，在以后的计划之内吧。

暂且这么多，有想到新的或者别人的想法再列入计划里。&lt;img src=&quot;http://www1.feedsky.com/t1/328650288/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650288/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650288/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650288/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>前端开发</category><category>jssc</category><category>beta5</category><pubDate>Fri, 22 Jan 2010 09:28:25 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2010/01/22/560.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=560</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2010/01/22/560.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650288/5961215</fs:itemid></item><item><title>智能JPEG优化技术</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650291/5961215/1/item.html</link><content:encoded>&lt;p&gt;同事发来了一篇&lt;a href=&quot;http://www.smashingmagazine.com/2009/07/01/clever-jpeg-optimization-techniques/&quot; target=&quot;_blank&quot;&gt;《Clever JPEG Optimization Techniques》&lt;/a&gt;，文中提到的部分技术很细致，分享下。&lt;/p&gt;
&lt;p&gt;大部分人在考虑图像压缩优化的时候，还只停留在设置图像处理软件的保存选项上。另外我们也有一些常用的优化工具，诸如OptiPNG和jpegtran。但是还有一些鲜为人知的方法，比如即将介绍的“8像素格优化方法”，它的原理是基于图像数据存储的格式上的。&lt;/p&gt;
&lt;h3&gt;8像素格&lt;/h3&gt;
&lt;p&gt;众所周知，jpeg图像存储是以8像素格为基本单位的，看下图示例：&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;img class=&quot;aligncenter size-full wp-image-547&quot; title=&quot;8pixel-1&quot; src=&quot;http://army8735.org/wp-content/uploads/2010/01/8pixel-1.png&quot; alt=&quot;8pixel-1&quot; width=&quot;236&quot; height=&quot;128&quot; /&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt; 32×32 pixels, Quality: 10 (in Photoshop), 396 bytes.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;两个白色块大小均为8×8像素，图像保存质量为低。可以看出，左上角的方块很清晰，右下角却出现了杂色，这是为什么呢？让我们放大图片并画出参考线格：&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;aligncenter size-full wp-image-548&quot; title=&quot;8pixel-2&quot; src=&quot;http://army8735.org/wp-content/uploads/2010/01/8pixel-2.png&quot; alt=&quot;8pixel-2&quot; width=&quot;527&quot; height=&quot;527&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看出，左上角的方块恰好在8×8格子里面（占据4个），而右下角的却横跨了9个格子，除了中间部分占满一个格子外，周围8个都只占据一部分。&lt;/p&gt;
&lt;p&gt;由于jpeg存储算法中，每8×8个像素格是单独进行优化的，算法会寻找这个基本单位格中的均色（jpeg是以颜色正弦波编码）。因此，图像处理时应该尽可能考虑到这点，使得元素的位置靠近每个8×8的像素格。&lt;/p&gt;
&lt;p&gt;这个方法使用起来很简单，比如下面这个例子：&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;img class=&quot;aligncenter size-full wp-image-549&quot; title=&quot;8grid-bad&quot; src=&quot;http://army8735.org/wp-content/uploads/2010/01/8grid-bad.jpg&quot; alt=&quot;8grid-bad&quot; width=&quot;550&quot; height=&quot;230&quot; /&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;13.51 KB&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;img class=&quot;aligncenter size-full wp-image-550&quot; title=&quot;8grid-good&quot; src=&quot;http://army8735.org/wp-content/uploads/2010/01/8grid-good.jpg&quot; alt=&quot;8grid-good&quot; width=&quot;550&quot; height=&quot;230&quot; /&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt;12.65 KB&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;第一张图片中，微波炉的位置是随意放的，而第二张却经过了细微的调整。两者存储的质量相同，都是55。让我们放大点看，红线是参考线：&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;aligncenter size-full wp-image-551&quot; title=&quot;8grid-zoom&quot; src=&quot;http://army8735.org/wp-content/uploads/2010/01/8grid-zoom.png&quot; alt=&quot;8grid-zoom&quot; width=&quot;418&quot; height=&quot;628&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看到，在略微移动了几个像素之后，图像减少了大约1 KB，并且也更清晰了一点。&lt;/p&gt;
&lt;h3&gt;颜色优化&lt;/h3&gt;
&lt;p&gt;这部分主要介绍不常用的图片存储格式，它主要应用在电视上面，暂不介绍。&lt;/p&gt;
&lt;h3&gt;常见的JPEG优化方法&lt;/h3&gt;
&lt;p&gt;这里介绍一些常用的优化方法。&lt;/p&gt;
&lt;p&gt;JPEG算法很严格，唯一的压缩准则是图像软件设置里的质量选项。你可能在Photoshop中存质量为55～60的图片，但是在其它软件中存80质量才拥有同样的尺寸和外观。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一定不要用100的质量来保存图片！&lt;/strong&gt;这其实并不是最高的质量值，因为这只是个数学理论上限，如果你非要质量很高的图片的话，一般存到95就够了，5点的质量丢失几乎没有区别。&lt;/p&gt;
&lt;p&gt;注意Photoshop中低于50质量的图片保存。因为在50之下的时候，jpeg优化会启动一个附加的算法——color down-sampling——它将均衡相邻的8×8像素格的颜色。&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;img class=&quot;aligncenter size-full wp-image-552&quot; title=&quot;q50&quot; src=&quot;http://army8735.org/wp-content/uploads/2010/01/q50.jpg&quot; alt=&quot;q50&quot; width=&quot;240&quot; height=&quot;240&quot; /&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt; 48×48 pixels, Quality: 50 (in Photoshop), 530 bytes.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;img class=&quot;aligncenter size-full wp-image-553&quot; title=&quot;q51&quot; src=&quot;http://army8735.org/wp-content/uploads/2010/01/q51.jpg&quot; alt=&quot;q51&quot; width=&quot;240&quot; height=&quot;240&quot; /&gt;&lt;span style=&quot;color: #ff0000;&quot;&gt; 48×48 pixels, Quality: 51 (in Photoshop), 484 bytes.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;高一个质量反而更好更小。所以，如果图片拥有小尺寸、高差别的情况，请至少在Photoshop中保存质量为51。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650291/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650291/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650291/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650291/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2010/01/07/546.html/feed</wfw:commentRss><slash:comments>8</slash:comments><description>同事发来了一篇《Clever JPEG Optimization Techniques》，文中提到的部分技术很细致，分享下。
大部分人在考虑图像压缩优化的时候，还只停留在设置图像处理软件的保存选项上。另外我们也有一些常用的优化工具，诸如OptiPNG和jpegtran。但是还有一些鲜为人知的方法，比如即将介绍的“8像素格优化方法”，它的原理是基于图像数据存储的格式上的。
8像素格
众所周知，jpeg图像存储是以8像素格为基本单位的，看下图示例：
 32×32 pixels, Quality: 10 (in Photoshop), 396 bytes.
两个白色块大小均为8×8像素，图像保存质量为低。可以看出，左上角的方块很清晰，右下角却出现了杂色，这是为什么呢？让我们放大图片并画出参考线格：

可以看出，左上角的方块恰好在8×8格子里面（占据4个），而右下角的却横跨了9个格子，除了中间部分占满一个格子外，周围8个都只占据一部分。
由于jpeg存储算法中，每8×8个像素格是单独进行优化的，算法会寻找这个基本单位格中的均色（jpeg是以颜色正弦波编码）。因此，图像处理时应该尽可能考虑到这点，使得元素的位置靠近每个8×8的像素格。
这个方法使用起来很简单，比如下面这个例子：
13.51 KB
12.65 KB
第一张图片中，微波炉的位置是随意放的，而第二张却经过了细微的调整。两者存储的质量相同，都是55。让我们放大点看，红线是参考线：

可以看到，在略微移动了几个像素之后，图像减少了大约1 KB，并且也更清晰了一点。
颜色优化
这部分主要介绍不常用的图片存储格式，它主要应用在电视上面，暂不介绍。
常见的JPEG优化方法
这里介绍一些常用的优化方法。
JPEG算法很严格，唯一的压缩准则是图像软件设置里的质量选项。你可能在Photoshop中存质量为55～60的图片，但是在其它软件中存80质量才拥有同样的尺寸和外观。
一定不要用100的质量来保存图片！这其实并不是最高的质量值，因为这只是个数学理论上限，如果你非要质量很高的图片的话，一般存到95就够了，5点的质量丢失几乎没有区别。
注意Photoshop中低于50质量的图片保存。因为在50之下的时候，jpeg优化会启动一个附加的算法——color down-sampling——它将均衡相邻的8×8像素格的颜色。
 48×48 pixels, Quality: 50 (in Photoshop), 530 bytes.
 48×48 pixels, Quality: 51 (in Photoshop), 484 bytes.
高一个质量反而更好更小。所以，如果图片拥有小尺寸、高差别的情况，请至少在Photoshop中保存质量为51。&lt;img src=&quot;http://www1.feedsky.com/t1/328650291/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650291/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650291/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650291/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>其它</category><category>jpeg</category><category>前端开发</category><category>翻译</category><category>优化</category><pubDate>Thu, 07 Jan 2010 11:09:16 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2010/01/07/546.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=546</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2010/01/07/546.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650291/5961215</fs:itemid></item><item><title>特性检测并非浏览器检测</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650294/5961215/1/item.html</link><content:encoded>&lt;p&gt;NCZ在他的同名博客&lt;a href=&quot;http://www.nczonline.net/blog/2009/12/29/feature-detection-is-not-browser-detection/&quot; target=&quot;_blank&quot;&gt;《Feature detection is not browser detection》&lt;/a&gt;中，讲述了一直以来前端开发中的一个热门技术——检测用户的浏览器平台，并详细地叙说历史发展以及各种办法的优缺点。我大致翻译了部分文章，可能有理解错误的地方，敬请指正。值得一提的是，评论部分的争论亦值得一看。&lt;/p&gt;
&lt;h3&gt;特性检测&lt;/h3&gt;
&lt;p&gt;起初前端工程师们就极力反对浏览器检测，他们认为类似&lt;span style=&quot;color: #ff0000;&quot;&gt;user-agent嗅探&lt;/span&gt;的方法是很不好的，理由是它并不是一种面向未来的代码，无法适应新版的浏览器。更好的做法是使用&lt;span style=&quot;color: #ff0000;&quot;&gt;特性检测&lt;/span&gt;，就像这样：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;if (navigator.userAgent.indexOf(&quot;MSIE 7&quot;) &amp;gt; -1){
    //do something
}&lt;/pre&gt;
&lt;p&gt;而更好的做法是这样：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;if(document.all){
    //do something
}&lt;/pre&gt;
&lt;p&gt;这两种方式并不相同。前者是检测浏览器的特殊名称和版本；后者却是检测浏览器的特性。UA嗅探能够精确得到浏览器的类型和版本（至少能得知浏览器类型），而特性检测却是去确定浏览器是否拥有某个对象或者支持某个方法。注意这两者是完全不同的。&lt;/p&gt;
&lt;p&gt;因为特性检测依赖于哪些浏览器支持，当出现新版本浏览器的时候需要繁琐的确认工作。例如DOM标准刚出现的时候，并不是所有浏览器都支持&lt;span style=&quot;color: #ff0000;&quot;&gt;getElementById()&lt;/span&gt;方法，所以一开始代码可能是这样：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;if(document.getElementById){  //DOM
    element = document.getElementById(id);
} else if (document.all) {  //IE
    element = document.all[id];
} else if (document.layers){  //Netscape &amp;lt; 6
    element = document.layers[id];
}&lt;/pre&gt;
&lt;p&gt;这是特性检测很好的一个例子，亮点在于当其它浏览器开始支持&lt;span style=&quot;color: #ff0000;&quot;&gt;getElementById()&lt;/span&gt;方法时不必修改代码。&lt;/p&gt;
&lt;h3&gt;混合方式&lt;/h3&gt;
&lt;p&gt;后来前端工程师们考虑改进的写法，代码变化成这样：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;//AVOID!!!
if (document.all) {  //IE
    id = document.uniqueID;
} else {
    id = Math.random();
}&lt;/pre&gt;
&lt;p&gt;这个代码的问题是通过检测&lt;span style=&quot;color: #ff0000;&quot;&gt;document.all&lt;/span&gt;属性来确定是否是IE。当确定是IE后，假定使用私有的&lt;span style=&quot;color: #ff0000;&quot;&gt;document.uniqueID&lt;/span&gt;属性也是安全的。然而，目前所作的只是确定是否支持&lt;span style=&quot;color: #ff0000;&quot;&gt;document.all&lt;/span&gt;，并非是去辨识浏览器是否为IE。仅仅支持&lt;span style=&quot;color: #ff0000;&quot;&gt;document.all&lt;/span&gt;的话也不意味着&lt;span style=&quot;color: #ff0000;&quot;&gt;document.uniqueID&lt;/span&gt;是可用的。&lt;/p&gt;
&lt;p&gt;后来人们开始这样写，用下面那行代替上面的：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var isIE = navigator.userAgent.indexOf(&quot;MSIE&quot;) &amp;gt; -1;
//下面这行代替上面那行
var isIE = !!document.all;&lt;/pre&gt;
&lt;p&gt;这些变化说明大家对“不要使用UA嗅探”存在误解——不再对浏览器的详细信息进行检测，取而代之的是通过特性的支持来推断。这种&lt;strong&gt;基于浏览器特性检测&lt;/strong&gt;的方式非常不好。&lt;/p&gt;
&lt;p&gt;后来前端们发现&lt;span style=&quot;color: #ff0000;&quot;&gt;document.all&lt;/span&gt;并不可靠，更好的检测IE变为：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;var isIE = !!document.all &amp;amp;&amp;amp; document.uniqueID;&lt;/pre&gt;
&lt;p&gt;这种实现方式陷入歧途。不仅需要费时费事地去识别浏览器所增加的特性支持，另外也不能确定其它浏览器开始支持相同的特性。&lt;/p&gt;
&lt;p&gt;如果你认为这样的代码并未被广泛使用，那么看看来自于老版本的Mootools代码片段吧：&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;//from MooTools 1.1.2
if (window.ActiveXObject) window.ie = window[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true;
else if (document.childNodes &amp;amp;&amp;amp; !document.all &amp;amp;&amp;amp; !navigator.taintEnabled) window.webkit = window[window.xpath ? 'webkit420' : 'webkit419'] = true;
else if (document.getBoxObjectFor != null || window.mozInnerScreenX != null) window.gecko = true;&lt;/pre&gt;
&lt;p&gt;注意它是如何使用特性检测的。我可以指出它一系列的问题，比如通过检测&lt;span style=&quot;color: #ff0000;&quot;&gt;window.ie&lt;/span&gt;会将ie8误认为ie7。&lt;/p&gt;
&lt;h3&gt;余波&lt;/h3&gt;
&lt;p&gt;随着浏览器的快速发展，使用特性检测变得越来越困难和不可靠。但是Mootools 1.2.4仍然使用这一方法，例如：&lt;span style=&quot;color: #ff0000;&quot;&gt;getBoxObjectFor()&lt;/span&gt;。&lt;/p&gt;
&lt;pre class=&quot;brush:js&quot;&gt;//from MooTools 1.2.4
var Browser = $merge({

	Engine: {name: 'unknown', version: 0},

	Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},

	Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},

	Plugins: {},

	Engines: {

		presto: function(){
			return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
		},

		trident: function(){
			return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
		},

		webkit: function(){
			return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
		},

		gecko: function(){
			return (!document.getBoxObjectFor &amp;amp;&amp;amp; window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
		}

	}

}, Browser || {});&lt;/pre&gt;
&lt;h3&gt;应该怎么做？&lt;/h3&gt;
&lt;p&gt;特性检测是个应该避免的方法，尽管直接进行特性检测是个很好的方法，并且大部分情况下能满足需求。一般只要在检测前知道这个特性是否被实现即可，而不会去考虑它们之间的关系。&lt;/p&gt;
&lt;p&gt;我并非是说永远不使用浏览器特性检测而是基于UA嗅探，因为我相信它还是有很多用途的，然而我不相信它有很多合理的用途。如果你考虑UA嗅探的话，请先贯彻这一思想：唯一安全的方式是针对特定浏览器的特定版本，超出范围之外都是不可靠的——例如新出的浏览器版本。其实这样做也是个明智的办法，因为相较于向前兼容不确定的新版本而言，向后兼容老版本是最简单的做法。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650294/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650294/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650294/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650294/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2010/01/01/534.html/feed</wfw:commentRss><slash:comments>3</slash:comments><description>NCZ在他的同名博客《Feature detection is not browser detection》中，讲述了一直以来前端开发中的一个热门技术——检测用户的浏览器平台，并详细地叙说历史发展以及各种办法的优缺点。我大致翻译了部分文章，可能有理解错误的地方，敬请指正。值得一提的是，评论部分的争论亦值得一看。
特性检测
起初前端工程师们就极力反对浏览器检测，他们认为类似user-agent嗅探的方法是很不好的，理由是它并不是一种面向未来的代码，无法适应新版的浏览器。更好的做法是使用特性检测，就像这样：
if (navigator.userAgent.indexOf(&quot;MSIE 7&quot;) &amp;#62; -1){
    //do something
}
而更好的做法是这样：
if(document.all){
    //do something
}
这两种方式并不相同。前者是检测浏览器的特殊名称和版本；后者却是检测浏览器的特性。UA嗅探能够精确得到浏览器的类型和版本（至少能得知浏览器类型），而特性检测却是去确定浏览器是否拥有某个对象或者支持某个方法。注意这两者是完全不同的。
因为特性检测依赖于哪些浏览器支持，当出现新版本浏览器的时候需要繁琐的确认工作。例如DOM标准刚出现的时候，并不是所有浏览器都支持getElementById()方法，所以一开始代码可能是这样：
if(document.getElementById){  //DOM
    element = document.getElementById(id);
} else if (document.all) {  //IE
    element = document.all[id];
} else if (document.layers){  //Netscape &amp;#60; 6
    element = document.layers[id];
}
这是特性检测很好的一个例子，亮点在于当其它浏览器开始支持getElementById()方法时不必修改代码。
混合方式
后来前端工程师们考虑改进的写法，代码变化成这样：
//AVOID!!!
if (document.all) { [...]&lt;img src=&quot;http://www1.feedsky.com/t1/328650294/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650294/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650294/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650294/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>前端开发</category><category>翻译</category><category>浏览器检测</category><category>特性检测</category><pubDate>Fri, 01 Jan 2010 11:16:29 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2010/01/01/534.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=534</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2010/01/01/534.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650294/5961215</fs:itemid></item><item><title>删除数组的重复项</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650297/5961215/1/item.html</link><content:encoded>&lt;p&gt;怿飞在同名文章&lt;a href=&quot;http://www.planabc.net/2009/12/26/array_uniq/&quot; target=&quot;_blank&quot;&gt;《删除数组中重复项（uniq）》&lt;/a&gt;中分享了他的方法，时间复杂度和空间复杂度均为O（n），但还无法解决弱类型的问题。凑巧我也思考过类似的方法，在此基础上修改一下，可以KO弱类型问题。&lt;/p&gt;
&lt;pre class=&quot;brush:html&quot;&gt;&amp;lt;script src=&quot;mootools-1.2.4-core-nc.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
Array.implement({
	distinct: function() {
		if(this.length &lt; 2) {
			return this;
		}
		var hash = new Hash(), value;
		outer:
		for(var i = 0, len = this.length; i &lt; len; i++) {
			if(hash.has(this[i])) {
				value = hash.get(this[i]);
				for(var j = 0, len2 = value.length; j &lt; len2; j++) {
					if($type(this[i]) == value[j]) {
						this.splice(i, 1);
						i--;
						len--;
						continue outer;
					}
				}
				value.push($type(this[i]));
			}
			else {
				hash.set(this[i], [$type(this[i])]);
			}
		}
		return this;
	}
});
var arr = [0, 0, &quot;0&quot;, '0', false, null, NaN, undefined, 1, &quot;1&quot;, true];
alert(arr.distinct().join(&quot;, &quot;)); //[0, &quot;0&quot;, false, null, NaN, undefined, 1, &quot;1&quot;, true]
&amp;lt;/script&amp;gt;&lt;/pre&gt;
&lt;p&gt;基于mootools，我为Array类提供一个distinct方法，以供删除重复项。&lt;/p&gt;
&lt;p&gt;基本思想是：遍历数组，建立一个hash用以统计出现过的每条项目。不过hash的key是数组每项的toString()值，value却是个类型数组。每遍历数组中的一个值时，首先检查hash中是否存在key，不存在设置value为一个新数组——并且里面有唯一的类型值（这个数组项的类型）；如果hash中存在key，则遍历value，查找此数组项的类型是否在value中，在则说明重复，不在将类型push到value里面。&lt;/p&gt;
&lt;p&gt;这样就解决了弱类型的问题，但如果数组中不仅仅有基本类型，还有数组、对象、arguments、hash等，则会更加复杂。&lt;/pre&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650297/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650297/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650297/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650297/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2009/12/27/523.html/feed</wfw:commentRss><slash:comments>9</slash:comments><description>怿飞在同名文章《删除数组中重复项（uniq）》中分享了他的方法，时间复杂度和空间复杂度均为O（n），但还无法解决弱类型的问题。凑巧我也思考过类似的方法，在此基础上修改一下，可以KO弱类型问题。
&amp;#60;script src=&quot;mootools-1.2.4-core-nc.js&quot;&amp;#62;&amp;#60;/script&amp;#62;
&amp;#60;script&amp;#62;
Array.implement({
	distinct: function() {
		if(this.length &lt; 2) {
			return this;
		}
		var hash = new Hash(), value;
		outer:
		for(var i = 0, len = this.length; i &lt; len; i++) {
			if(hash.has(this[i])) {
				value = hash.get(this[i]);
				for(var j = 0, len2 = value.length; j &lt; len2; j++) {
					if($type(this[i]) == value[j]) {
						this.splice(i, 1);
						i--;
						len--;
						continue outer;
					}
				}
				value.push($type(this[i]));
			}
			else {
				hash.set(this[i], [$type(this[i])]);
			}
		}
		return this;
	}
});
var arr = [0, 0, &quot;0&quot;, '0', false, null, NaN, [...]&lt;img src=&quot;http://www1.feedsky.com/t1/328650297/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650297/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650297/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650297/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>distinct</category><category>前端开发</category><pubDate>Sun, 27 Dec 2009 18:29:14 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2009/12/27/523.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=523</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2009/12/27/523.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650297/5961215</fs:itemid></item><item><title>960gs中浮动清除新变化</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650300/5961215/1/item.html</link><content:encoded>&lt;p&gt;昨天下了新版本&lt;a href=&quot;http://960.gs/&quot; target=&quot;_blank&quot;&gt;960gs&lt;/a&gt;，瞅瞅里面有无新的变化，正巧发现其中.clearfix的新变化：&lt;/p&gt;
&lt;pre class=&quot;brush:css&quot;&gt;/* `Clear Floated Elements
----------------------------------------------------------------------------------------------------*/

/* http://sonspring.com/journal/clearing-floats */

.clear {
	clear: both;
	display: block;
	overflow: hidden;
	visibility: hidden;
	width: 0;
	height: 0;
}

/* http://perishablepress.com/press/2009/12/06/new-clearfix-hack */

.clearfix:after {
	clear: both;
	content: ' ';
	display: block;
	font-size: 0;
	line-height: 0;
	visibility: hidden;
	width: 0;
	height: 0;
}

/*
	The following zoom:1 rule is specifically for IE6 + IE7.
	Move to separate stylesheet if invalid CSS is a problem.
*/
* html .clearfix,
*:first-child+html .clearfix {
	zoom: 1;
}&lt;/pre&gt;
&lt;p&gt;关键在于最后几行的更新，用单独的样式为ie6和ie7设置了hack（应该是zoom:1触发hasLayout），来解决极少数情况下浮动清除的bug。至于争议性的.clearfix到底用不用，则实在是个鱼与熊掌的问题。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650300/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650300/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650300/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650300/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2009/12/24/520.html/feed</wfw:commentRss><slash:comments>0</slash:comments><description>昨天下了新版本960gs，瞅瞅里面有无新的变化，正巧发现其中.clearfix的新变化：
/* `Clear Floated Elements
----------------------------------------------------------------------------------------------------*/

/* http://sonspring.com/journal/clearing-floats */

.clear {
	clear: both;
	display: block;
	overflow: hidden;
	visibility: hidden;
	width: 0;
	height: 0;
}

/* http://perishablepress.com/press/2009/12/06/new-clearfix-hack */

.clearfix:after {
	clear: both;
	content: ' ';
	display: block;
	font-size: 0;
	line-height: 0;
	visibility: hidden;
	width: 0;
	height: 0;
}

/*
	The following zoom:1 rule is specifically for IE6 + IE7.
	Move to separate stylesheet if invalid CSS is a problem.
*/
* html .clearfix,
*:first-child+html .clearfix {
	zoom: 1;
}
关键在于最后几行的更新，用单独的样式为ie6和ie7设置了hack（应该是zoom:1触发hasLayout），来解决极少数情况下浮动清除的bug。至于争议性的.clearfix到底用不用，则实在是个鱼与熊掌的问题。&lt;img src=&quot;http://www1.feedsky.com/t1/328650300/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650300/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650300/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650300/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>960gs</category><category>前端开发</category><category>clearfix</category><pubDate>Thu, 24 Dec 2009 14:28:18 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2009/12/24/520.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=520</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2009/12/24/520.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650300/5961215</fs:itemid></item><item><title>JAse预览版</title><link>http://item.feedsky.com/~feedsky/army8735/~7851743/328650307/5961215/1/item.html</link><content:encoded>&lt;p&gt;很高兴能为大家带来&lt;a href=&quot;http://code.google.com/p/jase/&quot; target=&quot;_blank&quot;&gt;JAse&lt;/a&gt;的预览版，一款基于as+js的网页语法编辑器。在经历了&lt;a href=&quot;http://code.google.com/p/jssc/&quot; target=&quot;_blank&quot;&gt;jssc&lt;/a&gt;的静态DFA语法解析高亮的研究和&lt;a href=&quot;http://code.google.com/p/jate/&quot; target=&quot;_blank&quot;&gt;JAte&lt;/a&gt;文本编辑器的失败之后，JAse终于能够吸取两者的经验和不足，慢慢地开发至今。开头不多说了，来看预览地址（暂且只有js解析器，其它的都是不存在的；基于Flash Player 10）。&lt;/p&gt;
&lt;p&gt;两个都可以：&lt;a href=&quot;http://jase.googlecode.com/svn/trunk/jase1/bin/index.html&quot;&gt;http://jase.googlecode.com/svn/trunk/jase1/bin/index.html&lt;/a&gt;；&lt;a href=&quot;http://army8735.org/wp-content/uploads/jase/&quot;&gt;http://army8735.org/wp-content/uploads/jase/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;它的下载地址：&lt;a href=&quot;http://code.google.com/p/jase/downloads/list&quot;&gt;http://code.google.com/p/jase/downloads/list&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;它的svn：&lt;a href=&quot;http://jase.googlecode.com/svn/trunk/jase1/&quot;&gt;http://jase.googlecode.com/svn/trunk/jase1/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;目前只能支撑千行级代码编辑，总体性能并不是卡在解析器上，而是卡在flash本身的input上。有过经验的人可能感受颇深，何时adobe能提供高性能的textfield？&lt;/p&gt;
&lt;p&gt;JAse基本内容分为两块：编辑器提供基础编辑功能、解析器以插件形式提供高亮功能。两者之间通过一个接口连接，也就是说所有的外部解析器必须实现IParser接口，编辑器每次的更改也都是调用接口方法。如此实现了扩展语法的功能，只要实现了接口，你可以开发任意的高亮程序。当然，我自己也写了个AbstractParser基类，可以方便地在其基础之上拓展。&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;编辑器的undo、redo等功能还未和解析器链接上，因此使用这些button的话可能会造成高亮错误，这并不是程序本身原因。预览版放出只是展示基本输入修改已经ok了。其它将会在后续版本中逐渐开发。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;JAse的整体流程是：寻找目标textarea（如果指定id直接获取，否则以document上第一个textarea为目标）=&amp;gt;隐藏textarea，将自己替换掉它的位置=&amp;gt;加载外部解析器（如果指定syntax直接加载，否则出现界面选择）=&amp;gt;编辑内容=&amp;gt;提交内容（如果指定url则使用urlloader，否则将内容放回textarea并寻找父级form提交）。&lt;/p&gt;
&lt;p&gt;其间动态破损、修复、显示模型经历过3次算法变更得以成型，斜线动态区分perl正则和除法的算法也经历过2次变更，让我用图来简单说下吧。&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;aligncenter size-full wp-image-499&quot; title=&quot;jase-preview-1&quot; src=&quot;http://army8735.org/wp-content/uploads/2009/12/jase-preview-1.png&quot; alt=&quot;jase-preview-1&quot; width=&quot;481&quot; height=&quot;220&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在这种情况下，每行代码首先会被解析开始状态（如注释、字符串或者普通），同时渲染标明此行是否被高亮过。这两个状态List是和每行代码一一对应的，增加随之增加，修改随之修改，删除随之删除。而区块List则完全脱离于代码行索引，自成一家。&lt;/p&gt;
&lt;p&gt;这样做的好处是分析和显示互相分开，每次编辑器内容被修改时先有破损和修复模型来完成行状态、渲染状态和区块的操作；然后由显示模型取得可视区域再从区块List中取出索引对每块进行高亮。&lt;/p&gt;
&lt;p&gt;但坏处也很明显，每次操作对 区块List的改动太大了，前面增加一个字符，会导致所有后面的索引进行修改（自增1），因此很快就被废除，有了第2种想法。&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;aligncenter size-full wp-image-500&quot; title=&quot;jase-preview-2&quot; src=&quot;http://army8735.org/wp-content/uploads/2009/12/jase-preview-2.png&quot; alt=&quot;jase-preview-2&quot; width=&quot;321&quot; height=&quot;220&quot; /&gt;&lt;/p&gt;
&lt;p&gt;废除区块索引。每次编辑器内容修改，都去计算行状态并且设置涉及行的渲染状态为否以便重新渲染。这个算法对分析阶段来说有了极大的便利，但是在显示阶段却叫苦不迭——在渲染每一行代码的时候，都要根据行状态重新对此行代码进行一遍DFA解析，而这个工作在前面的分析阶段已经做过了（大部分）。同样的功能需要两份代码显得冗余，要合并到一起的话又会变得难以维护，而且多分析一次也显得没有必要。&lt;/p&gt;
&lt;p&gt;在经历了一段时间的纠结后，最终还是变更到现在这个样子。&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;aligncenter size-full wp-image-503&quot; title=&quot;jase-preview-3&quot; src=&quot;http://army8735.org/wp-content/uploads/2009/12/jase-preview-31.png&quot; alt=&quot;jase-preview-3&quot; width=&quot;449&quot; height=&quot;204&quot; /&gt;&lt;/p&gt;
&lt;p&gt;区块List成为二维的，亦和每行对应。然后List中的List保存索引。不过和第1种方式略有不同，之前保存的是绝对索引（字符串的下标），而这里保存的是相对索引（相对于行代码第一个字符而言）。这样每次渲染的时候，只要知道行代码的第一个字符索引，然后遍历这行对应的区块List，相加得出绝对索引即可完成显示。而且每次修改后，后面行的相对索引都无需变更，综合了以上两种方法的优点。&lt;/p&gt;
&lt;p&gt;至于斜线的动态区分，最终情况和这类似。静态DFA解析的情况下很容易确定一个斜线的含义究竟是除法还是正则——因为源代码扫猫是从头至尾的。动态情况下因为不确定性，不可能每次修改都从头遍历代码，代价太大了，最好能够从当前行或者前面几行判断出。最初我采用向前回溯的方式，这显得很愚蠢，而且前面行还可能存在单行注释、多行注释等情况干扰（词法分析无需考虑，因为注释空白一开始就被剔除了），基本上行不通。&lt;/p&gt;
&lt;p&gt;后来我为每行增加一个布尔值来标明行起始状态如果出现斜线的话它的含义是什么，每次内容修改也会根据解析情况重置它，就和重置行状态和是否被渲染一样。&lt;/p&gt;
&lt;p&gt;另外曾经让我非常头疼的一个地方是事件侦听部分。我希望的是每次编辑器内容修改时，所触发的侦听在修改后立刻执行，而as中所有的事件侦听都是在之前执行。这导致使用解析器进行分析显示的时候，实际上代码还根本未发生改变，显然这是个悖论。唯一的做法就是将侦听中模拟事件率先执行，然后通过preventDefault()来取消默认事件发生。这里有个问题，那就是像删除这样的事件（无论del还是backspace或者选区替换）是无法被取消的。所幸的是最终这些难题被一一解决——包括TAB键不能使用，感兴趣的可以查看编辑器源文件来了解是怎么做到的。只关心外部解析器的话不用考虑。&lt;/p&gt;
&lt;p&gt;试用过程中有任何问题、反馈或者建议请不吝提出。在beta版完成之后，也会出系列文章来详细介绍JAse的所有技术，就像&lt;a href=&quot;http://army8735.org/2009/11/02/236.html&quot; target=&quot;_blank&quot;&gt;jssc&lt;/a&gt;一样。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/328650307/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650307/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650307/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650307/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content:encoded><wfw:commentRss>http://army8735.org/2009/12/17/498.html/feed</wfw:commentRss><slash:comments>1</slash:comments><description>很高兴能为大家带来JAse的预览版，一款基于as+js的网页语法编辑器。在经历了jssc的静态DFA语法解析高亮的研究和JAte文本编辑器的失败之后，JAse终于能够吸取两者的经验和不足，慢慢地开发至今。开头不多说了，来看预览地址（暂且只有js解析器，其它的都是不存在的；基于Flash Player 10）。
两个都可以：http://jase.googlecode.com/svn/trunk/jase1/bin/index.html；http://army8735.org/wp-content/uploads/jase/
它的下载地址：http://code.google.com/p/jase/downloads/list
它的svn：http://jase.googlecode.com/svn/trunk/jase1/
目前只能支撑千行级代码编辑，总体性能并不是卡在解析器上，而是卡在flash本身的input上。有过经验的人可能感受颇深，何时adobe能提供高性能的textfield？
JAse基本内容分为两块：编辑器提供基础编辑功能、解析器以插件形式提供高亮功能。两者之间通过一个接口连接，也就是说所有的外部解析器必须实现IParser接口，编辑器每次的更改也都是调用接口方法。如此实现了扩展语法的功能，只要实现了接口，你可以开发任意的高亮程序。当然，我自己也写了个AbstractParser基类，可以方便地在其基础之上拓展。
编辑器的undo、redo等功能还未和解析器链接上，因此使用这些button的话可能会造成高亮错误，这并不是程序本身原因。预览版放出只是展示基本输入修改已经ok了。其它将会在后续版本中逐渐开发。
JAse的整体流程是：寻找目标textarea（如果指定id直接获取，否则以document上第一个textarea为目标）=&amp;#62;隐藏textarea，将自己替换掉它的位置=&amp;#62;加载外部解析器（如果指定syntax直接加载，否则出现界面选择）=&amp;#62;编辑内容=&amp;#62;提交内容（如果指定url则使用urlloader，否则将内容放回textarea并寻找父级form提交）。
其间动态破损、修复、显示模型经历过3次算法变更得以成型，斜线动态区分perl正则和除法的算法也经历过2次变更，让我用图来简单说下吧。

在这种情况下，每行代码首先会被解析开始状态（如注释、字符串或者普通），同时渲染标明此行是否被高亮过。这两个状态List是和每行代码一一对应的，增加随之增加，修改随之修改，删除随之删除。而区块List则完全脱离于代码行索引，自成一家。
这样做的好处是分析和显示互相分开，每次编辑器内容被修改时先有破损和修复模型来完成行状态、渲染状态和区块的操作；然后由显示模型取得可视区域再从区块List中取出索引对每块进行高亮。
但坏处也很明显，每次操作对 区块List的改动太大了，前面增加一个字符，会导致所有后面的索引进行修改（自增1），因此很快就被废除，有了第2种想法。

废除区块索引。每次编辑器内容修改，都去计算行状态并且设置涉及行的渲染状态为否以便重新渲染。这个算法对分析阶段来说有了极大的便利，但是在显示阶段却叫苦不迭——在渲染每一行代码的时候，都要根据行状态重新对此行代码进行一遍DFA解析，而这个工作在前面的分析阶段已经做过了（大部分）。同样的功能需要两份代码显得冗余，要合并到一起的话又会变得难以维护，而且多分析一次也显得没有必要。
在经历了一段时间的纠结后，最终还是变更到现在这个样子。

区块List成为二维的，亦和每行对应。然后List中的List保存索引。不过和第1种方式略有不同，之前保存的是绝对索引（字符串的下标），而这里保存的是相对索引（相对于行代码第一个字符而言）。这样每次渲染的时候，只要知道行代码的第一个字符索引，然后遍历这行对应的区块List，相加得出绝对索引即可完成显示。而且每次修改后，后面行的相对索引都无需变更，综合了以上两种方法的优点。
至于斜线的动态区分，最终情况和这类似。静态DFA解析的情况下很容易确定一个斜线的含义究竟是除法还是正则——因为源代码扫猫是从头至尾的。动态情况下因为不确定性，不可能每次修改都从头遍历代码，代价太大了，最好能够从当前行或者前面几行判断出。最初我采用向前回溯的方式，这显得很愚蠢，而且前面行还可能存在单行注释、多行注释等情况干扰（词法分析无需考虑，因为注释空白一开始就被剔除了），基本上行不通。
后来我为每行增加一个布尔值来标明行起始状态如果出现斜线的话它的含义是什么，每次内容修改也会根据解析情况重置它，就和重置行状态和是否被渲染一样。
另外曾经让我非常头疼的一个地方是事件侦听部分。我希望的是每次编辑器内容修改时，所触发的侦听在修改后立刻执行，而as中所有的事件侦听都是在之前执行。这导致使用解析器进行分析显示的时候，实际上代码还根本未发生改变，显然这是个悖论。唯一的做法就是将侦听中模拟事件率先执行，然后通过preventDefault()来取消默认事件发生。这里有个问题，那就是像删除这样的事件（无论del还是backspace或者选区替换）是无法被取消的。所幸的是最终这些难题被一一解决——包括TAB键不能使用，感兴趣的可以查看编辑器源文件来了解是怎么做到的。只关心外部解析器的话不用考虑。
试用过程中有任何问题、反馈或者建议请不吝提出。在beta版完成之后，也会出系列文章来详细介绍JAse的所有技术，就像jssc一样。&lt;img src=&quot;http://www1.feedsky.com/t1/328650307/army8735/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/army8735/~7851743/328650307/5961215/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;&lt;p class=&quot;fswww1&quot;&gt;&lt;a href=&quot;http://www1.feedsky.com/r/l/feedsky/army8735/328650307/art01.html&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; ismap=&quot;ismap&quot; src=&quot;http://www1.feedsky.com/r/i/feedsky/army8735/328650307/art01.gif&quot; onerror=&quot;this.style.display='none'&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</description><category>JAse</category><category>preview</category><pubDate>Thu, 17 Dec 2009 12:20:14 +0800</pubDate><author>army8735</author><comments>http://army8735.org/2009/12/17/498.html#comments</comments><guid isPermaLink="false">http://army8735.org/?p=498</guid><dc:creator>army8735</dc:creator><fs:srclink>http://army8735.org/2009/12/17/498.html</fs:srclink><fs:srcfeed>http://www.army8735.org/feed</fs:srcfeed><fs:itemid>feedsky/army8735/~7851743/328650307/5961215</fs:itemid></item></channel></rss>