<?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>岁月如歌</title>
	<atom:link href="http://lifesinger.org/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://lifesinger.org/blog</link>
	<description>关注用户体验、前端开发，记录生活点滴、岁月足迹。</description>
	<lastBuildDate>Mon, 08 Mar 2010 02:25:37 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Script 元素的异步加载属性</title>
		<link>http://lifesinger.org/blog/2010/03/script-async-attribute/</link>
		<comments>http://lifesinger.org/blog/2010/03/script-async-attribute/#comments</comments>
		<pubDate>Sun, 07 Mar 2010 10:15:58 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[async]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[script]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2446</guid>
		<description><![CDATA[进入正题之前，先考大家一个问题：defer 属性现在有哪些浏览器支持？
喜悦
除了 defer 属性，script 还新增了一个 async 属性，请看 MDC：

async Requires Gecko 1.9.2
This Boolean attribute is set to indicate that the browser should, if possible, execute the script asynchronously. If the script cannot be executed asynchronously, or the browser doesn&#8217;t support this attribute, the script is executed synchronously (and loading the content blocks until the script finishes [...]]]></description>
			<content:encoded><![CDATA[<p>进入正题之前，先考大家一个问题：defer 属性现在有哪些浏览器支持？</p>
<h3>喜悦</h3>
<p>除了 defer 属性，script 还新增了一个 async 属性，请看 <a href="https://developer.mozilla.org/En/HTML/Element/Script">MDC</a>：</p>
<blockquote><p>
async Requires Gecko 1.9.2<br />
This Boolean attribute is set to indicate that the browser should, if possible, execute the script asynchronously. If the script cannot be executed asynchronously, or the browser doesn&#8217;t support this attribute, the script is executed synchronously (and loading the content blocks until the script finishes running).
</p></blockquote>
<p>理想情况下，利用 async 属性，脚本异步加载将变得非常简单：<br />
async-test.js:</p>
<pre>
var g = 1;
</pre>
<p>async-test-1.html:</p>
<pre>
&lt;script src="async-test.js" async="true"&gt;&lt;/script&gt;
&lt;script&gt;
    alert(typeof g); // Firefox 3.6 弹出 undefined
&lt;/script&gt;
</pre>
<p>测试页面：<a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/ad-script/async-test-1.html">async-test-1.html</a><br />
目前只有 Firefox 3.6 支持 async 属性，希望其它浏览器能迅速跟进。<br />
<span id="more-2446"></span></p>
<h3>悲哀</h3>
<p>Firefox 3.6 的尝试和探索精神令人钦佩。但是，在引入这个新特性时，也导致了传统异步加载方式的失效：</p>
<pre>
&lt;script&gt;
    (function(d) {
        var a = d.createElement('script');
        a.src = 'async-test.js';
        d.getElementsByTagName('head')[0].appendChild(a);
    })(document);
&lt;/script&gt;
&lt;script&gt;
    alert(typeof g); // 预期结果是 undefined, Firefox 3.6 弹出 number
&lt;/script&gt;
</pre>
<p>测试页面：<a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/ad-script/async-test-2.html">async-test-2.html</a><br />
真糟糕！类似问题，还有 Steve Souders 前不久发现的 <a href="http://www.stevesouders.com/blog/2010/02/10/5b-document-write-scripts-block-in-firefox/">document.write 在 Firefox 3.6 下的阻塞行为</a>。</p>
<h3>分析</h3>
<p>用 Firebug 可以看到，在 Firefox 3.6 中，script 元素 async 属性的默认值是 false. 换言之，script 元素默认是同步加载的。可以推测，Firefox 3.6 在增加 async 这个属性时，很可能忽略了用 createElement(&#8217;script&#8217;) 和 document.write(&#8216;&lt;script&#8230;&#8217;) 等方式创建的 script 元素，async 的属性值应该从默认值 false 调整为 true. 正是这个疏忽，导致了传统加载异步脚本的方式在 Firefox 3.6 中失效。对于 YUI3 和很多站点来说，这实在是太糟糕了。</p>
<p>知道了问题本因，可以得到目前异步加载脚本最安全的方式如下：</p>
<pre>
&lt;script&gt;
    (function(d) {
        var a = d.createElement('script');
        a.async = true;
        a.src = 'async-test.js';
        d.getElementsByTagName('head')[0].appendChild(a);
    })(document);
&lt;/script&gt;
</pre>
<p>测试页面：<a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/ad-script/async-test-3.html">async-test-3.html</a></p>
<p>注意1：所有测试结果，均在无缓存的情况下得出。<br />
注意2：async-test-2.html 和 async-test-3.html 在 Opera 10.10 中弹出值非预期，原因未深究，有兴趣者可以进一步挖掘。</p>
<h3>感慨</h3>
<p>Google 在 <a href="http://code.google.com/apis/analytics/docs/tracking/asyncUsageGuide.html">ga 的部署脚本</a> 中已经使用上了 async 属性：</p>
<pre>
&lt;script type="text/javascript"&gt;
    (function() {
      var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
      ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
      (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
    })();
  &lt;/script&gt;
</pre>
<h3>最后</h3>
<p>再考大家一题，在 Firefox 3.6 下，下面的代码，alert 出什么：</p>
<pre>
&lt;script src="async-test.js" async="false"&gt;&lt;/script&gt;
&lt;script&gt;
    alert(typeof g); // ?
&lt;/script&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/03/script-async-attribute/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Chrome V8 引擎对 sort 的优化</title>
		<link>http://lifesinger.org/blog/2010/02/chrome-v8-optimization-for-sort/</link>
		<comments>http://lifesinger.org/blog/2010/02/chrome-v8-optimization-for-sort/#comments</comments>
		<pubDate>Wed, 03 Feb 2010 04:25:15 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[array]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[sort]]></category>
		<category><![CDATA[v8]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2431</guid>
		<description><![CDATA[
var a = 0, b = 0;
[0, 0].sort(function() {
    a = 1;
    return 0;
});
[0, 1].sort(function() {
    b = 1;
    return 0;
});
alert(a === b); // true or false ?

上面的代码，除了 Chrome 输出 false, 其它浏览器皆为 true.
原因是 Chrome 对数组的 sort 方法进行了优化：

function sort(comparefn) {
  var custom_compare = [...]]]></description>
			<content:encoded><![CDATA[<pre>
var a = 0, b = 0;
[0, 0].sort(function() {
    a = 1;
    return 0;
});
[0, 1].sort(function() {
    b = 1;
    return 0;
});
alert(a === b); // true or false ?
</pre>
<p>上面的代码，除了 Chrome 输出 false, 其它浏览器皆为 true.</p>
<p>原因是 Chrome 对数组的 sort 方法进行了优化：</p>
<pre>
function sort(comparefn) {
  var custom_compare = (typeof(comparefn) === 'function');
  function Compare(x,y) {
    if (x === y) return 0;
    if (custom_compare) {
      return comparefn.call(null, x, y);
    }
    ...
}
</pre>
<p>虽然是优化，但也是陷阱。想用 sort 来干点额外体力活时，一定要小心。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/02/chrome-v8-optimization-for-sort/feed/</wfw:commentRss>
		<slash:comments>9</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) {
   [...]]]></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>2</slash:comments>
		</item>
		<item>
		<title>Study jQuery in a Simplified Way</title>
		<link>http://lifesinger.org/blog/2010/01/study-jquery-in-a-simplified-way/</link>
		<comments>http://lifesinger.org/blog/2010/01/study-jquery-in-a-simplified-way/#comments</comments>
		<pubDate>Mon, 25 Jan 2010 15:24:27 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[prototype]]></category>
		<category><![CDATA[push]]></category>
		<category><![CDATA[study]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2419</guid>
		<description><![CDATA[学习复杂代码的最好方法是简化：

(function(win, undefined) {
    var jQuery = function(selector, context) {
            // jQuery 对象就是 init 函数的一个实例
            return new jQuery.fn.init(selector, context);
        },
     [...]]]></description>
			<content:encoded><![CDATA[<p>学习复杂代码的最好方法是简化：</p>
<pre>
(function(win, undefined) {
    var jQuery = function(selector, context) {
            // jQuery 对象就是 init 函数的一个实例
            return new jQuery.fn.init(selector, context);
        },
        document = window.document,
        push = Array.prototype.push,
        slice = Array.prototype.slice;

    jQuery.fn = jQuery.prototype = {
        init: function(selector, context) {
            // 选择器
            var ret = (context || document).querySelectorAll(selector);

            // 转换为普通数组
            ret = slice.call(ret);

            // jQuery API 的奥妙全在下面这句，将选择器获取的元素添加到 jQuery 对象中
            // 使用 push, 速度飞快（当年担心大量 jQuery 对象实例化的性能问题，根本就不是问题）
            // 使用 push, 还能自动更新 length 属性
            push.apply(this, ret);

            return this;
        },
        length: 0,
        // 实例方法
        attr: function(name, value) {
            return access(this, name, value, jQuery.attr);
        }
    };

    // 这句保证了 init 方法里的 this 拥有 jQuery 实例的方法
    jQuery.fn.init.prototype = jQuery.fn;

    // 静态方法
    jQuery.attr = function(elem, name, value) {
        if (value === undefined) {
            return elem.getAttribute(name);
        }
        return elem.setAttribute(name, value);
    };

    // 神奇的 access, 在实例方法和静态方法中建立了一座桥梁
    // 数组批次操作的实现也在这里
    function access(elems, key, value, fn) {
        var length = elems.length;

        if (value !== undefined) {
            for (var i = 0; i < length; i++) {
                fn(elems[i], key, value);
            }
            return elems;
        }

        return length ? fn(elems[0], key) : null;
    }

    win.$ = win.jQuery = jQuery;
})(window);
</pre>
<p>测试页面：<a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/study-jquery-in-simplified-way.html">study-jquery-in-simplified-way.html</a> （请在非 IE 浏览器下运行）</p>
<p>源码：<a href="http://github.com/jquery/jquery/blob/master/src/core.js">jquery~core.js</a></p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/01/study-jquery-in-a-simplified-way/feed/</wfw:commentRss>
		<slash:comments>10</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, \n, \u0020, 这个不必多说。
在 css 中，只支持 unicode 转义。比如 \0020.
注意：\u0020 [...]]]></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>5</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, val); // 表示 setStyle
el.css({ propA: valA, propB: valB }); // [...]]]></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>34</slash:comments>
		</item>
		<item>
		<title>The Deferred Evaluation of YUI 3</title>
		<link>http://lifesinger.org/blog/2010/01/the-deferred-evaluation-of-yui3/</link>
		<comments>http://lifesinger.org/blog/2010/01/the-deferred-evaluation-of-yui3/#comments</comments>
		<pubDate>Tue, 12 Jan 2010 02:08:47 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[defer]]></category>
		<category><![CDATA[evaluation]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2391</guid>
		<description><![CDATA[JS 的加载速度，包括下载和运算两部分（参考 JavaScript Library Loading Speed）：

Total_Speed = Time_to_Download + Time_to_Evaluate

运算（evaluate）又可细分为解析（parse）和执行（execute）。考虑加载速度时，一般放在一起考虑。
通常来说，下载时间是影响 JS 加载速度的关键。这方面的优化已经很成熟，比如 Compressor, GZip, Cache 等等。
evaluation 部分，目前主要优化方案是延迟（称之 Deferred Evaluation）。具体策略有：

将 JS 代码以注释的形式下载，需要使用时再 parse. 可以参考 GMail for Mobile Team 的研究。
将 JS 代码以字符串定义的形式下载。可以参考 SproutCore Blog 的分析。
将 JS 代码以闭包的形式下载。

第一种策略能将 evaluation 完全延迟，后两种还存在少量未被延迟的 evaluation, 但和第一种差异很小。下面讨论第三种。

YUI 3
YUI 3 的模块定义代码：

YUI.add("module-a", function(Y) {
    /* module code */
});

模块添加到 YUI 上时，function(Y)作为一个整体(closure)注册到环境变量里，module code 尚未执行。
模块调用代码：

YUI().use("module-a", [...]]]></description>
			<content:encoded><![CDATA[<p>JS 的加载速度，包括下载和运算两部分（参考 <a href="http://ejohn.org/blog/library-loading-speed/">JavaScript Library Loading Speed</a>）：</p>
<blockquote><p>
Total_Speed = Time_to_Download + Time_to_Evaluate
</p></blockquote>
<p>运算（evaluate）又可细分为解析（parse）和执行（execute）。考虑加载速度时，一般放在一起考虑。</p>
<p>通常来说，下载时间是影响 JS 加载速度的关键。这方面的优化已经很成熟，比如 Compressor, GZip, Cache 等等。</p>
<p>evaluation 部分，目前主要优化方案是延迟（称之 Deferred Evaluation）。具体策略有：</p>
<ol>
<li>将 JS 代码以注释的形式下载，需要使用时再 parse. 可以参考 <a href="http://googlecode.blogspot.com/2009/09/gmail-for-mobile-html5-series-reducing.html">GMail for Mobile Team</a> 的研究。</li>
<li>将 JS 代码以字符串定义的形式下载。可以参考 <a href="http://feedproxy.google.com/~r/Sproutcore-BlogPosts/~3/-0S1tX1vtN0/225219087">SproutCore Blog</a> 的分析。</li>
<li>将 JS 代码以闭包的形式下载。</li>
</ol>
<p>第一种策略能将 evaluation 完全延迟，后两种还存在少量未被延迟的 evaluation, 但和第一种差异很小。下面讨论第三种。<br />
<span id="more-2391"></span></p>
<h3>YUI 3</h3>
<p>YUI 3 的模块定义代码：</p>
<pre>
YUI.add("module-a", function(Y) {
    /* module code */
});
</pre>
<p>模块添加到 YUI 上时，<code>function(Y)</code>作为一个整体(closure)注册到环境变量里，module code 尚未执行。</p>
<p>模块调用代码：</p>
<pre>
YUI().use("module-a", function(Y) {
    /* user code */
});
</pre>
<p>此时，定义代码中的 function closure 才被调用：执行 module code, 将 module a 挂载到 YUI 的实例 Y 上。</p>
<p>很显然，YUI 3 的模块定义和加载机制，非常自然地实现了 Deferred Evaluation 的第三种策略。我们来看看效果如何。</p>
<h3>Tests</h3>
<p>测试页面：<a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/faster-javascript-load/test.html">deferred evaluation test</a></p>
<p>测试方法：点击 Load YUI3 按钮，会看到第二个 iframe 重复加载 5 次。加载完成后，点击 iframe 里的 Run Again （确保有缓存）。结果里的 Setup time &#8211; Load time, 就是 Deferred Evaluation 能为页面减少的加载时间。</p>
<p><img src="http://lifesinger.googlecode.com/svn/trunk/lab/2010/faster-javascript-load/yui3-deferred-evaluation.png" alt="YUI3 Deferred Evaluation Chart" /><br />
上图是在 T61 上的测试结果。加载的 JS 文件是 <a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/yui3-performance-test/yui3-combo-min.js">yui3-combo-min.js</a>, 仅打包了 YUI3 的基础模块。</p>
<p>从结果上可以看出，对于普通电脑来说，Deferred Evaluation 带来的性能提升并不明显：差异最大的 Safari 上，也仅减少了不到 50ms. 但可以推测：电脑 CPU 越低，Deferred Evaluation 带来的提升会越明显，比如 Mobile 应用上。</p>
<h3>结论</h3>
<ol>
<li>对于 YUI 3 来说，这是沙箱和模块加载机制带来的意外惊喜：能减少初始加载时间总是好的，虽然微不足道。</li>
<li>对于 Mobile 应用来说，Deferred Evaluation 非常值得尝试，Gmail for Mobile 团队走在了前列。</li>
<li>对于普通 Web 应用来说，JS 加载的主要性能优化点依旧是减少文件本身的大小，比如将 YUI Compressor 换成 Google Closure Compiler.</li>
</ol>
<p>最后推荐 Ara Pehlivanian 的博文：<a href="http://www.phpied.com/javascript-loading-strategies/">JavaScript loading strategies</a>, JS 加载有五个方面的优化策略：Reduce(减少文件大小), Defer(延迟), Go Async(异步), Be Lazy(懒加载), Bite Size(真的需要 JS 吗)，这五点特别是最后一点，需要我们的坚持和努力。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/01/the-deferred-evaluation-of-yui3/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>JavaScript 框架的 DOM 操作性能</title>
		<link>http://lifesinger.org/blog/2010/01/jslib-dom-performance-test/</link>
		<comments>http://lifesinger.org/blog/2010/01/jslib-dom-performance-test/#comments</comments>
		<pubDate>Mon, 11 Jan 2010 04:25:51 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[taskspeed]]></category>
		<category><![CDATA[YUI]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2389</guid>
		<description><![CDATA[经克军提醒，在博客上也搭建了个 TaskSpeed: 
http://lifesinger.org/labs/taskspeed/
将感兴趣的框架都更新到最新版本。修改了部分 tests.js, 以保证主流浏览器下所有用例都能得到正确结果。
特别修改了 YUI 2.8 的测试文件：yui-tests.js. 原测试文件大量使用YAHOO.util.Selector这个并不推荐使用的类，不符合 YUI 2 的实际使用情况（一般仅使用 yahoo-dom-event, get, anim 等 utilities）。
从测试结果可以看出：
前三甲是 Dojo 1.4, jQuery 1.4, YUI 2.8
MooTools 在 Safari 下表现优异，其它浏览器下有些惨淡。
Prototype 老矣，尚能饭否？
YUI 3 中规中矩，不出色，但也不糟糕。
]]></description>
			<content:encoded><![CDATA[<p>经<a href="http://hikejun.com/">克军</a>提醒，在博客上也搭建了个 TaskSpeed: </p>
<p><a href="http://lifesinger.org/labs/taskspeed/">http://lifesinger.org/labs/taskspeed/</a></p>
<p>将感兴趣的框架都更新到最新版本。修改了部分 tests.js, 以保证主流浏览器下所有用例都能得到正确结果。</p>
<p>特别修改了 YUI 2.8 的测试文件：<a href="http://lifesinger.org/labs/taskspeed/tests/yui-tests.js">yui-tests.js</a>. 原测试文件大量使用<code>YAHOO.util.Selector</code>这个并不推荐使用的类，不符合 YUI 2 的实际使用情况（一般仅使用 yahoo-dom-event, get, anim 等 utilities）。</p>
<p>从测试结果可以看出：</p>
<p>前三甲是 Dojo 1.4, jQuery 1.4, YUI 2.8<br />
MooTools 在 Safari 下表现优异，其它浏览器下有些惨淡。<br />
Prototype 老矣，尚能饭否？<br />
YUI 3 中规中矩，不出色，但也不糟糕。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/01/jslib-dom-performance-test/feed/</wfw:commentRss>
		<slash:comments>10</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>

		<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>YUI3 DOM Manipulation Performance Test</title>
		<link>http://lifesinger.org/blog/2010/01/yui3-dom-manipulation-performance-test/</link>
		<comments>http://lifesinger.org/blog/2010/01/yui3-dom-manipulation-performance-test/#comments</comments>
		<pubDate>Sun, 10 Jan 2010 09:00:08 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=2373</guid>
		<description><![CDATA[测试场景：给 table 的 150 个 tr 添加style="color: #ccc", 重复 20 次。
测试页面：dom-manipulation.html
点击每个测试按钮前，都重新启动浏览器。手工记录 10 * 4 * 4 次无明显偏差的数据，取平均值，可得到以下统计结果：

其它 DOM 操作结果类似。
结论：当页面较复杂，DOM 操作较多时，YUI3 会明显增加脚本执行时间，存在性能隐患。

原因分析
测试代码：

// jQuery 1.4
$("#test-table tr").css("color", "#ccc");

// YUI 3
Y.all("#test-table tr").setStyle("color", "#ccc");

用 Firebug 跟踪调用：
jQuery 基本调用链为：Sizzle &#8211; access(让 collection 有 css 方法) &#8211; css.
YUI 3 的调用链更复杂，甚至出现了 docHeight() 方法的调用。阅读源码可推测基本调用链为：Query &#8211; NodeList &#8211; Node &#8211; setStyle.
可以看出执行性能有三个方面：选择器性能、内部调用过程 和 setStyle 原子操作性能。
对于选择器来说，简单如#test-table tr, 经测试两者相差很小。
纯 [...]]]></description>
			<content:encoded><![CDATA[<p>测试场景：给 table 的 150 个 tr 添加<code>style="color: #ccc"</code>, 重复 20 次。<br />
测试页面：<a href="http://lifesinger.googlecode.com/svn/trunk/lab/2010/yui3-performance-test/dom-manipulation.html">dom-manipulation.html</a></p>
<p>点击每个测试按钮前，都重新启动浏览器。手工记录 10 * 4 * 4 次无明显偏差的数据，取平均值，可得到以下统计结果：<br />
<img width="550" height="319" src="http://lifesinger.googlecode.com/svn/trunk/lab/2010/yui3-performance-test/dom-manipulation-spent-time-comparison.png" alt="dom manipulation spent time comparison chart" /></p>
<p>其它 DOM 操作结果类似。</p>
<p>结论：当页面较复杂，DOM 操作较多时，YUI3 会明显增加脚本执行时间，存在性能隐患。<br />
<span id="more-2373"></span></p>
<h4>原因分析</h4>
<p>测试代码：</p>
<pre>
// jQuery 1.4
$("#test-table tr").css("color", "#ccc");

// YUI 3
Y.all("#test-table tr").setStyle("color", "#ccc");
</pre>
<p>用 Firebug 跟踪调用：<br />
jQuery 基本调用链为：Sizzle &#8211; access(让 collection 有 css 方法) &#8211; css.<br />
YUI 3 的调用链更复杂，甚至出现了 docHeight() 方法的调用。阅读源码可推测基本调用链为：Query &#8211; NodeList &#8211; Node &#8211; setStyle.</p>
<p>可以看出执行性能有三个方面：选择器性能、内部调用过程 和 setStyle 原子操作性能。</p>
<p>对于选择器来说，简单如<code>#test-table tr</code>, 经测试两者相差很小。<br />
纯 setStyle 操作，两者也差不多。<br />
可以判断：性能差异来自内部调用过程的设计。</p>
<h4>思考</h4>
<p>YUI3 的整体设计很漂亮，体现了 scalable（可大可小，能适用于小型站点也能搭建大型复杂应用） 的愿景。但这个 scalable 特性，目前看起来，仅体现在代码的模块组织以及灵活的组件加载和沙箱机制上。对于代码功能本身来说，YUI3 是个庞然大物。简单的功能，也需要历经设计优雅但执行漫长的内部调用过程。</p>
<p>感觉 YUI3 实际走向的是 ExtJS 路线。对于小型站点和快速应用来说，还是 jQuery 和 YUI2 好使。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2010/01/yui3-dom-manipulation-performance-test/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
