<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="http://blog.xole.net/rss/style.css" type="text/css"?>
<rdf:RDF xmlns="http://purl.org/rss/1.0/"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:content="http://purl.org/rss/1.0/modules/content/"
         xmlns:dc="http://purl.org/dc/elements/1.1/"
         xml:lang="ja">
<channel rdf:about="http://blog.xole.net/rss/1.0.php?id=645">
<title>ハタさんのブログ(復刻版)</title>
<link>http://blog.xole.net/index.php</link>
<dc:date>2007-12-17T08:16:56+09:00</dc:date>
<description>
ハタさんのブログ(復刻版) - RSS (RDF Site Summary).
</description>
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://blog.xole.net/article.php?id=645" />
</rdf:Seq>
</items>
</channel>
<item>
<title>javascriptを初めて学ぶ人についてのおさらい。その２</title>
<link>http://blog.xole.net/article.php?id=645</link>
<dc:date>2007-12-17T08:16:56+09:00</dc:date>
<description>
前回のエントリが700users突入しました。ありがとうございます。参考になれば幸いです。

ということで、その２になります。
前回書いた通り、C/Javaについてはある程度の知識がある人なので、クラスなどのオブジェクト指向はちゃん...</description>
<content:encoded>
<![CDATA[
<p>
<a href="http://b.hatena.ne.jp/entry/http://blog.xole.net/article.php?id=640">前回のエントリが700users</a>突入しました。ありがとうございます。参考になれば幸いです。</p>

<p>ということで、その２になります。<br />
<a href="http://blog.xole.net/article.php?id=640">前回書いた</a>通り、C/Javaについてはある程度の知識がある人なので、クラスなどのオブジェクト指向はちゃんと理解されているようですが、プロトタイプ指向は初めて学ぶようです。<br />
javascript(ECMAScript)のプロトタイプは他のプロトタイプ指向言語とはひと味違う動作をするので、その点も含めておさらい</p>

<h3>プロトタイプとはなんですか？プロトタイプとは継承パターンの一つでしかないです</h3>
<p>プロトタイプは単なる継承パターンであり、単一の方向への継承しか行わない点についてはクラスベースと同じです。</p>

<pre class="javascript">
<span class="keyword" >var</span> Hoge = <span class="keyword" >function</span> (){};
Hoge.prototype.methodA = <span class="keyword" >function</span> (){
    <span class="keyword" >return</span> <span class="string" >"this is methodA"</span>;
};
<span class="keyword" >var</span> Foo = <span class="keyword" >function</span> (){};
Foo.prototype = <span class="keyword" >new</span> Hoge;

<span class="keyword" >var</span> foo = <span class="keyword" >new</span> Foo;
alert(foo.methodA()); <span class="comment" >// this is methodA</span>
alert(foo <span class="keyword" >instanceof</span> Foo &amp;&amp; foo <span class="keyword" >instanceof</span> Hoge); <span class="comment" >// true</span>
</pre>

<p>また、上の記述は次のように書く事もできます</p>
<pre class="javascript">
<span class="keyword" >var</span> Hoge = <span class="keyword" >function</span> (){
    <span class="keyword" >this</span>.methodA = <span class="keyword" >function</span> (){
        <span class="keyword" >return</span> <span class="string" >'this is methodA'</span>;
    };
    <span class="keyword" >this</span>.methodB = <span class="keyword" >function</span> (){
        <span class="keyword" >return</span> <span class="string" >'this is methodB'</span>;
    };
};

<span class="keyword" >var</span> Foo = <span class="keyword" >function</span> (){};
Foo.prototype = <span class="keyword" >new</span> Hoge;

<span class="keyword" >var</span> foo = <span class="keyword" >new</span> Foo;
alert(foo.methodA()); <span class="comment" >// this is methodA</span>
alert(foo.methodB()); <span class="comment" >// this is methodB</span>
alert(foo <span class="keyword" >instanceof</span> Hoge &amp;&amp; foo <span class="keyword" >instanceof</span> Foo); <span class="comment" >// true</span>
</pre>

<p>しかし、次のような記述を指定してしまうと、上記の動作とは違ってきます。</p>
<pre class="javascript">
<span class="keyword" >var</span> Hoge = <span class="keyword" >function</span> (){
    <span class="keyword" >this</span>.methodA = <span class="keyword" >function</span> (){
        <span class="keyword" >return</span> <span class="string" >'this is methodA'</span>;
    };  
};
Hoge.prototype.methodB = <span class="keyword" >function</span> (){
    <span class="keyword" >return</span> <span class="string" >'this is methodB'</span>;
};

<span class="keyword" >var</span> Foo = <span class="keyword" >function</span> (){
    <span class="comment" >// apply</span>
    Hoge.apply(<span class="keyword" >this</span>, arguments);
};

<span class="keyword" >var</span> foo = <span class="keyword" >new</span> Foo;
alert(foo.methodA()); <span class="comment" >// this is methodA</span>
alert(foo.methodB); <span class="comment" >// undefined</span>
alert(foo <span class="keyword" >instanceof</span> Hoge &amp;&amp; foo <span class="keyword" >instanceof</span> Foo); <span class="comment" >// false</span>

<span class="comment" >// Fooと同じ</span>
<span class="keyword" >var</span> Bar = <span class="keyword" >function</span> (){};
Hoge.apply(Bar.prototype);

<span class="keyword" >var</span> bar = <span class="keyword" >new</span> Bar;
alert(bar.methodA()); <span class="comment" >// this is methodA</span>
alert(bar.methodB); <span class="comment" >// undefined</span>
alert(bar <span class="keyword" >instanceof</span> Hoge &amp;&amp; bar <span class="keyword" >instanceof</span> Bar); <span class="comment" >// false</span>
</pre>

<p>この違いが生まれることについては、後記</p>

<h3>インスタンスにメソッドをつけてprototypeすることも可能です。特異メソッドのように</h3>

<p>rubyの特異メソッド(以下)では、インスタンスにメソッドを付加することができます。</p>

<pre class="ruby">
<span class="keyword" >class</span> Hoge
    <span class="keyword" >def</span> hello
        <span class="string" >'hello'</span>
    <span class="keyword" >end</span>
<span class="keyword" >end</span>

foo = Hoge.<span class="keyword" >new</span>

p foo.hello <span class="comment" ># hello</span>

<span class="keyword" >def</span> Hoge.world
    <span class="string" >'world'</span>
<span class="keyword" >end</span>

p foo.respond_to?(<span class="string" >'world'</span>) <span class="comment" >#false</span>

<span class="keyword" >def</span> foo.world2
    <span class="string" >'world2'</span>
<span class="keyword" >end</span>

p foo.respond_to?(<span class="string" >'world2'</span>) <span class="comment" >#true</span>
p foo.world2 <span class="comment" ># world2</span>
</pre>

<p>特異メソッドについては、javascriptでも実装可能です。また、特異メソッドをつけたインスタンスをprototypeすることが可能となっています。</p>

<pre class="javascript">
<span class="keyword" >var</span> Hoge = <span class="keyword" >function</span> (){};
Hoge.prototype.hello = <span class="keyword" >function</span> (){
    <span class="keyword" >return</span> <span class="string" >'hello'</span>;
};

<span class="keyword" >var</span> foo = <span class="keyword" >new</span> Hoge;
alert(foo.hello()); <span class="comment" >// hello</span>

foo.world = <span class="keyword" >function</span> (){
    <span class="keyword" >return</span> <span class="string" >'world'</span>;
};  
alert(foo.world()); <span class="comment" >// world</span>

<span class="keyword" >var</span> Bar = <span class="keyword" >function</span> (){};
Bar.prototype = foo;

<span class="keyword" >var</span> bar = <span class="keyword" >new</span> Bar;
alert(bar.hello()); <span class="comment" >// hello</span>
alert(bar.world()); <span class="comment" >// world</span>
</pre>

<h3>Javascriptのプロトタイプは、インスタンスベースにあらず。</h3>
<p>上のインスタンスメソッドによる動きをサポートしている(?)おかげで、次のような記述が可能となり、プロトタイプ言語ならではな、ダイナミックなプログラミングが可能となります。</p>

<pre class="javascript">
<span class="keyword" >var</span> Echo = {
    hello: <span class="keyword" >function</span> (){
        <span class="keyword" >return</span> <span class="string" >'hello'</span>;
    }
};

<span class="keyword" >var</span> Hoge = <span class="keyword" >function</span> (){};
Hoge.prototype = Echo;

<span class="keyword" >var</span> hoge = <span class="keyword" >new</span> Hoge;
alert(hoge.hello()); <span class="comment" >// hello</span>

Echo.world = <span class="keyword" >function</span> (){
    <span class="keyword" >return</span> <span class="string" >'world'</span>;
};

alert(hoge.world()); <span class="comment" >// world</span>
</pre>

<p>しかしながら、javascriptではクラスベースのインスタンスを生成するため、以下のように書いてしまう事で動作が不明になり、混乱してしまいます。</p>

<pre class="javascript">
<span class="keyword" >var</span> Echo = <span class="keyword" >function</span> (){};
Echo.prototype = {
    hello: <span class="keyword" >function</span> (){
        <span class="keyword" >return</span> <span class="string" >'hello2'</span>;
    }
};

<span class="keyword" >var</span> Hoge = <span class="keyword" >function</span> (){};
Hoge.prototype = <span class="keyword" >new</span> Echo;

<span class="keyword" >var</span> hoge = <span class="keyword" >new</span> Hoge;
alert(hoge.hello()); <span class="comment" >// hello2</span>

Echo.world = <span class="keyword" >function</span> (){
    <span class="keyword" >return</span> <span class="string" >'worl2'</span>;
};
alert(hoge.world); <span class="comment" >// undefined</span>

Echo.prototype.world = <span class="keyword" >function</span> (){
    <span class="keyword" >return</span> <span class="string" >'world3'</span>;
};
alert(hoge.world()); <span class="comment" >// world3</span>
</pre>

<p>上記undefinedになる原因は、<code>Echo</code>クラスに対して<code>world</code>メソッドを追加したものの、既に生成済みインスタンスが<code>Echo</code>のインスタンスと違っているため、このような動きになります</p>

<p>javascriptのプロトタイプはインスタンスについてのプロトタイプではなく、prototype宣言されたものについてプロトタイプされる言語といえそうです。<br />
このあたりの動きが、javascriptのプロトタイプが嫌われる原因なのかもしれません。(慣れない人は本当に慣れないですよね…)</p>

<p>また、prototypeを自由に切り替えれる点においてもなかなか慣れないみたいです(あまり有効な使い方は無いんですけどね)</p>

<pre class="javascript">
<span class="keyword" >var</span> Hoge = <span class="keyword" >function</span> (){};
Hoge.prototype.a = <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'a'</span>};

<span class="keyword" >var</span> Foo = <span class="keyword" >function</span> (){};
Foo.prototype.b = <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'b'</span>};

<span class="keyword" >var</span> Bar = <span class="keyword" >function</span> (){};

Bar.prototype = <span class="keyword" >new</span> Hoge;
<span class="keyword" >var</span> bar = <span class="keyword" >new</span> Bar;
alert(bar.a); <span class="comment" >// a</span>
alert(bar.b); <span class="comment" >// undefined</span>

Bar.prototype = <span class="keyword" >new</span> Foo;
<span class="keyword" >var</span> bar = <span class="keyword" >new</span> Bar;
alert(bar.a); <span class="comment" >// undefined</span>
alert(bar.b); <span class="comment" >// b</span>
</pre>

<h3>同一名プロパティ(slot)はプロトタイプできないのです</h3>

<p>prototype言語のよいところは、未実装のメソッドについては、自動的に親のメソッドを探しに行ってくれる点にあると言えます。</p>

<pre class="javascript">
<span class="keyword" >var</span> Hoge = <span class="keyword" >function</span> (){};
Hoge.prototype.methodA = <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'a'</span>};
<span class="keyword" >var</span> Foo = <span class="keyword" >function</span> (){}; 
Foo.prototype = <span class="keyword" >new</span> Hoge;
Foo.prototype.methodB = <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'b'</span>};
<span class="keyword" >var</span> Bar = <span class="keyword" >function</span> (){};
Bar.prototype = <span class="keyword" >new</span> Foo;
Foo.prototype.methodC = <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'c'</span>};

<span class="keyword" >var</span> bar = <span class="keyword" >new</span> Bar;
alert(bar.methodA()); <span class="comment" >// 'a'</span>
alert(bar.methodB()); <span class="comment" >// 'b'</span>
alert(bar.methodC()); <span class="comment" >// 'c'</span>
</pre>

<p>しかしjavascriptでは、prototypeを上書きしてしまうので、同一プロトタイプでは親の実装を呼び出す事ができません。</p>

<pre class="javascript">
<span class="keyword" >var</span> Hoge = <span class="keyword" >function</span> (){};
Hoge.prototype.methodA = <span class="keyword" >function</span> (){
    <span class="keyword" >return</span> <span class="string" >'this is Hoge#methodA'</span>;
};  

<span class="keyword" >var</span> Foo = <span class="keyword" >function</span> (){
    <span class="keyword" >this</span>.methodA = <span class="keyword" >function</span> (){
        <span class="keyword" >return</span> <span class="string" >'this is Foo#methodA'</span>;
    };
};
Foo.prototype = <span class="keyword" >new</span> Hoge;

<span class="keyword" >var</span> foo = <span class="keyword" >new</span> Foo;
alert(foo.methodA()); <span class="comment" >// this is Foo#methodA</span>
alert(Foo.prototype.methodA()); <span class="comment" >// this is Hoge#methodA</span>
</pre>

<p>ただし、prototypeを巡って呼び出す事もできます。</p>

<h3>jsは3分の1の純情なインスタンスベース、クラスベース、プロトタイプベース。ならcloneがおすすめ。</h3>
<p>ということで、複数のインスタンスを生成できてしまうjavascriptでは混乱を多く招いてしまいます。常に単一方向への継承をするように、<a href="http://blog.xole.net/article.php?id=560">以前書いたプロトタイプ継承のclone</a>を行うとすんなり理解できるのではないでしょうか</p>

<pre class="javascript">Object.prototype.clone = <span class="keyword" >function</span> (){
    <span class="keyword" >var</span> f = <span class="keyword" >function</span> (){};
    f.prototype = <span class="keyword" >this</span>;
    <span class="keyword" >return</span> <span class="keyword" >new</span> f;
};

<span class="keyword" >var</span> hoge = {}.clone();
hoge.a = <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'a'</span>};

<span class="keyword" >var</span> foo = hoge.clone();
foo.b = <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'b'</span>};

<span class="keyword" >var</span> bar = foo.clone();
alert(bar.a()); <span class="comment" >// a</span>
alert(bar.b()); <span class="comment" >// b</span>

hoge.c = <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'c'</span>};
alert(foo.c()); <span class="comment" >// c</span>
alert(bar.c()); <span class="comment" >// c</span>
</pre>

<h3>継承ってなんですか？プロパティのコピーですか？</h3>
<p>プロトタイプ指向言語においては、全てがインスタンスであり、インスタンスを<code>clone</code>することで、親オブジェクトの変更が子オブジェクトに対しても反映されます。</p>

<h4>以下は、ioのコードになります</h4>
<pre class="php">
<span class="func" >Echo</span> := Object clone <span class="keyword" >do</span>(
    hello := method(
        <span class="string" >"hello"</span>
    )
)

hoge := <span class="func" >Echo</span> clone
hoge hello println <span class="comment" >// hello</span>

<span class="func" >Echo</span> world := method(
    <span class="string" >"world"</span>
)

hoge world println <span class="comment" >// world</span>
</pre>

<p>また、ioによく似た(というか、smalltalkによく似ている) <a href="http://slate.tunes.org/">Slate</a> では次のように記述する事ができます(Slateもまたprototype指向の言語の一つです)</p>
<pre class="java">Slate <span class="number" >1</span>&gt; lobby addSlot: #Echo valued: Cloneable clone.
lobby
Slate <span class="number" >2</span>&gt; _<span class="annotation" >@Echo</span> hello [inform: <span class="string" >'hello'</span>].
[hello]
Slate <span class="number" >3</span>&gt; lobby addSlot: #hoge valued: Echo clone.
lobby
Slate <span class="number" >4</span>&gt; hoge hello print.
<span class="comment" >hello</span>
NilNil
Slate <span class="number" >5</span>&gt; _<span class="annotation" >@Echo</span> world [inform: <span class="string" >'world'</span>].
[world]
Slate <span class="number" >6</span>&gt; hoge world print.
The method #world was not found <span class="keyword" >for</span> the following arguments:
{(<span class="string" >"Cloneable"</span> )}
Nil
Slate <span class="number" >7</span>&gt; Echo removeSlot: #world.
(<span class="string" >"Cloneable"</span> )
Slate <span class="number" >8</span>&gt; _@(Echo traits) world [inform: <span class="string" >'world'</span>].
[world]
Slate <span class="number" >9</span>&gt; hoge world print.
<span class="comment" >world</span>
NilNil
Slate <span class="number" >10</span>&gt; quit.</pre>

<p>ioとslateは非常によく似ていますねえ。(slateはioと違いtraitsの概念がある分面倒になりますが)</p>

<h4>で、jsのコード(using: prototype.js)</h4>

<p>既に、記述のとおり、インスタンスに対して<code>prototype</code>するのではなく、<code>Class.create</code>した関数の<code>prototype</code>について拡張を行います</p>

<pre class="javascript">
<span class="keyword" >var</span> Echo = Class.create({
    hello: <span class="keyword" >function</span> (){
        <span class="keyword" >return</span> <span class="string" >'hello'</span>;
    }
});

<span class="keyword" >var</span> hoge = <span class="keyword" >new</span> Echo;
alert(hoge.hello()); <span class="comment" >// hello</span>

Object.extend(Echo.prototype, {
    world: <span class="keyword" >function</span> (){
        <span class="keyword" >return</span> <span class="string" >'world'</span>;
    }
}); 
alert(hoge.world()); <span class="comment" >// world</span>
</pre>

<p>ついでに、継承についてはprototype.jsのおかげで豊富に実装可能ですが、<code>instanceof</code>の結果は正しくない(?)みたいです。</p>

<pre class="javascript">
<span class="keyword" >var</span> Hoge = Class.create({
    a: <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'a'</span>}
});
<span class="keyword" >var</span> Foo = Class.create({
    b: <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'b'</span>}
});
<span class="keyword" >var</span> Bar = Class.create({
    c: <span class="keyword" >function</span> (){<span class="keyword" >return</span> <span class="string" >'c'</span>}
});

<span class="keyword" >var</span> Baz = <span class="keyword" >function</span> (){};
Object.extend(Baz.prototype, <span class="keyword" >new</span> Hoge);
Object.extend(Baz.prototype, <span class="keyword" >new</span> Foo);
Object.extend(Baz.prototype, <span class="keyword" >new</span> Bar);

<span class="keyword" >var</span> baz = <span class="keyword" >new</span> Baz;
alert(baz.a()); <span class="comment" >// a</span>
alert(baz.b()); <span class="comment" >// b</span>
alert(baz.c()); <span class="comment" >// c</span>
alert(baz <span class="keyword" >instanceof</span> Hoge &amp;&amp; baz <span class="keyword" >instanceof</span> Foo &amp;&amp; baz <span class="keyword" >instanceof</span> Bar); <span class="comment" >// false</span>
</pre>

<p>prototype.jsの1.6.0のコードでも<code>Objext.extend</code>は次のようになっているため、確かに、プロパティのコピーが行われているだけみたいです。(これも注意点?)</p>

<pre class="javascript">Object.extend = <span class="keyword" >function</span>(destination, source) {
  <span class="keyword" >for</span> (<span class="keyword" >var</span> property <span class="keyword" >in</span> source)
    destination[property] = source[property];
  <span class="keyword" >return</span> destination;
};
</pre>

<h3>jsが持っているデフォルトオブジェクトにprototypeしよう</h3>
<p>ということで、jsのデフォルトオブジェクトです。これをプロトタイプで拡張することができるので色々と楽しむ事ができます。が、度が過ぎる(Object.prototypeとかに設定すると)とhasOwnpropertyで何回も回ることになるのでほどほどに。</p>

<ul>
<li>Object.prototype</li>
<li>String.prototype</li>
<li>Number.prototype</li>
<li>Boolean.prototype</li>
<li>Date.prototype</li>
<li>RegExp.prototype</li>
<li>Function.prototype</li>
<li>Array.prototype</li>
<li>Error.prototype</li>
</ul>

<p>ref - <a href="
http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects">Core JavaScript 1.5 Reference:Global Objects - MDC</a>
</p>

<h3>おさらい。その２</h3>
<p>ということで、長々と書きましたが、その２でした。<br />
継承パターンとしてのプロトタイプと、その記述(実装)による動きの違いを身につけてもらえれば、結構便利にプロトタイププログラミングできるのではないでしょうか。<br />
どちらかというと、クラス指向(?)の実装と比べ、プロトタイプ指向は馴染めない実装のようですが、仕組みが簡単なので理解してもらうと楽しいです。(学ぶことが多すぎるのが難点ですけどね)</p>

<p>その３へつづく(?)</p>
]]>
</content:encoded>
</item>

</rdf:RDF>