<?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; OOP</title>
	<atom:link href="http://lifesinger.org/blog/tag/oop/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>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__ [...]]]></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>8</slash:comments>
		</item>
	</channel>
</rss>
