var Hoge = function (){};
Hoge.prototype.methodA = function (){
return"this is methodA";
};
var Foo = function (){};
Foo.prototype = new Hoge;
var foo = new Foo;
alert(foo.methodA()); // this is methodA
alert(foo instanceof Foo && foo instanceof Hoge); // true
また、上の記述は次のように書く事もできます
var Hoge = function (){
this.methodA = function (){
return'this is methodA';
};
this.methodB = function (){
return'this is methodB';
};
};
var Foo = function (){};
Foo.prototype = new Hoge;
var foo = new Foo;
alert(foo.methodA()); // this is methodA
alert(foo.methodB()); // this is methodB
alert(foo instanceof Hoge && foo instanceof Foo); // true
しかし、次のような記述を指定してしまうと、上記の動作とは違ってきます。
var Hoge = function (){
this.methodA = function (){
return'this is methodA';
};
};
Hoge.prototype.methodB = function (){
return'this is methodB';
};
var Foo = function (){
// apply
Hoge.apply(this, arguments);
};
var foo = new Foo;
alert(foo.methodA()); // this is methodA
alert(foo.methodB); // undefined
alert(foo instanceof Hoge && foo instanceof Foo); // false// Fooと同じvar Bar = function (){};
Hoge.apply(Bar.prototype);
var bar = new Bar;
alert(bar.methodA()); // this is methodA
alert(bar.methodB); // undefined
alert(bar instanceof Hoge && bar instanceof Bar); // false
この違いが生まれることについては、後記
インスタンスにメソッドをつけてprototypeすることも可能です。特異メソッドのように
rubyの特異メソッド(以下)では、インスタンスにメソッドを付加することができます。
class Hoge
def hello
'hello'endend
foo = Hoge.new
p foo.hello # hellodef Hoge.world
'world'end
p foo.respond_to?('world') #falsedef foo.world2
'world2'end
p foo.respond_to?('world2') #true
p foo.world2 # world2
var Hoge = function (){};
Hoge.prototype.hello = function (){
return'hello';
};
var foo = new Hoge;
alert(foo.hello()); // hello
foo.world = function (){
return'world';
};
alert(foo.world()); // worldvar Bar = function (){};
Bar.prototype = foo;
var bar = new Bar;
alert(bar.hello()); // hello
alert(bar.world()); // world
var Echo = {
hello: function (){
return'hello';
}
};
var Hoge = function (){};
Hoge.prototype = Echo;
var hoge = new Hoge;
alert(hoge.hello()); // hello
Echo.world = function (){
return'world';
};
alert(hoge.world()); // world
var Echo = function (){};
Echo.prototype = {
hello: function (){
return'hello2';
}
};
var Hoge = function (){};
Hoge.prototype = new Echo;
var hoge = new Hoge;
alert(hoge.hello()); // hello2
Echo.world = function (){
return'worl2';
};
alert(hoge.world); // undefined
Echo.prototype.world = function (){
return'world3';
};
alert(hoge.world()); // world3
var Hoge = function (){};
Hoge.prototype.a = function (){return'a'};
var Foo = function (){};
Foo.prototype.b = function (){return'b'};
var Bar = function (){};
Bar.prototype = new Hoge;
var bar = new Bar;
alert(bar.a); // a
alert(bar.b); // undefined
Bar.prototype = new Foo;
var bar = new Bar;
alert(bar.a); // undefined
alert(bar.b); // b
var Hoge = function (){};
Hoge.prototype.methodA = function (){return'a'};
var Foo = function (){};
Foo.prototype = new Hoge;
Foo.prototype.methodB = function (){return'b'};
var Bar = function (){};
Bar.prototype = new Foo;
Foo.prototype.methodC = function (){return'c'};
var bar = new Bar;
alert(bar.methodA()); // 'a'
alert(bar.methodB()); // 'b'
alert(bar.methodC()); // 'c'
var Hoge = function (){};
Hoge.prototype.methodA = function (){
return'this is Hoge#methodA';
};
var Foo = function (){
this.methodA = function (){
return'this is Foo#methodA';
};
};
Foo.prototype = new Hoge;
var foo = new Foo;
alert(foo.methodA()); // this is Foo#methodA
alert(Foo.prototype.methodA()); // this is Hoge#methodA
Object.prototype.clone = function (){
var f = function (){};
f.prototype = this;
returnnew f;
};
var hoge = {}.clone();
hoge.a = function (){return'a'};
var foo = hoge.clone();
foo.b = function (){return'b'};
var bar = foo.clone();
alert(bar.a()); // a
alert(bar.b()); // b
hoge.c = function (){return'c'};
alert(foo.c()); // c
alert(bar.c()); // c
var Echo = Class.create({
hello: function (){
return'hello';
}
});
var hoge = new Echo;
alert(hoge.hello()); // hello
Object.extend(Echo.prototype, {
world: function (){
return'world';
}
});
alert(hoge.world()); // world
var Hoge = Class.create({
a: function (){return'a'}
});
var Foo = Class.create({
b: function (){return'b'}
});
var Bar = Class.create({
c: function (){return'c'}
});
var Baz = function (){};
Object.extend(Baz.prototype, new Hoge);
Object.extend(Baz.prototype, new Foo);
Object.extend(Baz.prototype, new Bar);
var baz = new Baz;
alert(baz.a()); // a
alert(baz.b()); // b
alert(baz.c()); // c
alert(baz instanceof Hoge && baz instanceof Foo && baz instanceof Bar); // false
前回のエントリが700users突入しました。ありがとうございます。参考になれば幸いです。
ということで、その2になります。
前回書いた通り、C/Javaについてはある程度の知識がある人なので、クラスなどのオブジェクト指向はちゃんと理解されているようですが、プロトタイプ指向は初めて学ぶようです。
javascript(ECMAScript)のプロトタイプは他のプロトタイプ指向言語とはひと味違う動作をするので、その点も含めておさらい
プロトタイプとはなんですか?プロトタイプとは継承パターンの一つでしかないです
プロトタイプは単なる継承パターンであり、単一の方向への継承しか行わない点についてはクラスベースと同じです。
また、上の記述は次のように書く事もできます
しかし、次のような記述を指定してしまうと、上記の動作とは違ってきます。
この違いが生まれることについては、後記
インスタンスにメソッドをつけてprototypeすることも可能です。特異メソッドのように
rubyの特異メソッド(以下)では、インスタンスにメソッドを付加することができます。
特異メソッドについては、javascriptでも実装可能です。また、特異メソッドをつけたインスタンスをprototypeすることが可能となっています。
Javascriptのプロトタイプは、インスタンスベースにあらず。
上のインスタンスメソッドによる動きをサポートしている(?)おかげで、次のような記述が可能となり、プロトタイプ言語ならではな、ダイナミックなプログラミングが可能となります。
しかしながら、javascriptではクラスベースのインスタンスを生成するため、以下のように書いてしまう事で動作が不明になり、混乱してしまいます。
上記undefinedになる原因は、
Echoクラスに対してworldメソッドを追加したものの、既に生成済みインスタンスがEchoのインスタンスと違っているため、このような動きになりますjavascriptのプロトタイプはインスタンスについてのプロトタイプではなく、prototype宣言されたものについてプロトタイプされる言語といえそうです。
このあたりの動きが、javascriptのプロトタイプが嫌われる原因なのかもしれません。(慣れない人は本当に慣れないですよね…)
また、prototypeを自由に切り替えれる点においてもなかなか慣れないみたいです(あまり有効な使い方は無いんですけどね)
同一名プロパティ(slot)はプロトタイプできないのです
prototype言語のよいところは、未実装のメソッドについては、自動的に親のメソッドを探しに行ってくれる点にあると言えます。
しかしjavascriptでは、prototypeを上書きしてしまうので、同一プロトタイプでは親の実装を呼び出す事ができません。
ただし、prototypeを巡って呼び出す事もできます。
jsは3分の1の純情なインスタンスベース、クラスベース、プロトタイプベース。ならcloneがおすすめ。
ということで、複数のインスタンスを生成できてしまうjavascriptでは混乱を多く招いてしまいます。常に単一方向への継承をするように、以前書いたプロトタイプ継承のcloneを行うとすんなり理解できるのではないでしょうか
継承ってなんですか?プロパティのコピーですか?
プロトタイプ指向言語においては、全てがインスタンスであり、インスタンスを
cloneすることで、親オブジェクトの変更が子オブジェクトに対しても反映されます。以下は、ioのコードになります
また、ioによく似た(というか、smalltalkによく似ている) Slate では次のように記述する事ができます(Slateもまたprototype指向の言語の一つです)
ioとslateは非常によく似ていますねえ。(slateはioと違いtraitsの概念がある分面倒になりますが)
で、jsのコード(using: prototype.js)
既に、記述のとおり、インスタンスに対して
prototypeするのではなく、Class.createした関数のprototypeについて拡張を行いますついでに、継承についてはprototype.jsのおかげで豊富に実装可能ですが、
instanceofの結果は正しくない(?)みたいです。prototype.jsの1.6.0のコードでも
Objext.extendは次のようになっているため、確かに、プロパティのコピーが行われているだけみたいです。(これも注意点?)jsが持っているデフォルトオブジェクトにprototypeしよう
ということで、jsのデフォルトオブジェクトです。これをプロトタイプで拡張することができるので色々と楽しむ事ができます。が、度が過ぎる(Object.prototypeとかに設定すると)とhasOwnpropertyで何回も回ることになるのでほどほどに。
ref - Core JavaScript 1.5 Reference:Global Objects - MDC
おさらい。その2
ということで、長々と書きましたが、その2でした。
継承パターンとしてのプロトタイプと、その記述(実装)による動きの違いを身につけてもらえれば、結構便利にプロトタイププログラミングできるのではないでしょうか。
どちらかというと、クラス指向(?)の実装と比べ、プロトタイプ指向は馴染めない実装のようですが、仕組みが簡単なので理解してもらうと楽しいです。(学ぶことが多すぎるのが難点ですけどね)
その3へつづく(?)