<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>岁月如歌 &#187; javascript</title>
	<atom:link href="http://lifesinger.org/blog/tag/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://lifesinger.org/blog</link>
	<description>关注用户体验、前端开发，记录生活点滴、岁月足迹。</description>
	<lastBuildDate>Wed, 28 Jul 2010 00:40:47 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>用 JS 枚举质数</title>
		<link>http://lifesinger.org/blog/2010/07/js-prime-number/</link>
		<comments>http://lifesinger.org/blog/2010/07/js-prime-number/#comments</comments>
		<pubDate>Sun, 25 Jul 2010 13:53:51 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[开发]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[prime]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2647</guid>
		<description><![CDATA[酷壳上的这篇文章很诱人：检查素数的正则表达式 翻译成 JavaScript 代码如下： function prime(MAX) { var re = /^(11+?)\1+$/, n, C = '1', s = C, r = [], j = 0; while ((n = (s += C).length) < MAX) { !re.test(s) &#038;&#038; (r[j++] = n); } return r; } alert(prime(10000).length); 作为前端，为了让上面的脚本能在实际页面中应用，还得考虑 脚本在浏览器中的耐心 以及 分时优化处理。 最后，请猛击测试页面：prime-number.html]]></description>
			<content:encoded><![CDATA[<p>酷壳上的这篇文章很诱人：<a href="http://coolshell.cn/?p=2704">检查素数的正则表达式</a><br />
翻译成 JavaScript 代码如下：</p>
<pre>
function prime(MAX) {
    var re = /^(11+?)\1+$/,
        n, C = '1', s = C,
        r = [], j = 0;

    while ((n = (s += C).length) < MAX) {
        !re.test(s) &#038;&#038; (r[j++] = n);
    }
    return r;
}
alert(prime(10000).length);
</pre>
<p>作为前端，为了让上面的脚本能在实际页面中应用，还得考虑 <a href="http://www.planabc.net/2009/02/04/how_long_time_will_javascript_execute_in_browsers/">脚本在浏览器中的耐心</a> 以及 <a href="http://lifesinger.org/blog/2009/08/big-array-processing/">分时优化处理</a>。</p>
<p>最后，请猛击测试页面：<a href="http://lifesinger.github.com/lab/2010/prime-number.html">prime-number.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/07/js-prime-number/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>innerHTML and Script Tags</title>
		<link>http://lifesinger.org/blog/2010/07/innerhtml-and-script-tags/</link>
		<comments>http://lifesinger.org/blog/2010/07/innerhtml-and-script-tags/#comments</comments>
		<pubDate>Mon, 12 Jul 2010 15:01:06 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[开发]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[KISSY]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2632</guid>
		<description><![CDATA[先看源码：innerhtml-and-script-tags.html 注释里已经写得很清楚，不赘述。 特别有趣的是 img onload 方案，很 nice 的 hack, 在写原生小代码时可以派上用场。 KISSY 的解决方案：dom-create.js 里的 setHTML 方法。 应用范例： KISSY.DOM.html('#container', 'some html with script tags&#60;script&#62;alert("Hello, KISSY!");&#60;/script&#62;', true); 文档：dom-create.html]]></description>
			<content:encoded><![CDATA[<p>先看源码：<a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/innerhtml-and-script-tags.html">innerhtml-and-script-tags.html</a></p>
<p>注释里已经写得很清楚，不赘述。<br />
特别有趣的是 img onload 方案，很 nice 的 hack, 在写原生小代码时可以派上用场。</p>
<p>KISSY 的解决方案：<a href="http://kissy.googlecode.com/svn/trunk/src/dom/dom-create.js">dom-create.js</a> 里的 setHTML 方法。<br />
应用范例：</p>
<pre>
KISSY.DOM.html('#container',
               'some html with script tags&lt;script&gt;alert("Hello, KISSY!");&lt;/script&gt;',
               true);
</pre>
<p>文档：<a href="http://kissy.googlecode.com/svn/trunk/docs/dom/dom-create.html#method_html">dom-create.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/07/innerhtml-and-script-tags/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>parseInt 小陷阱</title>
		<link>http://lifesinger.org/blog/2010/05/the-trap-of-parseint/</link>
		<comments>http://lifesinger.org/blog/2010/05/the-trap-of-parseint/#comments</comments>
		<pubDate>Sat, 22 May 2010 05:21:15 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[开发]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[parseInt]]></category>
		<category><![CDATA[toString]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2588</guid>
		<description><![CDATA[看代码： alert(parseInt(0.000001)); alert(parseInt(0.0000001)); 第一条语句输出 0, 第二条语句输出 1, 囧。 继续看代码： alert(parseInt('0.000001')); alert(parseInt('0.0000001')); 都输出 0, 这才符合预期。 查看 ECMA-262 规范，parseInt 会先调用 toString 方法。问题已逐渐清晰： alert(0.000001); alert(0.0000001); 第一条语句原样输出，第二条语句输出 1e-7. 继续翻查 ECMA-262 9.8.1 ToString Applied to the Number Type 一节，恍然大悟： assertEquals("0.00001", (0.00001).toString()); assertEquals("0.000001", (0.000001).toString()); assertEquals("1e-7", (0.0000001).toString()); assertEquals("1.2e-7", (0.00000012).toString()); assertEquals("1.23e-7", (0.000000123).toString()); assertEquals("1e-8", (0.00000001).toString()); assertEquals("1.2e-8", (0.000000012).toString()); 上面是 V8 引擎 number-tostring 的单元测试脚本, 很好地诠释了 ECMA [...]]]></description>
			<content:encoded><![CDATA[<p>看代码：</p>
<pre>
alert(parseInt(0.000001));
alert(parseInt(0.0000001));
</pre>
<p>第一条语句输出 0, 第二条语句输出 1, 囧。</p>
<p>继续看代码：</p>
<pre>
alert(parseInt('0.000001'));
alert(parseInt('0.0000001'));
</pre>
<p>都输出 0, 这才符合预期。</p>
<p>查看 ECMA-262 规范，parseInt 会先调用  toString 方法。问题已逐渐清晰：</p>
<pre>
alert(0.000001);
alert(0.0000001);
</pre>
<p>第一条语句原样输出，第二条语句输出 1e-7.<br />
继续翻查 ECMA-262 9.8.1 ToString Applied to the Number Type 一节，恍然大悟：<span id="more-2588"></span></p>
<pre>
assertEquals("0.00001", (0.00001).toString());
assertEquals("0.000001", (0.000001).toString());
assertEquals("1e-7", (0.0000001).toString());
assertEquals("1.2e-7", (0.00000012).toString());
assertEquals("1.23e-7", (0.000000123).toString());
assertEquals("1e-8", (0.00000001).toString());
assertEquals("1.2e-8", (0.000000012).toString());
</pre>
<p>上面是 V8 引擎 <a href="http://www.google.com/codesearch/p?hl=en#W9JxUuHYyMg/trunk/test/mjsunit/number-tostring.js&#038;q=Number%20toString&#038;exact_package=http://v8.googlecode.com/svn&#038;sa=N&#038;cd=1&#038;ct=rc">number-tostring 的单元测试脚本</a>, 很好地诠释了 ECMA 规范。</p>
<p>小结：<strong>对于小于 1e-6 的数值来说，ToString 时会自动转换为科学计数法。</strong>因此 parseInt 方法，在参数类型不确定时，最好封装一层：</p>
<pre>
function parseInt2(a) {
    if(typeof a === 'number') {
        return Math.floor(a);
    }
    return parseInt(a);
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/05/the-trap-of-parseint/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Array.prototype.slice</title>
		<link>http://lifesinger.org/blog/2010/05/array-prototype-slice/</link>
		<comments>http://lifesinger.org/blog/2010/05/array-prototype-slice/#comments</comments>
		<pubDate>Tue, 04 May 2010 01:32:31 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[开发]]></category>
		<category><![CDATA[array]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[slice]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2581</guid>
		<description><![CDATA[slice 可以用来获取数组片段，它返回新数组，不会修改原数组。 除了正常用法，slice 经常用来将 array-like 对象转换为 true array. 名词解释：array-like object &#8211; 拥有 length 属性的对象，比如 { 0: &#8216;foo&#8217;, length: 1 }, 甚至 { length: &#8216;bar&#8217; }. 最常见的 array-like 对象是 arguments 和 NodeList. 查看 V8 引擎 array.js 的源码，可以将 slice 的内部实现简化为： function slice(start, end) { var len = ToUint32(this.length), result = []; for(var i = start; i < [...]]]></description>
			<content:encoded><![CDATA[<p>slice 可以用来获取数组片段，它返回新数组，不会修改原数组。</p>
<p>除了正常用法，slice 经常用来将 array-like 对象转换为 true array.</p>
<p>名词解释：array-like object &#8211; 拥有 length 属性的对象，比如 { 0: &#8216;foo&#8217;, length: 1 }, 甚至 { length: &#8216;bar&#8217; }. 最常见的 array-like 对象是 arguments 和 NodeList.</p>
<p>查看 V8 引擎<a href="http://www.google.com/codesearch/p?hl=en#W9JxUuHYyMg/trunk/src/array.js"> array.js</a> 的源码，可以将 slice 的内部实现简化为：</p>
<pre>
function slice(start, end) {
    var len = ToUint32(this.length), result = [];
    for(var i = start; i < end; i++) {
        result.push(this[i]);
    }
    return result;
}
</pre>
<p>可以看出，slice 并不需要 this 为 array 类型，只需要有 length 属性即可。并且 length 属性可以不为 number 类型，当不能转换为数值时，ToUnit32(this.length) 返回 0.<br />
<span id="more-2581"></span><br />
对于标准浏览器，上面已经将 slice 的原理解释清楚了。但是恼人的 ie, 总是给我们添乱子：</p>
<pre>
var slice = Array.prototype.slice;
slice.call(); // => IE: Object expected.
slice.call(document.childNodes); // => IE: JScript object expected.
</pre>
<p>以上代码，在 ie 里报错。可恨 IE 的 Trident 引擎不开源，那我们只有猜测了：</p>
<pre>
function ie_slice(start, end) {
    var len = ToUint32(this.length), result = [];

    if(__typeof__ this !== 'JScript Object') throw 'JScript object expected';
    if(this === null) throw 'Oject expected';

    for(var i = start; i < end; i++) {
        result.push(this[i]);
    }
    return result;
}
</pre>
<p>至此，把猥琐的 ie 自圆其说完毕。</p>
<p>关于 slice, 还有一个话题：用 Array.prototype.slice 还是 [].slice ? 从理论上讲，[] 需要创建一个数组，性能上会比 Array.prototype 稍差。但实际上，这两者差不多，就如循环里用 i++ 还是 ++i 一样，纯属个人习惯。</p>
<p>最后一个话题，有关性能。对于数组的筛选来说，有一个牺牲色相的写法：</p>
<pre>
var ret = [];
for(var i = start, j = 0; i < end; i++) {
    ret[j++] = arr[i];
}
</pre>
<p>用空间换时间。去掉 push, 对于大数组来说，性能提升还是比较明显的。</p>
<p>一大早写博，心情不是很好，得留个题目给大家：</p>
<pre>
var slice = Array.prototype.slice;
alert(slice.call({0: 'foo', length: 'bar'})[0]); // ?
alert(slice.call(NaN).length); // ?
alert(slice.call({0: 'foo', length: '100'})[0]); // ?
</pre>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/05/array-prototype-slice/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>关于架构的一句话，还有一个实例</title>
		<link>http://lifesinger.org/blog/2010/03/thinking-of-architecture/</link>
		<comments>http://lifesinger.org/blog/2010/03/thinking-of-architecture/#comments</comments>
		<pubDate>Fri, 12 Mar 2010 17:18:18 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[思考]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[KISSY]]></category>
		<category><![CDATA[library]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2461</guid>
		<description><![CDATA[昨天有幸邀请到周爱民先生在懒懒交流会上分享《前端，架构、框架和库》，很精彩睿智的讲演，受益颇多。其中提到对架构的一个描述： 架构是把握问题的关键，平衡设计。 很认可。下面是我的理解： 什么是把握？在武术届有一种器械训练方式是“抖大杆”：一条白蜡木做的大杆，杆长超过两米，训练者一手把杆，一手握住杆的底部，全身配合双手，将杆抖出各种样式。把和握是两个不同的动作，把的是方向，握的是基底。把握是一种掌控，武林高手能让大杆随心而动，随意而行，这就是把握。把握可以以把为主，以握为辅，随心所欲，花样百出。把握亦可以握为主，以把为辅，以气驭剑，天马行空。 什么是问题？物理学家狄拉克有一个典故。有一天他到一个学校去演讲，讲完以后，主持人说：“大家有什么问题？可以问狄拉克教授。” 这时，有一个学生站起来说：“刚才你在黑板上写的那个方程式我不懂。” 狄拉克没有回答，好长时间都没有回答。于是主持人就问：“狄拉克教授，您可不可以回答这个问题呢？” 狄拉克说：“那不是一个问题。” 狄拉克为何拒绝回答？因为提问者看不懂方程式只是现象，而不是问题。什么是问题这个问题，推荐阅读杰拉尔德·温伯格的经典著作《你的灯还亮着吗 &#8212; 发现问题的真正所在》。 什么是关键？关键的原始释义是门闩。关是关闭，把门合上，键指机械零件。放到现代，关键就是门上的锁。如何找出未知问题的关键所在？资深的盗墓者，遭遇一个前所未有的机关时，首先是观察，尽可能的获取信息。其次是分析，尽可能推断机关背后的机理。紧接着可能会联想和尝试，用已有的破解技巧，去尝试解决当前问题。反复以上步骤，不懈努力，等机关破解的那一刹那，盗墓者也就明白了该机关的关键所在。 什么是平衡？Douglas 说，万事皆权衡。架构的好坏，是一个适不适合场景的问题。无论是类库的设计，还是某一行代码的书写方式，好坏与否，都要看这个类库或这行代码，使用在什么场景下。Google 首页可以省去 html 结束符，但并不意味着你的博客这么干也是妥当的。在前端界，table 布局也不是万恶不赦的。如果你的用户群里还有不少用户在使用非常旧式的浏览器，table 布局就是最合适的方式。 为了进一步阐释，下面举一个实例。 很喜欢看书，好几年前开始，喜欢从网络上收集各种书籍： （几年前的详细分类已丢失，上面仅是模拟能想起来的几个类别） 按照上面的书籍整理方式，很快发现一个问题：有些书籍，会同时属于多个类别。比如《红楼梦》，放在“古典文学”里好呢，还是放在“精品小说”里好？很明显，这涉及图书管理学的经典问题：如果做一个合理有效的分类？ 于是开始尝试从各种维度来重新分类，甚至跑到大型图书馆里去借鉴图书馆的分类方式。但很快我就崩溃了：中国文学 &#8211; 古典文学 &#8211; 小说 &#8211; 清朝 &#8211; 红楼梦。我不是图书管理员，我就是想收集点自己喜欢看的书而已。这种完美主义者的“科学”分类法立刻被我抛弃。 后来很长一段时间，我的书籍分类一直很混沌。经常隔上几个月，就要大动干戈全部调整一次，让自己追求“完美”的心灵临时安顿。 很漫长的一段时间，很纠结的一段折腾。 一直到大约一年前，忘了当时是什么触因，突然就找到了一个让自己非常满意的分类。在给出这个分类前，我们先“马后炮”一把，尝试从架构角度来进行分析： 这个例子中，什么是问题？表面上看，是如何找到一个合理的图书分类方式。但这真的是问题吗？显然不是。大型图书馆的书目，绝对是合理的。那问题在什么地方？稍一分析不难发现，问题不在于书目分类是否合理，而在于是否适合我的习惯，是否能满足我的需求。（初始的分类问题被转换成了需求问题，有关问题转换方面的话题，推荐阅读温伯格的书籍） 那么，什么是我的需求？我为何要对书籍进行分类？仔细思考，我将需求整理为： 新下载的书籍有个固定的目录存放。能让自己想看时，快速找到。 正在看的书籍能立马找到。 已看完的书籍要归档，以后能比较方便的查阅。 从上面的描述中，可看出我的需求有时间线。我的需求是根据阅读书籍的时间来组织的！“时间”就是我寻找了很久的书目分类的维度！因此时间维度就是该信息架构问题的关键！这样，立刻就有了看似简单但能很好解决问题的分类方式： 一切就这么简单！按照这个方式重新整理书目后，立刻心旷神怡。 当然，这里也涉及到权衡： 新下载的书籍太多怎么办？需要分类吗？ 已看完的书籍，归档时需要分类吗？ 上面两个问题，不同的人有不同的方案。我的选择是： 新下载的书籍不再分类，全部混杂放在一起。好处是能杜绝自己盲目收集书籍的“恶习”，并规定新下载的书籍不能超过 10 本。第 11 本入库时，必须删掉前 10 本中的一本。 已看完的书籍会分类，但只做一级大分类，比如：小说/诗歌/摄影。并规定从“正在读”移动到“已阅读”时，要非常谨慎，必须有理由说服自己有保存价值时才归档，否则删无赦。这样，一年下来，真正有价值归档的书籍并不多。 这就是权衡！ [...]]]></description>
			<content:encoded><![CDATA[<p>昨天有幸邀请到周爱民先生在懒懒交流会上分享《前端，架构、框架和库》，很精彩睿智的讲演，受益颇多。其中提到对架构的一个描述：</p>
<blockquote><p>
<strong>架构是把握问题的关键，平衡设计。</strong>
</p></blockquote>
<p>很认可。下面是我的理解：</p>
<ol>
<li><strong>什么是把握？</strong>在武术届有一种器械训练方式是“抖大杆”：一条白蜡木做的大杆，杆长超过两米，训练者一手把杆，一手握住杆的底部，全身配合双手，将杆抖出各种样式。把和握是两个不同的动作，把的是方向，握的是基底。把握是一种掌控，武林高手能让大杆随心而动，随意而行，这就是把握。把握可以以把为主，以握为辅，随心所欲，花样百出。把握亦可以握为主，以把为辅，以气驭剑，天马行空。</li>
<li><strong>什么是问题？</strong>物理学家狄拉克有一个典故。有一天他到一个学校去演讲，讲完以后，主持人说：“大家有什么问题？可以问狄拉克教授。” 这时，有一个学生站起来说：“刚才你在黑板上写的那个方程式我不懂。” 狄拉克没有回答，好长时间都没有回答。于是主持人就问：“狄拉克教授，您可不可以回答这个问题呢？” 狄拉克说：“那不是一个问题。” 狄拉克为何拒绝回答？因为提问者看不懂方程式只是现象，而不是问题。什么是问题这个问题，推荐阅读杰拉尔德·温伯格的经典著作《你的灯还亮着吗 &#8212; 发现问题的真正所在》。</li>
<li><strong>什么是关键？</strong>关键的原始释义是门闩。关是关闭，把门合上，键指机械零件。放到现代，关键就是门上的锁。如何找出未知问题的关键所在？资深的盗墓者，遭遇一个前所未有的机关时，首先是观察，尽可能的获取信息。其次是分析，尽可能推断机关背后的机理。紧接着可能会联想和尝试，用已有的破解技巧，去尝试解决当前问题。反复以上步骤，不懈努力，等机关破解的那一刹那，盗墓者也就明白了该机关的关键所在。</li>
<li><strong>什么是平衡？</strong>Douglas 说，万事皆权衡。架构的好坏，是一个适不适合场景的问题。无论是类库的设计，还是某一行代码的书写方式，好坏与否，都要看这个类库或这行代码，使用在什么场景下。Google 首页可以省去 html 结束符，但并不意味着你的博客这么干也是妥当的。在前端界，table 布局也不是万恶不赦的。如果你的用户群里还有不少用户在使用非常旧式的浏览器，table 布局就是最合适的方式。</li>
</ol>
<p>为了进一步阐释，下面举一个实例。<br />
<span id="more-2461"></span></p>
<p>很喜欢看书，好几年前开始，喜欢从网络上收集各种书籍：<br />
<img src="http://lifesinger.org/blog/wp-content/uploads/2010/03/old-ebooks-directory.png" alt="以前的书籍分类方式" /><br />
（几年前的详细分类已丢失，上面仅是模拟能想起来的几个类别）</p>
<p>按照上面的书籍整理方式，很快发现一个问题：有些书籍，会同时属于多个类别。比如《红楼梦》，放在“古典文学”里好呢，还是放在“精品小说”里好？很明显，这涉及图书管理学的经典问题：如果做一个合理有效的分类？</p>
<p>于是开始尝试从各种维度来重新分类，甚至跑到大型图书馆里去借鉴图书馆的分类方式。但很快我就崩溃了：中国文学 &#8211; 古典文学 &#8211; 小说 &#8211; 清朝 &#8211; 红楼梦。我不是图书管理员，我就是想收集点自己喜欢看的书而已。这种完美主义者的“科学”分类法立刻被我抛弃。</p>
<p>后来很长一段时间，我的书籍分类一直很混沌。经常隔上几个月，就要大动干戈全部调整一次，让自己追求“完美”的心灵临时安顿。</p>
<p>很漫长的一段时间，很纠结的一段折腾。</p>
<p>一直到大约一年前，忘了当时是什么触因，突然就找到了一个让自己非常满意的分类。在给出这个分类前，我们先“马后炮”一把，尝试从架构角度来进行分析：</p>
<p>这个例子中，什么是问题？表面上看，是如何找到一个合理的图书分类方式。<strong>但这真的是问题吗？</strong>显然不是。大型图书馆的书目，绝对是合理的。那问题在什么地方？稍一分析不难发现，问题不在于书目分类是否合理，而在于是否适合我的习惯，是否能满足我的需求。（初始的分类问题被转换成了需求问题，有关问题转换方面的话题，推荐阅读温伯格的书籍）</p>
<p>那么，<strong>什么是我的需求？</strong>我为何要对书籍进行分类？仔细思考，我将需求整理为：</p>
<ol>
<li>新下载的书籍有个固定的目录存放。能让自己想看时，快速找到。</li>
<li>正在看的书籍能立马找到。</li>
<li>已看完的书籍要归档，以后能比较方便的查阅。</li>
</ol>
<p>从上面的描述中，可看出我的需求有时间线。我的需求是根据阅读书籍的时间来组织的！“时间”就是我寻找了很久的书目分类的维度！<strong>因此时间维度就是该信息架构问题的关键！</strong>这样，立刻就有了看似简单但能很好解决问题的分类方式：<br />
<img src="http://lifesinger.org/blog/wp-content/uploads/2010/03/new-ebooks-directory.png" alt="现在的书籍分类方式" /><br />
一切就这么简单！按照这个方式重新整理书目后，立刻心旷神怡。</p>
<p>当然，这里也涉及到权衡：</p>
<ol>
<li>新下载的书籍太多怎么办？需要分类吗？</li>
<li>已看完的书籍，归档时需要分类吗？</li>
</ol>
<p>上面两个问题，不同的人有不同的方案。我的选择是：</p>
<ol>
<li>新下载的书籍不再分类，全部混杂放在一起。好处是能杜绝自己盲目收集书籍的“恶习”，并规定新下载的书籍不能超过 10 本。第 11 本入库时，必须删掉前 10 本中的一本。</li>
<li>已看完的书籍会分类，但只做一级大分类，比如：小说/诗歌/摄影。并规定从“正在读”移动到“已阅读”时，要非常谨慎，必须有理由说服自己有保存价值时才归档，否则删无赦。这样，一年下来，真正有价值归档的书籍并不多。</li>
</ol>
<p>这就是权衡！</p>
<p>最后再给一个案例，我的 Google Reader 订阅项分类：<br />
<img src="http://lifesinger.org/blog/wp-content/uploads/2010/03/google-reader-rss.png" alt="Google Reader 订阅项分类方式" /></p>
<p>必读 &#8212; 符合自己脾胃的精品订阅源<br />
闲读 &#8212; 可看可不看的订阅源<br />
待通读 &#8212; 新发现的一些相见恨晚的博客，想翻阅其所有文章<br />
更新 &#8212; 软件更新提示等订阅源</p>
<p>自从采用这个分类后，信息筛选的时间大大减少，有效阅读的时间增加了很多。注意：这个分类适合我，但未必适合你。适合你的分类，可以从自己的真正需求出发，仔细的思考和分析，通过探索和实践去获得。</p>
<p>上面说的虽然是很小的生活中的分类问题，但往大里说就是信息架构，再类推开去，就是软件架构。道理是相通的。架构是个过程，是思考、实践，再次思考、再次实践的过程。在这过程中，下面三个问题经常遇到：</p>
<ol>
<li><strong>问题是什么？</strong></li>
<li><strong>什么是问题的关键？</strong></li>
<li><strong>在当前应用场景下，如何设计，如何权衡？</strong></li>
</ol>
<p>架构就是在特定应用场景下，不断追问和寻求以上问题的过程。在这过程中，你的所有决策的集合，就是架构。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/03/thinking-of-architecture/feed/</wfw:commentRss>
		<slash:comments>27</slash:comments>
		</item>
		<item>
		<title>JavaScript 全半角转换</title>
		<link>http://lifesinger.org/blog/2010/02/js-sbc2dbc/</link>
		<comments>http://lifesinger.org/blog/2010/02/js-sbc2dbc/#comments</comments>
		<pubDate>Tue, 02 Feb 2010 05:06:29 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[开发]]></category>
		<category><![CDATA[dbc]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[sbc]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2426</guid>
		<description><![CDATA[规律：半角空格的 charCode 为 32, 全角空格为 12288. 其他半角字符 ( 33 &#8211; 126 ) 与全角 ( 65281 &#8211; 65374 ) 的对应关系是：均相差 65248. 找好规律，代码就好写了： var hash = {'32' : '\u3000'}; // 半角转全角 function sbc2dbc(str) { var ret = [], i = 0, len = str.length, code, chr; for (; i < len; ++i) { code = str.charCodeAt(i); chr [...]]]></description>
			<content:encoded><![CDATA[<p>规律：半角空格的 charCode 为 32, 全角空格为 12288. 其他半角字符 ( 33 &#8211; 126 ) 与全角 ( 65281 &#8211; 65374 ) 的对应关系是：均相差 65248.</p>
<p>找好规律，代码就好写了：</p>
<pre>
var hash = {'32' : '\u3000'};
// 半角转全角
function sbc2dbc(str) {
    var ret = [], i = 0, len = str.length, code, chr;
    for (; i < len; ++i) {
        code = str.charCodeAt(i);
        chr = hash[code];
        if (!chr &#038;&#038; code > 31 &#038;&#038; code < 127) {
            chr = hash[code] = String.fromCharCode(code + 65248);
        }
        ret[i] = chr ? chr : str.charAt(i);
    }
    return ret.join('');
}
</pre>
<p><span id="more-2426"></span><br />
同理：</p>
<pre>
var hash = {'12288' : ' '};
// 全角转半角
function dbc2sbc(str) {
    var ret = [], i = 0, len = str.length, code, chr;
    for (; i < len; ++i) {
        code = str.charCodeAt(i);
        chr = hash[code];
        if (!chr &#038;&#038; code > 65280 &#038;&#038; code < 65375) {
            chr = hash[code] = String.fromCharCode(code - 65248);
        }
        ret[i] = chr ? chr : str.charAt(i);
    }
    return ret.join('');
}
</pre>
<p>上面的代码会将 33 - 126 中间的符号也转换。很多时候，这并不是我们需要的（比如将 @ 转换为 ＠）。下面的代码侵入性更小：</p>
<pre>
var hash = {};
// 半角转全角。仅转换 [0-9a-zA-Z]
function sbc2dbc_w(str) {
    var ret = [], i = 0, len = str.length, code, chr;
    for (; i < len; ++i) {
        code = str.charCodeAt(i);
        chr = hash[code];
        if (!chr &#038;&#038;
            (47 < code &#038;&#038; code < 58 ||
             64 < code &#038;&#038; code < 91 ||
             96 < code &#038;&#038; code < 123)) {
            chr = hash[code] = String.fromCharCode(code + 65248);
        }
        ret[i] = chr ? chr : str.charAt(i);
    }
    return ret.join('');
}
</pre>
<p>详细测试页面：<a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/sbc2dbc-test.html">sbc2dbc-test.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/02/js-sbc2dbc/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>字符引用和空白字符</title>
		<link>http://lifesinger.org/blog/2010/01/charater-references/</link>
		<comments>http://lifesinger.org/blog/2010/01/charater-references/#comments</comments>
		<pubDate>Mon, 25 Jan 2010 01:33:08 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[开发]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[regex]]></category>
		<category><![CDATA[unicode]]></category>
		<category><![CDATA[whitespace]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2411</guid>
		<description><![CDATA[看到最近有讨论，前些日子刚好也收集过一些资料，补充如下： 字符引用 在 html 中，有三种字符引用方式（参考 HTML5 规范）： Named character references, 通过名称来引用。在 HTML 4.01 中称之为 Character entity references（字符实体引用）比如 &#38;lt; &#38;gt; &#38;nbsp; &#38;quot;, 这里有一份详细列表 named-character-references Decimal numeric character reference, 通过十进制数值来引用。比如 &#38;#229; &#38;#1048; Hexadecimal numeric character reference, 通过十六进制数值来引用。比如 &#38;#xe5; &#38;#x6C34; 在 HTML 4.01 中，上面 2 和 3 归为一种：Numeric character references（字符数值引用）。 这里有份非常好的对照表：XHTML Character Entity Reference 在 javascript 中，特殊字符用反斜杠转义，比如 \t, [...]]]></description>
			<content:encoded><![CDATA[<p>看到最近有<a href="http://www.99css.com/?p=286">讨论</a>，前些日子刚好也收集过一些资料，补充如下：</p>
<h3>字符引用</h3>
<p>在 html 中，有三种字符引用方式（参考 <a href="http://www.w3.org/TR/html5/syntax.html#character-references">HTML5 规范</a>）：</p>
<ol>
<li>Named character references, 通过名称来引用。在 HTML 4.01 中称之为 Character entity references（字符实体引用）比如 &amp;lt; &amp;gt; &amp;nbsp; &amp;quot;, 这里有一份详细列表 <a href="http://www.w3.org/TR/html5/named-character-references.html">named-character-references</a></li>
<li>Decimal numeric character reference, 通过十进制数值来引用。比如 &amp;#229; &amp;#1048;</li>
<li>Hexadecimal numeric character reference, 通过十六进制数值来引用。比如 &amp;#xe5; &amp;#x6C34;</li>
</ol>
<p>在 HTML 4.01 中，上面 2 和 3 归为一种：Numeric character references（字符数值引用）。<br />
这里有份非常好的对照表：<a href="http://www.digitalmediaminute.com/reference/entity/">XHTML Character Entity Reference</a><br />
<span id="more-2411"></span><br />
在 javascript 中，特殊字符用反斜杠转义，比如 \t, \n, \u0020, 这个不必多说。<br />
在 css 中，只支持 unicode 转义。比如 \0020.<br />
注意：\u0020 在 js 中，必须有 u, 而且必须 4 位。在 css 中，u 必须省略，同时可以省略前导 0. 比如 \0020 在 css 中可以简化为 \20. 因此那个常见的 clearfix, 可以简化为 content: &#8216;\20&#8242;. 好友<a href="http://twitter.com/blankzheng/status/8107822420">怿飞</a>提到的兼容性和语义，应该指的是这个。content 后直接用空格或其它空白字符，会导致某些兼容性问题。</p>
<h3>空白字符</h3>
<p>说到字符引用，不得不提一下空白字符。在 JavaScript 的正则里，\s 是 unicode-aware 的，可以匹配 \u0020, \u0009 等空白字符。但各个浏览器的实现有差异，具体可以参见：<a href="http://blog.stevenlevithan.com/archives/javascript-regex-and-unicode">JavaScript, Regex, and Unicode</a>.</p>
<p>大部分情况下，JS 类库在实现 trim 方法时，使用 /^\s+|\s+$/g 即可。<br />
在 jQuery 中，加入了对 \u00A0 的检测：/^(\s|\u00A0)+|(\s|\u00A0)+$/g<br />
\u00A0 其实就是 &amp;nbsp; 代表 non-breaking space(不间断空白)</p>
<p>关于 &amp;nbsp;, 严格语义上来说，使用场景是不希望自动换行时使用。<br />
但现实中，绝大部分情况下，&amp;nbsp; 就用来表示纯粹的空格。因为多个字符空格在 html 中会被当成一个空格，为了连续输出多个空格，只好用 &amp;nbsp; （毕竟用 css 控制太麻烦）。按理说用多个 &amp;#32; 输出多个空格是最好的选择，但强大的 html 引擎依旧会把多个 &amp;#32; 转换成一个。</p>
<p>jQuery 的 trim 方法加入 \u00A0, 是种无奈，但也是一种对现实标准的认可。</p>
<p>一切皆权衡。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/01/charater-references/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>jQuery 让人恋恋不舍的秘密</title>
		<link>http://lifesinger.org/blog/2010/01/the-beauty-of-jquery-api/</link>
		<comments>http://lifesinger.org/blog/2010/01/the-beauty-of-jquery-api/#comments</comments>
		<pubDate>Tue, 12 Jan 2010 15:41:09 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[思考]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[library]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2396</guid>
		<description><![CDATA[jQuery 将马上发布 1.4 正式版，代码也从 googlecode 上迁移到了 github. jQuery 是我接触的第一个 JS 类库，俗话说初恋总是让人难以忘记。一年以前，这种难以忘记仅仅是一种纯感觉，说不出来具体原因。前几天重新看了一遍 github 上的源码。从纯功能上说，jQuery 并没有特别出色的地方。究竟是什么让我如此恋恋不舍呢？ 昨天搭建 taskspeed, 检查 jQuery 的测试代码时，突然明晓了一个也许大家都已知道的秘密： jQuery 最出色最让人恋恋不舍的是它的 API 设计。 比如 dom-style 的 api, YUI3 和 MooTools 等框架采用的是传统方式： el.setStyle(prop, val); el.getStyle(prop); el.setStyles({ propA: valA, propB: valB }); el.getStyles(propA, propB); // MooTools 支持 在 jQuery 里，一个 css 方法全部搞定： el.css(prop); // 表示 getStyle el.css(prop, [...]]]></description>
			<content:encoded><![CDATA[<p>jQuery 将马上发布 1.4 正式版，代码也从 googlecode 上迁移到了 <a href="http://github.com/jquery/jquery">github</a>. jQuery 是我接触的第一个 JS 类库，俗话说初恋总是让人难以忘记。一年以前，这种难以忘记仅仅是一种纯感觉，说不出来具体原因。前几天重新看了一遍 github 上的源码。从纯功能上说，jQuery 并没有特别出色的地方。究竟是什么让我如此恋恋不舍呢？</p>
<p>昨天搭建 <a href="http://lifesinger.org/labs/taskspeed/">taskspeed</a>, 检查 jQuery 的测试代码时，突然明晓了一个也许大家都已知道的秘密：</p>
<p><strong>jQuery 最出色最让人恋恋不舍的是它的 API 设计。</strong><br />
<span id="more-2396"></span><br />
比如 dom-style 的 api, YUI3 和 MooTools 等框架采用的是传统方式：</p>
<pre>
el.setStyle(prop, val);
el.getStyle(prop);
el.setStyles({ propA: valA, propB: valB });
el.getStyles(propA, propB); // MooTools 支持
</pre>
<p>在 jQuery 里，一个 css 方法全部搞定：</p>
<pre>
el.css(prop); // 表示 getStyle
el.css(prop, val); // 表示 setStyle
el.css({ propA: valA, propB: valB }); // 表示 setStyles
el.css(prop, func); // func 是一个返回 val 值的函数
</pre>
<p>对比以上两种 API 设计，乍一看 jQuery 显得不那么“标准”。但从可记忆性和灵活性上讲，我觉得 jQuery 的设计都更人性化。jQuery 的 API 还符合学习上的渐进式思维：先学会最简单的情况<code>el.css(prop)</code>, 再了解到还可以有两个参数，接着发现参数可以是 map, 更进一步发现 val 还可以是一个函数。func 参数甚至能带给学习者一种惊喜：居然还可以这样用！jQuery 把一种渐进和愉悦带进了学习和使用的过程中，实在漂亮！</p>
<p>YUI3 的 API 缺少这种乐趣。查询 jQuery 的 API, 会有一种探寻秘密的寻宝感觉。YUI 的文档查询则让人感觉就是份工作，有点 boring.</p>
<p>和 YUI2 相比，YUI3 的 API 做了些改变。在 YUI2 里，YAHOO.util.Dom 的方法名，严格以动词开头，虽然有些方法名长点，但总体规律性很强，可记忆性还不错。在 YUI3 里，则出现了 byId, elementByAxis 等方式命名的方法。纯粹为了省几个字符？这种不一致性很纳闷。还有一些以名词命名的方法：ancestor, docHeight, 乍一看很难以为是方法。</p>
<p>老婆说，要睡觉了，就不码字了。最后说一句：YUI3 的 API 整体还是挺不错的，比如 Node 的方法命名，就非常严谨。ancestor 也是为了对应 next, prev 等命名。也就是说：Y.Dom 其实已变成了内部 API, 不鼓励用户直接调用。</p>
<p>但是不知为什么，我还是觉得 jQuery 的 API 设计高出一个层次，套用一句流行话就是：<br />
<strong>jQuery API 的用户体验更好。</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/01/the-beauty-of-jquery-api/feed/</wfw:commentRss>
		<slash:comments>37</slash:comments>
		</item>
		<item>
		<title>Fastest JavaScript Trim</title>
		<link>http://lifesinger.org/blog/2010/01/fastest-javascript-trim/</link>
		<comments>http://lifesinger.org/blog/2010/01/fastest-javascript-trim/#comments</comments>
		<pubDate>Sun, 10 Jan 2010 14:55:14 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[开发]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2384</guid>
		<description><![CDATA[这是 2007 年的一篇老文：Faster JavaScript Trim. 测试页面：trim-test.html 感兴趣的几个方法： function trim1(str) { return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); } function trim2(str) { return str.replace(/^\s+/, '').replace(/\s+$/, ''); } function trim4(str) { return str.replace(/^\s+&#124;\s+$/g, ''); } function trim8(str) { return str.replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1'); } 时至今日，有些结论已经发生变化： 1. trim1 中使用的正则优化：pre-check of required character and start of string anchor, 在最新的 JS 引擎中已经内置。trim4 也得到了非常好的优化，比 trim1, trim2 [...]]]></description>
			<content:encoded><![CDATA[<p>这是 2007 年的一篇老文：<a href="http://blog.stevenlevithan.com/archives/faster-trim-javascript">Faster JavaScript Trim</a>.</p>
<p>测试页面：<a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/trim-test.html">trim-test.html</a></p>
<p>感兴趣的几个方法：</p>
<pre>
function trim1(str) {
    return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
function trim2(str) {
    return str.replace(/^\s+/, '').replace(/\s+$/, '');
}
function trim4(str) {
    return str.replace(/^\s+|\s+$/g, '');
}
function trim8(str) {
    return str.replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
}
</pre>
<p>时至今日，有些结论已经发生变化：<br />
<span id="more-2384"></span><br />
1. trim1 中使用的正则优化：pre-check of required character and start of string anchor, 在最新的 JS 引擎中已经内置。trim4 也得到了非常好的优化，比 trim1, trim2 都快。</p>
<p>2. trim8 在 IE 中依旧非常有优势。这是因为 IE 对 \S\s (any character token) 有非常好的优化。另外 ?: (non-capturing group) 对性能也有提升。</p>
<p>3. trim10 是非正则实现方案，速度飕飕的。原文中指出关键点：正则在处理过程中无法直接跳转到字符串末尾，从而导致在处理长字符串时耗时稍长。于是有了混合版本 trim11 和 trim11 的改进版本 trim12:</p>
<pre>
function trim11(str) {
    str = str.replace(/^\s+/, '');
    for (var i = str.length - 1; i >= 0; i--) {
        if (/\S/.test(str.charAt(i))) {
            str = str.substring(0, i + 1);
            break;
        }
    }
    return str;
}

function trim12(str) {
    str = str.replace(/^\s\s*/, "");
    var ws = /\s/,
            i = str.length;
    while (ws.test(str.charAt(--i))) {}
    return str.slice(0, i + 1);
}
</pre>
<p>4. 在原文的回复中，还有利用空间换时间的 map 方案。</p>
<p>5. 对于普通应用，比如 JS 框架，原文作者推荐使用 trim1. 从上面的变化可以看出，目前更好的方案是 trim4. 这也正是当今流行 JS 框架中广泛采用的实现方案。对于特殊应用需要处理长字符串时，推荐采用 trim11.</p>
<p>6. 目前 Firefox 3 和 Chrome 4 中都已原生实现 String.prototype.trim, 对于这些浏览器，原生实现始终是最佳方案。</p>
<p>7. 原文作者的 final note 值得注意：许多开发者习惯用变量缓存正则，这可以避免正则的重复编译，但这对于简单如 trim 的正则来说意义不大。trim 的正则如此简单，编译时间是纳秒级的。而且不少浏览器自身已对最近使用的正则进行缓存，不会存在重复编译的问题。trim13 用来验证此观点，除了 IE, 其它浏览器下，当重复次数 times 变大时，trim13 反而比 trim12 略有优势。</p>
<p>8. 讨论一个有争议的点：对于 JS 类库来说，trim 方法究竟应不应该附加在 String.prototype 上呢？侵入 built-in 对象，会导致<code>for...in</code>产生<a href="https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Statements/for...in">某些非预期结果</a>。但是，对于 String 来说，用<code>for...in</code>遍历 String 本就是非常不明智的行为。为了这些“愚蠢的”使用方法，而坚决不侵入 built-in 对象，是否真的值得？毕竟从用户角度讲，<code>str.trim()</code> ，比类似<code>$.trim(str)</code>这种写法自然多了。</p>
<p>最后给一个我的方案：</p>
<pre>
if(!String.prototype.trim) {
    var TRIM_REG = /^\s+|\s+$/g;
    String.prototype.trim = function() { return this.replace(TRIM_REG, ''); }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/01/fastest-javascript-trim/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>重构之美 &#8211; Switchable 组件开发心得</title>
		<link>http://lifesinger.org/blog/2010/01/the-beauty-of-refactoring/</link>
		<comments>http://lifesinger.org/blog/2010/01/the-beauty-of-refactoring/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 12:19:14 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[开发]]></category>
		<category><![CDATA[carousel]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[KISSY]]></category>
		<category><![CDATA[refactoring]]></category>
		<category><![CDATA[slide]]></category>
		<category><![CDATA[switchable]]></category>
		<category><![CDATA[tabs]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2366</guid>
		<description><![CDATA[slideshare 在线观看：The Beauty Of Refactoring ppt 下载：重构之美.pptx src code：/trunk/src/widget-switchable 欢迎讨论交流。]]></description>
			<content:encoded><![CDATA[<p>slideshare 在线观看：<a href="http://www.slideshare.net/lifesinger/the-beauty-of-refactoring">The Beauty Of Refactoring</a><br />
ppt 下载：<a href="http://lifesinger.googlecode.com/svn/trunk/share/2010/201001_重构之美.pptx">重构之美.pptx</a><br />
src code：<a href="http://kissy.googlecode.com/svn/trunk/src/widget-switchable/">/trunk/src/widget-switchable</a><br />
<img src="http://lifesinger.org/blog/wp-content/uploads/2010/01/the-beauty-of-refactoring.jpg" alt="the beauty of refactoring" /><br />
欢迎讨论交流。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/01/the-beauty-of-refactoring/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
	</channel>
</rss>
