<?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>Sat, 11 Jul 2009 14:44:56 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>YUI 3 学习笔记：node</title>
		<link>http://lifesinger.org/blog/2009/07/yui3-node/</link>
		<comments>http://lifesinger.org/blog/2009/07/yui3-node/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 14:48:15 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[Node]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1960</guid>
		<description><![CDATA[node 包括 7 个子模块：

node-base, node-style 和 node-screen 分别封装了 dom-base, dom-style, dom-screen.
node-event-delegate 和 node-event-simulate 为 Node 提供了代理和模拟事件的能力。
node-aria 是挺有意思的一个子模块。非常简单的几行代码，使得 Node 可以便捷地支持 WAI-ARIA 定义的成员。（感慨国外对残障人士的关心）
nodelist 模块，提供批处理能力。

node 还有两个 plugins: node-focusmanager 和 node-menunav.
阅读细节代码时，一个很深的印象是代码的分离技巧。比如Y.Node.ATTRS, 简单约定和对象组合，让代码很灵活，各个子模块能方便地解耦。具体细节将在下一期学习笔记 ( component framework ) 中挖掘。
快乐学习，努力分享，欢迎讨论。
]]></description>
			<content:encoded><![CDATA[<p>node 包括 7 个子模块：</p>
<ol>
<li>node-base, node-style 和 node-screen 分别封装了 dom-base, dom-style, dom-screen.</li>
<li>node-event-delegate 和 node-event-simulate 为 Node 提供了代理和模拟事件的能力。</li>
<li>node-aria 是挺有意思的一个子模块。非常简单的几行代码，使得 Node 可以便捷地支持 <a href="http://www.w3.org/TR/wai-aria/">WAI-ARIA</a> 定义的成员。（感慨国外对残障人士的关心）</li>
<li>nodelist 模块，提供批处理能力。</li>
</ol>
<p>node 还有两个 plugins: node-focusmanager 和 node-menunav.</p>
<p>阅读细节代码时，一个很深的印象是代码的分离技巧。比如<code>Y.Node.ATTRS</code>, 简单约定和对象组合，让代码很灵活，各个子模块能方便地解耦。具体细节将在下一期学习笔记 ( component framework ) 中挖掘。</p>
<p>快乐学习，努力分享，欢迎讨论。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/07/yui3-node/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>fixed 定位在 IE6 下的实现技巧一则</title>
		<link>http://lifesinger.org/blog/2009/07/ie6-fixed-position/</link>
		<comments>http://lifesinger.org/blog/2009/07/ie6-fixed-position/#comments</comments>
		<pubDate>Thu, 09 Jul 2009 09:08:50 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[fixed]]></category>
		<category><![CDATA[Hack]]></category>
		<category><![CDATA[ie6]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1951</guid>
		<description><![CDATA[从蓝色理想看到这贴：固定在右下角的离奇方法
受上面帖子的启发，也做了一个demo：ie6_fixed_position.html
代码：

&#60;div id="a" style="width: 300px; height: 100px; background: red"&#62;test&#60;/div&#62;
&#60;script&#62;
    var ie6 = !window.XMLHttpRequest;

    var a = document.getElementById('a');
    a.style.position = ie6 ? 'absolute' : 'fixed';
    a.style.right = 0;
    a.style.bottom = 0;

    if (ie6) {
      [...]]]></description>
			<content:encoded><![CDATA[<p>从蓝色理想看到这贴：<a href="http://bbs.blueidea.com/thread-2938030-1-1.html">固定在右下角的离奇方法</a></p>
<p>受上面帖子的启发，也做了一个demo：<a href="http://lifesinger.org/lab/2009/ie6_fixed_position.html">ie6_fixed_position.html</a></p>
<p>代码：</p>
<pre>
&lt;div id="a" style="width: 300px; height: 100px; background: red"&gt;test&lt;/div&gt;
&lt;script&gt;
    var ie6 = !window.XMLHttpRequest;

    var a = document.getElementById('a');
    a.style.position = ie6 ? 'absolute' : 'fixed';
    a.style.right = 0;
    a.style.bottom = 0;

    if (ie6) {
        window.onscroll = function() {
            a.className = a.className;
        };
    }
&lt;/script&gt;
</pre>
<p>原理分析：</p>
<ol>
<li><code>position: absolute</code>的元素，直接放在<code>position:static</code>的 body 中时，绝对定位的参考物是 body 的 viewport 部分。这使得元素 a 在窗口 resize 时，自动就能准确定位到右下角。注意：body 的 position 不能为 relative 等值，否则失效。</li>
<li>onscroll 时需要特殊处理一下，使得滚动时，也让绝对定位元素 a 的参考物能更新为当前的 viewport. 上面的<code>a.className = a.className</code>就是这样一个 hack: 使得 a 的定位参考物动态更新为当前的 viewport. 类似的 hack 还有<code>a.style.background = 'red'</code>. 这些 hack 会引发 reflow, 但反之不一定，进一步的规律没找到，不过有一个 hack，也就够用了。</li>
</ol>
<p>耐心总结，快乐分享，欢迎讨论。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/07/ie6-fixed-position/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>YUI 3 学习笔记：event</title>
		<link>http://lifesinger.org/blog/2009/07/yui3-event/</link>
		<comments>http://lifesinger.org/blog/2009/07/yui3-event/#comments</comments>
		<pubDate>Thu, 09 Jul 2009 06:27:55 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[Event]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1944</guid>
		<description><![CDATA[event 模块包括 event, event-custom, event-simulate 三部分，类图如下：



Event 类的调用融合在 Node 中，和 Loader 与 YUI().use 的关系类似。
任何对象，只要扩展 EventTarget 接口，就自动具有了一套事件管理机制。使用很简单：

YUI().use('event', function(Y) {
    function Pig() {
        // 公布事件
        this.publish('farting', {
            emitFacade: true,
  [...]]]></description>
			<content:encoded><![CDATA[<p>event 模块包括 event, event-custom, event-simulate 三部分，类图如下：<br />
<a href="http://lifesinger.org/blog/wp-content/uploads/2009/07/yui-event.png"><img src="http://lifesinger.org/blog/wp-content/uploads/2009/07/yui-event-thumb.png" alt="" /></a><br />
<span id="more-1944"></span></p>
<ol>
<li>Event 类的调用融合在 Node 中，和 Loader 与 YUI().use 的关系类似。</li>
<li>任何对象，只要扩展 EventTarget 接口，就自动具有了一套事件管理机制。使用很简单：
<pre>
YUI().use('event', function(Y) {
    function Pig() {
        // 公布事件
        this.publish('farting', {
            emitFacade: true,
            defaultFn: function() {
                alert('猪猪放屁啦');
            }
        });
    }
    Pig.prototype.fart = function() {
        // 触发事件
        this.fire('farting');
    };
    Y.augment(Pig, Y.EventTarget);

    var pig = new Pig();
    // 订阅事件
    pig.on('farting', function(e) {
        e.preventDefault();
        alert('小猪猪乖，不放屁');
    });
    pig.fart();
});
</pre>
</li>
<li>CustomEvent 很完整的实现了一套事件管理机制，对象的冒泡、立即停止、订阅器的先进先出和取消执行等等，YUI 3 都支持。</li>
<li>Do 实现了一套简单的 AOP 机制。</li>
<li>DOMEventFacade 对原生 e 做了封装，使用起来更自然，很舒服。</li>
<li>Y.Env.evt.plugins 是事件处理的扩展钩子。在 YUI 3 的代码中，这种扩展和分离机制用得比较多。</li>
</ol>
<p>快乐学习，欢迎讨论。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/07/yui3-event/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>YUI 3 学习笔记：dom</title>
		<link>http://lifesinger.org/blog/2009/07/yui3-dom/</link>
		<comments>http://lifesinger.org/blog/2009/07/yui3-dom/#comments</comments>
		<pubDate>Mon, 06 Jul 2009 07:54:18 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1936</guid>
		<description><![CDATA[YUI Dom 模块的层次结构很清楚：

展开的详细图：yui-dom-full.png

一点心得

很清晰的 submodules 组织方式，各个子模块分工明确，源码明朗如皓月，看得心里很晴天。
selector-css3 采用的是 plugin 方式，在 selector-css2 里预留了扩展接口，css3 里直接扩充伪类和操作符。这种可扩展写法在细节代码里也被多次用到，比如针对 IE 的某些兼容代码的写法。代码独立，可增可砍，很 cute.
小颗粒的灵活也是有代价的。各个 submodules 之间，有些许重复代码。值还是不值，很难评说。
三元运算符是个好东西：

getText: (document.documentElement.textContent !== undefined) ?
    function(element) {
        var ret = '';
        if (element) {
          [...]]]></description>
			<content:encoded><![CDATA[<p>YUI Dom 模块的层次结构很清楚：<br />
<img alt="yui-dom.png" src="http://lifesinger.org/blog/wp-content/uploads/2009/07/yui-dom.png" /><br />
展开的详细图：<a href="http://lifesinger.org/blog/wp-content/uploads/2009/07/yui-dom-full.png">yui-dom-full.png</a><br />
<span id="more-1936"></span></p>
<h4>一点心得</h4>
<ol>
<li>很清晰的 submodules 组织方式，各个子模块分工明确，源码明朗如皓月，看得心里很晴天。</li>
<li>selector-css3 采用的是 plugin 方式，在 selector-css2 里预留了扩展接口，css3 里直接扩充伪类和操作符。这种可扩展写法在细节代码里也被多次用到，比如针对 IE 的某些兼容代码的写法。代码独立，可增可砍，很 cute.</li>
<li>小颗粒的灵活也是有代价的。各个 submodules 之间，有些许重复代码。值还是不值，很难评说。</li>
<li>三元运算符是个好东西：
<pre>
getText: (document.documentElement.textContent !== undefined) ?
    function(element) {
        var ret = '';
        if (element) {
            ret = element.textContent;
        }
        return ret || '';
    } : function(element) {
        var ret = '';
        if (element) {
            ret = element.innerText;
        }
        return ret || '';
    }
</pre>
<p>在不同的浏览器下，getText 指向不同的函数，而且三元运算符中的判断只需执行一次。<br />
类似还有一种写法：</p>
<pre>
_childrenByTag: function() {
    if (document[DOCUMENT_ELEMENT].children) {
        return function(element, tag, fn, toArray) {
            ...
        };
    } else {
        return function(element, tag, fn) {
            ...
        };
    }
}()
</pre>
<p>返回函数的函数挺好用。
</li>
<li>看源码前，会尝试回答一个问题：如何我来设计 DOM 的 API, 会画出怎样一副图来？最后将自己设计的 API 和 YUI3 的对照，我的设计里，功能比较全，但层次分类和清晰度上，YUI3 的明显好很多。从总体上比较和学习 API 的设计，别有一番迷人的天地。</li>
<li>这次看代码，没怎么深究到代码细节。一直提醒自己从大局上去看。舍得舍得，有所舍才有所得，的确如此。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/07/yui3-dom/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>onload 和 DOMContentLoaded 的陷阱</title>
		<link>http://lifesinger.org/blog/2009/07/onload-and-domcontentloaded/</link>
		<comments>http://lifesinger.org/blog/2009/07/onload-and-domcontentloaded/#comments</comments>
		<pubDate>Fri, 03 Jul 2009 08:33:35 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[DOMContentLoaded]]></category>
		<category><![CDATA[onload]]></category>
		<category><![CDATA[setTimeout]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1922</guid>
		<description><![CDATA[在正常情况下：

window.onload 事件在所有页面元素（包括图片，脚本等）都下载完毕后才会触发。
除了 IE，其它浏览器支持 DOMContentLoaded 事件。当 DOM 内容下载完毕，就会立刻触发。
事件处理器必须在事件触发前注册才有效，否则不会被执行。例子：在 window 已经 load 完成后，再给 window.onload 注册的处理器不会被执行。
针对 IE，有各种模拟 DOMContentLoaded 事件的办法。目前被广泛采纳的方案是判断 document 是否可以滚动（doScroll）。一旦可以滚动，就意味着 DOM Content 已经加载完毕。

下面说两个陷阱：

在 setTimeout 里给 window.onload 添加处理器。这是不可靠的，无法保证添加的处理器一定会执行。是否执行取决于 setTimeout 的延时和页面内容的加载时间。比如：

// Test 1. 在 onload 后添加 onload handler
setTimeout(function() {
    onLoad(function() {
        alert('Test 1: window loaded.'); // 不一定执行
   [...]]]></description>
			<content:encoded><![CDATA[<p>在正常情况下：</p>
<ol>
<li>window.onload 事件在所有页面元素（包括图片，脚本等）都下载完毕后才会触发。</li>
<li>除了 IE，其它浏览器支持 DOMContentLoaded 事件。当 DOM 内容下载完毕，就会立刻触发。</li>
<li>事件处理器必须在事件触发前注册才有效，否则不会被执行。例子：在 window 已经 load 完成后，再给 window.onload 注册的处理器不会被执行。</li>
<li>针对 IE，有各种模拟 DOMContentLoaded 事件的办法。目前被广泛采纳的方案是判断 document 是否可以滚动（doScroll）。一旦可以滚动，就意味着 DOM Content 已经加载完毕。</li>
</ol>
<p>下面说两个陷阱<span id="more-1922"></span>：</p>
<ol>
<li>在 setTimeout 里给 window.onload 添加处理器。这是不可靠的，无法保证添加的处理器一定会执行。是否执行取决于 setTimeout 的延时和页面内容的加载时间。比如：
<pre>
// Test 1. 在 onload 后添加 onload handler
setTimeout(function() {
    onLoad(function() {
        alert('Test 1: window loaded.'); // 不一定执行
    });
}, 20);
</pre>
</li>
<li>在动态加载的 script 的 onload 事件里，给 window.DOMContentLoaded 添加处理器。这更不可靠，除了 IE，其它浏览器下根本不会执行。比如：
<pre>
// Test 2. 测试动态添加 script
getScript('http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js', function() {
   alert('Test 2: jQuery loaded.');
    onReady(function() { alert('Test 2: content loaded.'); }); // 不可靠
});
</pre>
</li>
</ol>
<p>测试页面：<a href="http://lifesinger.org/lab/2009/load_test.html">load_test.html</a></p>
<p>最后提一点：从上面的测试页面中可以看出，IE 下模拟 DOMContentLoaded 事件仅仅是模拟，和其它浏览器依旧存在本质上的差异。在 IE 真正原生支持 DOMContentLoaded 前，疯狂使用<code>$(document).ready(...), YAHOO.util.Event.onDOMReady(...)</code>等方法时，依旧需要谨慎，要留意陷阱。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/07/onload-and-domcontentloaded/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>YUI 3 学习笔记：oop</title>
		<link>http://lifesinger.org/blog/2009/06/yui3-oop/</link>
		<comments>http://lifesinger.org/blog/2009/06/yui3-oop/#comments</comments>
		<pubDate>Tue, 30 Jun 2009 14:37:13 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1900</guid>
		<description><![CDATA[JavaScript 是一门基于原型的面向对象语言，它是无类（class-free）的。
OOP 是 YUI 的核心代码，代码非常精简：

上面的 7 个方法，加上在 YUI~base 模块里的mix, merge, cached3 个方法，是 YUI 3 代码组织中的 10 个核心方法。
基础知识点
详细解析代码前，先复习下 JavaScript 的一些基础知识点：

在 JavaScript 中，所有对象 o 都拥有一个隐藏的原型对象（在 Firefox 中是 o.__proto__）。该隐藏原型对象拥有一个 constructor 成员，指向该对象的构造函数。当读取对象成员 o.member 时，会顺着原型链往上回溯。因此我们可以得到o.constructor === o.__proto__.constructor. 这是最基本的知识点，不多说。
在 JavaScript 中，所有函数声明在解析后，都自动拥有一个 prototype 成员。该 prototype 成员拥有一个自动添加的 constructor 成员，指向函数本身。也就是Fn === Fn.prototype.constructor.
函数 Fn 本身也是对象，因此Fn.constructor === Fn.__proto__.constructor, 注意Fn.__proto__ !== Fn.prototype, 千万不要混淆了。
var fn = new Fn()，在 [...]]]></description>
			<content:encoded><![CDATA[<p>JavaScript 是一门基于原型的面向对象语言，它是无类（class-free）的。</p>
<p>OOP 是 YUI 的核心代码，代码非常精简：<br />
<img src="http://lifesinger.org/blog/wp-content/uploads/2009/06/yui-oop.png" alt="YUI OOP" /></p>
<p>上面的 7 个方法，加上在 YUI~base 模块里的<code>mix, merge, cached</code>3 个方法，是 YUI 3 代码组织中的 10 个核心方法。</p>
<h3>基础知识点</h3>
<p>详细解析代码前，先复习下 JavaScript 的一些基础知识点<span id="more-1900"></span>：</p>
<ol>
<li>在 JavaScript 中，所有对象 o 都拥有一个隐藏的原型对象（在 Firefox 中是 o.__proto__）。该隐藏原型对象拥有一个 constructor 成员，指向该对象的构造函数。当读取对象成员 o.member 时，会顺着原型链往上回溯。因此我们可以得到<code>o.constructor === o.__proto__.constructor</code>. 这是最基本的知识点，不多说。</li>
<li>在 JavaScript 中，所有函数声明在解析后，都自动拥有一个 prototype 成员。该 prototype 成员拥有一个自动添加的 constructor 成员，指向函数本身。也就是<code>Fn === Fn.prototype.constructor</code>.</li>
<li>函数 Fn 本身也是对象，因此<code>Fn.constructor === Fn.__proto__.constructor</code>, 注意<code>Fn.__proto__ !== Fn.prototype</code>, 千万不要混淆了。</li>
<li><code>var fn = new Fn()</code>，在 Firefox 下，不考虑参数传递，可以用下面的代码来表示 new 的过程：
<pre>
var o = {__proto__: Fn.prototype};
Fn.apply(o);
fn = o;
</pre>
</li>
<li>上面第 4 点是第 1 点的原因，因为任何一个对象都源自函数构造器，比如<code> var a = {}</code> 可以等价为：
<pre>
var o = {__proto__: Object.prototype};
Object.apply(o);
a = o;
</pre>
<p>因此所有对象都具有 constructor 成员。
</li>
</ol>
<p>上面 5 点可以归结为 2 点：<strong>构造函数的实例化过程和对象成员的原型链回溯机制</strong>。理解了这两点，市面上 JavaScript 的各种各样 OOP 机制，比如 Dean Edwards 的 Base.extend，MooTools 里的 new Class, 以及《悟透 JavaScript》里的甘露模型等等，就都能轻松轻松理解了。</p>
<h3>YUI extend</h3>
<p>对于 oop, YUI 让人喜悦的是，仅做了非常简洁的封装：</p>
<pre>
Y.extend = function(r, s, px, sx) {
    if (!s || !r) {
        // @TODO error symbols
        Y.error("extend failed, verify dependencies");
    }

    var sp = s.prototype, rp = Y.Object(sp);
    r.prototype = rp;

    rp.constructor = r;
    r.superclass = sp;

    // assign constructor property
    if (s != Object &#038;&#038; sp.constructor == OP.constructor) {
        sp.constructor = s;
    }

    // add prototype overrides
    if (px) {
        Y.mix(rp, px, true);
    }

    // add object overrides
    if (sx) {
        Y.mix(r, sx, true);
    }

    return r;
};
</pre>
<p>基本原理不废话了，可以参考 <a href="http://www.kevlindev.com/tutorials/javascript/inheritance/index.htm">Kevin 的这篇文章</a>。</p>
<p>使用方式很 JavaScript, 很简单：</p>
<pre>
YUI().use('oop', function(Y) {
    var Bird = function(name) {
        this.name = name;
    };
    Bird.prototype.getName = function(){ return this.name; };

    var Chicken = function(name) {
        Chicken.superclass.constructor.call(this, name);
    };
    Y.extend(Chicken, Bird);

    var chicken = new Chicken('Tom');
    Y.log(chicken.getName());
});
</pre>
<p>supperclass 有两个作用：一是可以用来调用父类的方法，二是可以通过 supperclass.constructor 调用父类的构造函数。一举两得，一旦理解了就非常好用。</p>
<p>extend 方法中，还有一段看起来无用的代码：</p>
<pre>
    // assign constructor property
    if (s != Object &#038;&#038; sp.constructor == OP.constructor) {
        sp.constructor = s;
    }
</pre>
<p>这段代码，正常情况下，的确是不可达的死代码。然而我们会经常不小心地这样组织代码：</p>
<pre>
YUI().use('oop', function(Y) {
    var Bird = function(name) {
        this.name = name;
    };
<strong>    Bird.prototype = {
        getName: function(){ return this.name; }
    };
</strong>
    var Chicken = function(name) {
        Chicken.superclass.constructor.call(this, name);
    };
    Y.extend(Chicken, Bird);

    var chicken = new Chicken('Tom');
    Y.log(chicken.getName());
});
</pre>
<p>注意加粗部分，直接给 prototype 赋值，会使得自动添加的 constructor 成员丢失。为了避免这种很容易犯的错误，在 extend 方法里，很人性化地为我们做了纠正工作。</p>
<p>简洁优美、小巧灵活的 extend, 是我最喜欢的 JavaScript 继承封装。</p>
<h3>YUI augment</h3>
<p>接下来一个有意思的方法是 augment. 如果说 extend 是实现类继承机制，augment 则是实现了接口。并且这种接口，还不需要自己去写实现代码，直接就借用过来了^o^</p>
<p>augment 源码里最难理解的是为何要对 function 成员做一层封装。其实也很简单，我们看一个例子：</p>
<pre>
YUI().use('oop', function(Y) {
    var Bird = function() {
        this.canFly = true;
    };
    Bird.prototype.canFly = function() { return this.canFly; };

    var Pig = function() {};
    Y.augment(Pig, Bird);

    var pig = new Pig();
    Y.log(pig.canFly());
});
</pre>
<p>如果 augment 方法里，没有以下封装代码：</p>
<pre>
replacements[k] = function() {
    for (i in sequestered) {
        if (sequestered.hasOwnProperty(i) &#038;&#038; (this[i] === replacements[i])) {
            this[i] = sequestered[i];
        }
    }
    // apply the constructor
    construct.apply(this, a);
    // apply the original sequestered function
    return sequestered[k].apply(this, arguments);
};
</pre>
<p>则 pig.canFly() 返回的就是 undefined 了。因为 pig 对象没有 canFly 属性。</p>
<p>有了以上封装代码，才能保证 augment 过来的方法确实可用，从而使得我们能很方便地构造出一头能飞的猪！</p>
<h3>其它方法</h3>
<p>其它几个方法都很容易理解，不多说。其中 bind 方法是一个返回函数的函数，巧妙利用可以非常灵活地实现非常精妙的代码，请参考这篇文章：<a href="http://lifesinger.org/blog/2008/11/javascript-arguments/">JavaScript 中的 arguments</a></p>
<h3>最后，闲话</h3>
<p>jQuery, MooTools 等框架，我们在使用时，先要学会它们自身的语法，比如 MooTools 的 new Class 机制等。学习成本高，而且可移植性差（比如学会 jQuery，对学习 MooTools 或原生 JavaScript 没啥很大帮助）。</p>
<p>但 YUI 不同。YUI 的风格是，让我们能像写原生 JavaScript 一样去使用 YUI. 无需掌握过多框架自身引入的语法。这意味着，用 YUI 用得越熟练，对 JavaScript 本身的理解也会越深入。YUI 是一种辅助增强，但不破坏 JavaScript 固有的美好和缺点。这是 YUI 最诱人的地方。</p>
<p>快乐学习，欢迎讨论。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/06/yui3-oop/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>YUI 3 学习笔记：loader</title>
		<link>http://lifesinger.org/blog/2009/06/yui3-loader/</link>
		<comments>http://lifesinger.org/blog/2009/06/yui3-loader/#comments</comments>
		<pubDate>Sat, 27 Jun 2009 09:14:42 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1890</guid>
		<description><![CDATA[YUI 3 的 loader 已经很优雅地融合在YUI(config).use('moduleName', callback)中：

YUI({
   base: 'http://t-yubo/assets/yui/3.0.0/build/',
   debug: true,
   filter: 'debug',
   modules: {
       jquery: {
           fullpath: 'http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js'
       }
   }
}).use('jquery', 'node', function(Y) {
 [...]]]></description>
			<content:encoded><![CDATA[<p>YUI 3 的 loader 已经很优雅地融合在<code>YUI(config).use('moduleName', callback)</code>中：</p>
<pre>
YUI({
   base: 'http://t-yubo/assets/yui/3.0.0/build/',
   debug: true,
   filter: 'debug',
   modules: {
       jquery: {
           fullpath: 'http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js'
       }
   }
}).use('jquery', 'node', function(Y) {
   jQuery('body').text('YUI kisses jQuery!');
});
</pre>
<p><code>config</code>参数，在 yui-base 模块里，原封不动地传给了 Loader：<span id="more-1890"></span></p>
<pre>
// use loader to expand dependencies and sort the
// requirements if it is available.
if (Y.Loader) {
    dynamic = true;
    this._useQueue = this._useQueue || new Y.Queue();
    loader = new Y.Loader(Y.config);
    loader.require(a);
    loader.ignoreRegistered = true;
    loader.allowRollup = false;
    loader.calculate();
    a = loader.sorted;
}
</pre>
<p>注意 debug 和 filter 等参数值，用来切换调试状态非常方便。另外，通过 fullpath, 可以很方便加载任意 js/css 文件。</p>
<h4>Loader 的基本工作原理</h4>
<p>1. 将 YUI 自带的所有 modules 信息，存放在一个很大的数据对象里：</p>
<pre>
modules = {
    moduleName: {
        requires: [...],
        optional: [...],
        skinnable: true,
        submodules: { ... },
        plugins: { ... }
    },
    ...
}
</pre>
<p>2. 根据传入参数和上面的数组，将需要加载的模块按依赖关系排好顺序。核心方法是<code>calculate</code>:</p>
<pre>
calculate: function(o) {
    if (o || this.dirty) {
        this._config(o);
        this._setup();
        this._explode();
        if (this.allowRollup &#038;&#038; !this.combine) {
            this._rollup();
        }
        this._reduce();
        this._sort();

        // Y.log("after calculate: " + this.sorted);

        this.dirty = false;
    }
</pre>
<p>封装成了 6 个辅助方法，其中<code>_sort</code>方法最考验算法。</p>
<p>3. 按照依赖关系排好顺序的模块信息存放在<code>this.sorted</code>属性中。接下来，调用 get 模块加载即可，具体请参考源码中的<code>_insert, _continue, loadNext</code>等方法。</p>
<p>以上是主线，还有很多细节和分支就不多说了。loader 总共 2000 多行代码，相当不易。</p>
<p>快乐学习，欢迎讨论。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/06/yui3-loader/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>YUI 3 学习笔记：get</title>
		<link>http://lifesinger.org/blog/2009/06/yui3-get/</link>
		<comments>http://lifesinger.org/blog/2009/06/yui3-get/#comments</comments>
		<pubDate>Fri, 26 Jun 2009 09:43:54 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1887</guid>
		<description><![CDATA[YUI 团队非常勤快，已经撰写了一份很详细的文档：The Get Utility. 还制作了 Cheat Sheet PDF.
上面的文档非常详细，我都不知道写什么了。下面仅说几个注意点：
1. 上面的文档中，提到的 onProgress 事件，在实际源码中还没实现。
2. 注意以下两种写法不等价：

// 写法 A：
Y.Get.script(['1.js', '2.js']);
// 写法 B：
Y.Get.script('1.js');
Y.Get.script('2.js');

写法 A 是在加载完 1.js 后，再加载 2.js, 是同步依次加载。
写法 B 是异步同时加载 1.js 和 2.js.
3. 对于 css 文件，由于 Firefox, Safari 和 Chrome 下 link 元素无 onload 事件，因此在这些浏览器下，onSuccess 等事件仅表示 css 文件都已经开始下载，但可能并没下载完，而且下载顺序也不一定。
4. 在源码里，还存在某些残留的无用代码，比如q.varName, 源码注释和 Cheat Sheet 也都需要更新。beta1 到正式版，还有不少路要走。
5. YUI 的源码有个非常好的习惯，将字符串常量定义在起始处：

var TYPE_JS    [...]]]></description>
			<content:encoded><![CDATA[<p>YUI 团队非常勤快，已经撰写了一份很详细的文档：<a href="http://developer.yahoo.com/yui/3/get/">The Get Utility</a>. 还制作了 <a href="http://yuiblog.com/assets/pdf/cheatsheets/get.pdf">Cheat Sheet PDF</a>.</p>
<p>上面的文档非常详细，我都不知道写什么了。下面仅说几个注意点：</p>
<p>1. 上面的文档中，提到的 onProgress 事件，在实际源码中还没实现。</p>
<p>2. 注意以下两种写法不等价：</p>
<pre>
// 写法 A：
Y.Get.script(['1.js', '2.js']);
// 写法 B：
Y.Get.script('1.js');
Y.Get.script('2.js');
</pre>
<p>写法 A 是在加载完 1.js 后，再加载 2.js, 是同步依次加载。<br />
写法 B 是异步同时加载 1.js 和 2.js.<span id="more-1887"></span></p>
<p>3. 对于 css 文件，由于 Firefox, Safari 和 Chrome 下 link 元素无 onload 事件，因此在这些浏览器下，onSuccess 等事件仅表示 css 文件都已经开始下载，但可能并没下载完，而且下载顺序也不一定。</p>
<p>4. 在源码里，还存在某些残留的无用代码，比如<code>q.varName</code>, 源码注释和 Cheat Sheet 也都需要更新。beta1 到正式版，还有不少路要走。</p>
<p>5. YUI 的源码有个非常好的习惯，将字符串常量定义在起始处：</p>
<pre>
var TYPE_JS    = "text/javascript",
     TYPE_CSS   = "text/css",
     STYLESHEET = "stylesheet";
</pre>
<p>这能方便维护，同时能提高 YUICompressor 的压缩率。</p>
<p>6. 默认情况下，js 文件下载执行完成后，会自动 purge 掉。css 不会。考虑到删除节点的性能损耗，在需要获取大量 script 的情景中，比如获取搜索提示的 jsonp数据，建议将 autopurge 设为 false. </p>
<p>7. Douglas 《JavaScript: The Good Parts》3.2 节里提到：</p>
<pre>
The || operator can be used to fill in default values:

var middle = stooge["middle-name"] || "(none)";
var status = flight.status || "unknown";
</pre>
<p>这个说法并不严谨。因为<code>stooge["middle-name"]</code>可能为 falsy 值：&#8221;", false, 0, undefined, null, NaN<br />
更严谨的写法如下：</p>
<pre>
var middle = ("middle-name" in stooge) ? stooge["middle-name"] : "(none)";
var status = (typeof fligt.status != "undefined") ? flight.status : "unknown";
</pre>
<p>注意：在大部分代码逻辑里，用<code>o = o || {}</code>来设默认值已经足够强健，不需要采用严谨写法。<br />
YUI get 里采用了严谨写法，因为是底层框架，不得不非常小心。</p>
<p>8. 感觉 get 模块最难处理的，是妥善的组织好事件代码，在合适的时机触发合适的事件。事件一多，还要考虑浏览器兼容，要写好真不容易。（测试相当重要）</p>
<p>9. get 模块里，函数都是小颗粒的，每个函数的职责清晰单一，佩服作者的代码组织和重构功底。</p>
<p>10. yui-base 里新增加了 queue-base 组件，get 组件可以使用 queue-base 封装 queues，能稍微减少些代码。不过目前保持独立也挺好的，毕竟 queue-base 是否会保留在 YUI seed 里，还不一定。</p>
<p>学习完毕，欢迎讨论。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/06/yui3-get/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>YUI 3 学习笔记：queue-base</title>
		<link>http://lifesinger.org/blog/2009/06/yui3-queue-base/</link>
		<comments>http://lifesinger.org/blog/2009/06/yui3-queue-base/#comments</comments>
		<pubDate>Thu, 25 Jun 2009 13:19:59 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[queue-base]]></category>
		<category><![CDATA[YUI3]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1883</guid>
		<description><![CDATA[今天 YUI 3 发布了 beta 1, 实在是高兴。继续学习源码：queue-base.

一点心得

yui3 beta1 的 yui-seed, 与pr1 的架构图已经有了稍许不同：增加了 queue-base 模块。
queue-base 的源码非常简单，就是一个包装后的数组。封装后的接口非常有 queue 的特性：add, next, size. 是一个 FIFO Queue.
插播：在 pr2 时，yui-base 的7个 submodules 是各自分开的，在 beta1 里终于合起来成为一个 module 了。yui-base 合并后的代码，各个 submodule 的闭包可以去除，有稍许优化空间。
YUI 3 这种小颗粒组装的 modules，很灵活，真不错。

]]></description>
			<content:encoded><![CDATA[<p>今天 YUI 3 发布了 beta 1, 实在是高兴。继续学习源码：queue-base.</p>
<p><img src="http://lifesinger.org/blog/wp-content/uploads/2009/06/yui-queue-base.png" alt="yui-quene-base.png" /></p>
<h4>一点心得</h4>
<ol>
<li>yui3 beta1 的 yui-seed, 与<a href="http://yuiblog.com/assets/pdf/yui3architecture.pdf">pr1 的架构图</a>已经有了稍许不同：增加了 queue-base 模块。</li>
<li>queue-base 的源码非常简单，就是一个包装后的数组。封装后的接口非常有 queue 的特性：add, next, size. 是一个 FIFO Queue.</li>
<li>插播：在 pr2 时，yui-base 的7个 submodules 是各自分开的，在 beta1 里终于合起来成为一个 module 了。yui-base 合并后的代码，各个 submodule 的闭包可以去除，有稍许优化空间。</li>
<li>YUI 3 这种小颗粒组装的 modules，很灵活，真不错。</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/06/yui3-queue-base/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Goodbye to CSS Hack</title>
		<link>http://lifesinger.org/blog/2009/06/goodbye-to-css-hack/</link>
		<comments>http://lifesinger.org/blog/2009/06/goodbye-to-css-hack/#comments</comments>
		<pubDate>Tue, 23 Jun 2009 04:59:39 +0000</pubDate>
		<dc:creator>lifesinger</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Ext]]></category>
		<category><![CDATA[Hack]]></category>

		<guid isPermaLink="false">http://lifesinger.org/blog/?p=1879</guid>
		<description><![CDATA[看看下面这段代码，是否倍感亲切但又觉得很陌生呢：

.test {
    background-color: black; /* firefox, opera, ie8 */
    [;background-color: green;] /* safari, chrome */
    *background-color: blue; /* ie7 */
     _background-color: red; /* ie6 */
}
html*~/**/body .test {
    border: 5px solid red; /* ie8 */
}

随着浏览器的更新换代，CSS Hack 变得越来越诡异和不可靠了-.-
相对来说，IE 的条件注释是值得信赖的：

&#60;!--[if lt IE [...]]]></description>
			<content:encoded><![CDATA[<p>看看下面这段代码，是否倍感亲切但又觉得很陌生呢：</p>
<pre>
.test {
    background-color: black; /* firefox, opera, ie8 */
    [;background-color: green;] /* safari, chrome */
    *background-color: blue; /* ie7 */
     _background-color: red; /* ie6 */
}
html*~/**/body .test {
    border: 5px solid red; /* ie8 */
}
</pre>
<p>随着浏览器的更新换代，CSS Hack 变得<a href="http://www.cssforest.org/blog/index.php?id=132">越来越诡异和不可靠</a>了-.-</p>
<p>相对来说，IE 的条件注释是值得信赖的<span id="more-1879"></span>：</p>
<pre>
&lt;!--[if lt IE 7 ]&gt;&lt;body class="ie6"&gt;&lt;![endif]--&gt;
&lt;!--[if IE 7 ]&gt;&lt;body class="ie7"&gt;&lt;![endif]--&gt;
&lt;!--[if IE 8 ]&gt;&lt;body class="ie8"&gt;&lt;![endif]-->&gt;
&lt;!--[if !IE]&gt;--&gt;&lt;body&gt;&lt;!--&lt;![endif]--&gt;

&lt;style type="text/css"&gt;
    body.ie6 .test { background-color: blue }
    body.ie7 .test { background-color: red }
    body.ie8 .test { background-color: green }
&lt;/style&gt;
</pre>
<p>注意，上面的代码虽然也是 hack，但大大简化和规范了 css hack 的写法。</p>
<p>最近看 ExtCore 3.0 的代码，发现 Ext 初始化时会自动给 body 添加浏览器信息：</p>
<pre>
//Initialize doc classes
(function(){

    var initExtCss = function(){
        // find the body element
        var bd = document.body || document.getElementsByTagName('body')[0];
        if(!bd){ return false; }
        var cls = [' ',
                Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
                : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
                : Ext.isOpera ? "ext-opera"
                : Ext.isWebKit ? "ext-webkit" : ""];

        if(Ext.isSafari){
            cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
        }else if(Ext.isChrome){
            cls.push("ext-chrome");
        }

        if(Ext.isMac){
            cls.push("ext-mac");
        }
        if(Ext.isLinux){
            cls.push("ext-linux");
        }
        if(Ext.isBorderBox){
            cls.push('ext-border-box');
        }
        if(Ext.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
            var p = bd.parentNode;
            if(p){
                p.className += ' ext-strict';
            }
        }
        bd.className += cls.join(' ');
        return true;
    }

    if(!initExtCss()){
        Ext.onReady(initExtCss);
    }
})();
</pre>
<p>比如在 IE8 下，会添加以下信息：<br />
<img src="http://lifesinger.org/blog/wp-content/uploads/2009/06/ext-css-hack.png" alt="ext-css-hack.png" /></p>
<p>将浏览器信息添加到 body 和 html 上后，我们书写 CSS Hack 就会变得很容易了。</p>
<p>通过 JS 添加浏览器信息，需要将 JS 放在头部执行，以确保渲染前就添加好。另外，对于不支持 JS 的用户代理，此方法无效。</p>
<p>还有一个方案是在服务器端处理，比如 <a href="http://josephj.com/entry.php?id=249">browscap 方案</a>。优点的是没有 JS 方案的缺点，缺点是遇上 proxy cache 就不准确了。</p>
<p>个人觉得 JS 方案可行。因为禁用 JS 的浏览器用户，自身已是高手，不用我们担心。对于不支持 JS 的用户代理，比如手机等浏览器，则有对应的 WAP 版本给用户使用。这样，我们可以：</p>
<ol>
<li>在 head 部分加载一个非常小巧精简的 js，该 js 在 body 可用时，能立刻给 body 添加浏览器信息。</li>
<li>在 css 里，通过<code>body.ie6 .test { ... }</code>来书写 css hack</li>
</ol>
<p>这样，无论浏览器如何更新换代，我们都不必去记忆或发掘那一堆诡异的 css hack 的写法了。</p>
]]></content:encoded>
			<wfw:commentRss>http://lifesinger.org/blog/2009/06/goodbye-to-css-hack/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
	</channel>
</rss>
