コンストラクタすると同時にメソッドをオーバーロードするには?

このQ&Aのポイント
  • コンストラクタすると同時にメソッドをオーバーロードする方法には、2つのアプローチがあります。
  • 方法(1)では、コンストラクタ後にメソッドを上書きすることで目的を達成します。
  • 方法(2)では、クロージャを使用してメソッドを生成することで目的を達成します。
回答を見る
  • ベストアンサー

コンストラクタすると同時にメソッドをオーバーロードするには?

コンストラクタすると同時にメソッドをオーバーロードするには? リスナー関数でthisの参照先が変わってしまう問題に悩んでいます。 <!-- 方法(1) コンストラクタした後にメソッドを上書きする --> <p id="Test">test</p> <script type="text/javascript"><!-- function Hello (value){ this.value = value ? value : 'Hello, World!'; } Hello.prototype.start = function (event) { alert(this.value); } var world = new Hello(); var start = function (event) { world.start.call(world, event); }; // thisがworldを参照するようにstartメソッドを作り直す document.getElementById('Test').addEventListener('click', start, false); //--></script> 上記コードは期待通りの動作ですが、せっかくコンストラクタしたにも関わらず、メソッドを上書きしなくてはなりません。 コンストラクタした時点でstartメソッドが完成しているのが理想です。 (以下、全角空白は半角空白に置換してください) <!-- 方法(2) クロージャでメソッドを生成する --> <p id="Test">test</p> <script type="text/javascript"><!-- function Hello (value) {  function start (event) {   alert(this.value);  }  this.value = value ? value : 'Hello, World!';  this.start = (function(that){   return function (event) {    start.call(that, event);    that = null;   };  })(this); } var world = new Hello('Hello, World2!'); document.getElementById('Test').addEventListener('click', world.start, false); //--></script> こちらは完全に期待通りの動作を示しますが、prototype を使用していないのが少し気になります。 <!-- 方法(3) クロージャで prototype.start を上書きする --> <p id="Test">test</p> <script type="text/javascript"><!-- function Hello (value) {  this.value = value ? value : 'Hello, World!';  this.start = (function(that, start){   return function (event) {    start.call(that, event);    that = start = null;   };  })(this, this.start); } Hello.prototype.start = function start (event) {  alert(this.value); }; var world = new Hello('Hello, World2!'); document.getElementById('Test').addEventListener('click', world.start, false); //--></script> 結果としては期待通りのコードになったのですが、以下の点が気になっています。 ・方法(2)、方法(3) を比較すると、それぞれどのようなメリット、デメリットがあるのか。(prototypeが高速と聞いてからは (3) に傾いているのですが、よくわかっていません) ・コンストラクタ時に発動する特別なメソッド、プロパティが用意されているのか。 ・クロージャを使わずに解決できるのか。 ・全く別の方法(もっと良い実装)があるのか。 コードの書き方は千差万別だとは思いますが、主観で結構ですので、アドバイスいただければ幸いです。 # start() はサンプル故に短いコードですが、実際には比較的長いコードがある状態を想定しています。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.9

> No.7お礼 >> 質問文中の方法(3)ですが(^^; >な…! >記憶にありません(汗) >多分、いろいろ書いているときにコピペでミスしてしまったんですね。 >この記述が誤解を与えたようで、すみませんでした。 Hello.prototype.start=function start(){} このfunction startという書き方について、「コピペでミスしてしまったんですね。」 と書かれていますよね。 ですが、No.8お礼ではあいかわらずfunction startと書かれていますが、function startなんでしょうか? > それでも、私が prototype を使用しているのは、start() の所在を明確にするため。 ふむ。。。 "start"がいっぱいあってstartが明確でない(どのstartがどのstartなのか)ので、変数名を変えてみました。 function Hello (value) { this.value = value ? value : 'Hello, World!'; this.start = (function(that, func){ return function (event) { func.call(that, event); }; })(this, this.bbb); } Hello.prototype.bbb = function aaa(event) { alert(this.value); }; var world1 = new Hello('Hello, World! (1)'); var world2 = new Hello('Hello, World! (2)'); document.getElementById('Test1').addEventListener('click', world1.start, false); document.getElementById('Test2').addEventListener('click', world2.start, false); これでalert(this.value)が期待通りになりそうですね。 なるほど、think49さんが書かれたthis.startの定義に渡される第二引数はHelloコンストラクタ内部で定義されるthis.startのことだと思っていましたが、どうやら違っていたようで。 start.call()で実行される関数もfunction startだと思っていたのですが、これも違ったようで、ちゃんと引数のstartになっていたようですね。 prototypeベースの作り方はよくわからないため、この方法が最も高速だとか読みやすいかどうかについては私からは言及できません。 再質問などでprototypeを熟知された方の回答を待たれる方が良いと思います。 最初にオーバーロードではなくオーバーライドの間違いではないか、と指摘しましたが、 オーバーロードでもオーバーライドでもないですね。 申し訳ありません。 改めて説明することではないと思いますが。 オーバーライド:同じ関数を書き直すこと var A=function(value){alert(value)} A=function (value, b){status=value;} A('hello')としてもA('hello', true)としても、どちらもalert(value)ではなくstats=valueが実行される。 オーバーライドすると、最初に定義した関数は消滅する。 オーバーロード:多重定義(JavaScriptにない概念、C++で記述(いろいろ省略、都合上C++の文法と異なった使い方あり)) void A(char* str){ cout << str << endl; // strを表示 } void A(char* str, bool f){   std::ofstream ofs( "file.txt" );   ofs << str << endl; // strをファイルに保存 } A('hello'); とすると画面にhelloが表示され、 A('hello', true); とすると、ファイルに保存されます。 これをJavaScriptで実現するには var A=function(value, b){ if(b) status=value; else alert(value); } A('hello'); // alert A('hello', true); // status という書き方をして、関数内で条件分岐させなければなりません。

think49
質問者

お礼

> ですが、No.8お礼ではあいかわらずfunction startと書かれていますが、function startなんでしょうか? う…、何度も申し訳ありません。 下記コードを書くつもりでまたコピペを…。(汗) Hello.prototype.start = function () { }; ---------------------------- > オーバーロードでもオーバーライドでもないですね。 質問当初は私も勘違いしていたのですが、私が想定していたのは「オーバーライド」でしたね。 その後、「prototype の原理からして、どちらでもない」ということに #6 の [お礼] を書いているときに気が付きました。 (ですので、いろいろと指摘いただいて助かりました。) 「オーバーロード」に関しては、用語は知りませんでしたが、 #2 で指摘いただいた arguments のキーワードと Wikipediaの「多重定義」から、実装方法の見当はつきました。 # 実は、以前、私はオーバーロードの実装法に関する質問をしており、「arguments.length を使う方法」「仮引数の値をチェックする方法」の2つを教わっていました。 # ただ…、方法は知っていても用語を知らなかったので、「オーバーロード」という曖昧に覚えていた言葉をそのまま使ってしまい…。(苦笑) 多重定義 http://ja.wikipedia.org/wiki/%E5%A4%9A%E9%87%8D%E5%AE%9A%E7%BE%A9 引数の数によって関数の動作を変えるには? | OKWave http://okwave.jp/qa/q5841966.html talooさんが例示されたコードは「仮引数が実引数より少ない時に、未指定の仮引数が undefined で初期化されるECMAScript仕様」を利用したものですね。 11.2.3 関数呼出し (Function Calls) - 11 式 (Expressions) http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/11_Expressions.html#section-11.2.3 13.2.1 [[Call]] - 13 関数定義 (Function Definition) http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/13_Function_Definition.html#section-13.2.1 10.2.3 関数コード (Function Code) - 10 実行コンテキスト (Execution Contexts) http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/10_Execution_Contexts.html#section-10.2.3 10.1.3 変数の実体化 (Variable Instantiation) - 10 実行コンテキスト (Execution Contexts) http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/10_Execution_Contexts.html#section-10.1.3 ……読むのに1時間ぐらいかかってしまいました。まだスラスラと読むには、ほど遠いです。 ---------------------------- 丁寧な回答をありがとうございました。 もう少し、時間をおいて、他に指摘or回答がつかなければ、質問を締め切りたいと思います。

その他の回答 (8)

回答No.8

> 多分、いろいろ書いているときにコピペでミスしてしまったんですね。 > この記述が誤解を与えたようで、すみませんでした。 不自然なところが何カ所か有るので、あまり気にしてなかったのですが、 そうなると、方法(3)はどういうのを作ろうとされてたんでしょうか? >> Hello.prototype.startをクロージャにするための無名関数(エンクロージャ)に渡すthis(の代わり)が何になるのかわかりません。m(_ _)m >方法(3) の解説ということでいいでしょうか。 そうです。 > 質問文 > ・方法(2)、方法(3) を比較すると、それぞれどのようなメリット、デメリットがあるのか。 > (prototypeが高速と聞いてからは (3) に傾いているのですが、よくわかっていません) prototypeを使用した作り方を考えられているそうなので、 function Hello(value){ this.value = value ? value : 'Hello, World!'; this.start=function(){} } ではなく function Hello(value){ this.value = value ? value : 'Hello, World!'; } Hello.prototype.start=function(){} で作り、かつworld.start()で実行する方法です。 まあ、インスタンスの拡張のタイミングをずらせば、worldを渡すことができますけどね。 function Hello (value) {  this.value = value ? value : 'Hello, World!'; } var world = new Hello('Hello, World2!'); // worldを生成してからHelloインスタンスを拡張する。 Hello.prototype.start = (function(that, start){   return function (event) {    start.call(that, event); //   that = start = null;   }; })( world, function(event){alert(this.value);} ); //生成済みのworldを渡す document.getElementById('Test').addEventListener('click', world.start, false); この方法では複数のオブジェクトを作ると動作がややこしくなります。 var world = new Hello('Hello, World2!'); var msg = new Hello('My Message!'); //Hello.prototype.start()の定義にworldを渡しているため、start()の内部のthisがmsgではなくworldを指すことになる。 document.getElementById('Test').addEventListener('click', world.start, false); document.getElementById('Test2').addEventListener('click', msg.start, false); Helloオブジェクトを1つしか作らないのであれば、Helloを拡張(Hello.prototypeを使用)し、worldを渡すようにしても良いかもしれません。 そうでなければHelloを拡張するのではなく、生成したオブジェクトを個々に拡張しないと行けないと思います。 function start(event){alert(this.value);} function warpper(that, func){   return function (event) {     func.call(that, event); //    that = func = null;   }; } function Hello (value) {   this.value = value ? value : 'Hello, World!'; } var world = new Hello('Hello, World2!'); world.start = warpper(world, start); var msg = new Hello('My Message!'); msg.start = warpper(msg, start);

think49
質問者

お礼

> そうなると、方法(3)はどういうのを作ろうとされてたんでしょうか? > ... > そうでなければHelloを拡張するのではなく、生成したオブジェクトを個々に拡張しないと行けないと思います。 うーん…。 方法(3) は生成したオブジェクトを個々に拡張していると思うのですが、私が何か勘違いしているのでしょうか…。 ----------- <p id="Test1">test1</p> <p id="Test2">test2</p> <script type="text/javascript"><!-- function Hello (value) {  this.value = value ? value : 'Hello, World!';  this.start = (function(that, start){   return function (event) {    start.call(that, event);   };  })(this, this.start); } Hello.prototype.start = function start (event) {  alert(this.value); }; var world1 = new Hello('Hello, World! (1)'); var world2 = new Hello('Hello, World! (2)'); document.getElementById('Test1').addEventListener('click', world1.start, false); document.getElementById('Test2').addEventListener('click', world2.start, false); //--></script> ----------- 私が 方法(3) でクロージャを使ってまで複雑な処理をしているのは、「仮引数の that が生成されたオブジェクトを参照し、クロージャによってその参照を維持すること」を期待するためです。 #7 でtalooさんは > ということは、方法(3)はこれでいいのでは?と思いますが。 と指摘されましたが、その方法も有りだと思います。 それでも、私が prototype を使用しているのは、start() の所在を明確にするため。 function Hello () {} Hello.start = function () {} function Hoge () {} Hoge.start = function () {} この状況で prototype を使わずに start() を定義する場合は HelloStart, HogeStart というように名前の衝突を回避しなければなりませんが、prototype を使えばメソッドの名前は短くてすみます。 方法(2) ではプライベート関数という形で start() の所在を明らかにしていますが、所在が異なるだけで、方法(2) と 方法(3) は全く同じ原理を使っています。 質問の意図を勘違いしていたら、すみません。 指摘は有り難いと思っていますので、遠慮なくご指摘下さい。

think49
質問者

補足

> var msg = new Hello('My Message!'); //Hello.prototype.start()の定義にworldを渡しているため、start()の内部のthisがmsgではなくworldを指すことになる。 多分ですが…、この部分で勘違いをされていると思います。 「this が参照するのは生成されたオブジェクトの方」で Hello 及び、Hello.prototype.start はずっと変化していません。 #6 の [お礼] でも少し触れましたが、これは上書きではなく、生成されたオブジェクトのプロパティを定義することで prototype の値より優先される事が狙いです。 prototypeを使う場合、String, Array などの標準オブジェクトを拡張する例がよく見られますが、 ------ String.prototype.trim = function () { return this.replace(/^\s|\s$/g, ''); }; var str = new String (' Hello '); str = str.trim(); alert('"' + str + '"'); ------ この場合の this も Stringオブジェクトを参照するのではなく、「new演算子で生成されたStringオブジェクト」(例示では str ) を参照しています。 prototype - MDC https://developer.mozilla.org/ja/Core_JavaScript_1.5_Reference/Global_Objects/String/prototype

回答No.7

> No.6お礼 > function start (event) { }; を代入する書き方は始めて見たのですが、よろしければ参考URLなど教えていただけないでしょうか? 質問文中の方法(3)ですが(^^; prototypeなしで検証コード。 var a=function b(arg){ alert(arg); }; この場合、以下と同じになるようです。 function b(arg){ alert(arg); }; var a=b; ---------- > No.6お礼 > 正確には、これは上書きではなく、obj.prop でプロパティ参照する際に「obj直属のプロパティから参照しにいくECMAScript仕様」を利用しています。 > (上書きではないので、Obj.prototype.prop も残っています) なるほど、下のような書き方をした場合、おおざっぱに言えば、こんな感じと理解させて頂きます。 function Hello(){ this.a=function(){}; // world.a()で実行できる } Hello.prototype.a=function start(){} // world.a()としては実行できないが、world.prototype.a()とするか、start()で実行できる world=new Hello(); ということは、方法(3)はこれでいいのでは?と思いますが。 function Hello (value) {  this.value = value ? value : 'Hello, World!';  this.start = (function(that, start){   return function (event) {    start.call(that, event);    that = start = null;   };  })(this, this.start); } function start (event) { // Hello.prototype.startへの代入をやめる  alert(this.value); }; var world = new Hello('Hello, World2!'); またはprototypeを使用して。。。と思ったのですが、 Hello.prototype.startをクロージャにするための無名関数(エンクロージャ)に渡すthis(の代わり)が何になるのかわかりません。m(_ _)m OOPはJavaから入っているため、prototypeベースの作り方はどちらかといえば苦手なのです。。。

think49
質問者

お礼

> 質問文中の方法(3)ですが(^^; な…! 記憶にありません(汗) 多分、いろいろ書いているときにコピペでミスしてしまったんですね。 この記述が誤解を与えたようで、すみませんでした。 > この場合、以下と同じになるようです。 早速、検証してみました。 -------------- (function () { // ケース(1) var a = function b (arg) { alert(arg); }; a ('Hello1'); // Hello1 b ('World1'); // (IE8 = "World1" / Firefox3.6, GoogleChrome5, Opera10.54 = "Uncaught ReferenceError: b is not defined") })(); (function () { // ケース(2) var a = function (arg) { alert(arg); }; a ('Hello2'); // Hello2 b ('World2'); // (IE8 = "オブジェクトを指定してください。" / Firefox3.6, GoogleChrome5, Opera10.54 = "Uncaught ReferenceError: b is not defined") })(); (function () { // ケース(3) var b = function (arg) { alert(arg); }; var a = b; a ('Hello3'); // Hello3 b ('World3'); // World3 })(); -------------- function b (arg) { alert(arg); }; で「オブジェクトb」としてが初期化されるのはIE8だけのようです。 クロスブラウザを考えると、この方法は使えないみたいですね…。 > Hello.prototype.startをクロージャにするための無名関数(エンクロージャ)に渡すthis(の代わり)が何になるのかわかりません。m(_ _)m 方法(3) の解説ということでいいでしょうか。 例えば、以下のようなコードを考えてみてください。 (function (str) { // 仮引数 alert (str); })('foo'); // 実引数 無名関数の仮引数は str であり、実引数は 'foo' です。 つまり、str に 'foo' が代入され、alert() に渡すことになります。 (function(that, start){ // 仮引数 (that === this, start === this.start)  return ... })(this, this.start); // 実引数 (this === world, this.start === Hello.prototype.start) 更に、functionオブジェクトを return する処理が入ります。 例えば、下記2つは同じ処理です。 ------ (function () { // クロージャの使用例(1) var a = function () { return function (str) { alert (str); }; // 関数オブジェクトを return する } var foo = a (); foo ('return1'); })(); (function () { // クロージャの使用例(2) var b = function (str) { alert (str); }; var a = function () { return b; // 関数オブジェクトを return する }; var foo = a (); foo ('return2'); })(); ------ 「関数a」は呼び出されることで関数オブジェクトを返し、その関数オブジェクトを実行することで alert(str); を内部コードに持つ関数が実行されます。 方法(3) では無名関数として既に実行されているので、return function () {}; がそのまま入るわけです。

回答No.6

字数制限により分割。m(_ _)m > NO.2 お礼 > この場合、this の参照先が p#Test に変化していることが分かります。 すみません。 No.3参照でお願いします。 > No.4 イベントリスナーを使うときに考慮すべきは、「thisは何を指すのか」だと思います。 私のselfを使う方法ではthis==HTML-Elementですが、 NO.2、No.3のcallを使った方法ではaddEventListenerに渡す関数でthis==worldになるように調整しています。 質問中のクロージャを使う方法ではthis.startまではthis==HTML-Elementですが、start.call()のところでthis==worldになるように調整していますから、 なるほど、それなら方法(2)もありかなあと思います。 > 方法(3) > Hello.prototype.start = function start (event) { >  alert(this.value); > }; じっくり読み直すと、 なるほど、クロージャのstart.call()はこの(右辺の)startのことか。。。 > this.start = (function(that, start){ > (略) >  })(this, this.start); これがprototype.startで上書きされないのは、 > Hello.prototype.start = function start (event) { > }; //上書きされない > Hello.prototype.start = function (event) { > }; //上書きされる ということなんですかねえ。。。 (prototypeにこういう仕様・違いがあることは聞いたことがないので、、、) > tagindex No.13 もう一度読み直すと、 > (function(){ // エンクロージャ > (略、function addEvent(){}等定義) > })(); という部分が書かれてましたね。 無名関数が終了するとaddEvent関数は消滅しますから、 その引数であるelement、listener変数(クロージャ=リークの原因として機能する)の保持が消える、 ということだと思います。 悪くはないと思うんですが、Ajaxなどで繰り返しaddEvent関数を使用したい場合に、 エンクロージャを動的に作る、というのは無理があるのではないか、、、と思います。 関数を消せば良いなら、、、 function AddEvent(){ this.addEvent=function(e,t, f,c){ if(e.addEvent) e.addEventListener(t, func, useCapture); else if(e.attachEvent) e.attachEvent('on'+t, func); }; } A=new AddEvent(); A.addEvent(element, 'click', func, false); delete A; // オブジェクトごとaddEvent関数を消去 というのをaddEvent関数を使いたい時に毎回やれば、addEventへの引数も消えるかもしれません。。。 一番簡単なのは、addEvent関数などのラッパーを作らずに毎回4行記述すればいいんですけどね。 if(element.addEvent) element.addEventListener(ev, func, useCapture); else if(element.attachEvent) element.attachEvent('on'+ev, func); > > 他の多くのサイトでは「メモリリークするパターン」として書かれています。 > 差し支えなければ、「他の多くのサイト」のURLを教えていただけないでしょうか? すみませんが、ブックマークなども削除しましたし、、、 http://p2b.jp/index.php?UID=1131336575 考え方はこのサイトの物と同じですが、無名関数(匿名関数)を経由させていたと思います。 どちらにしても結構複雑になると思います。

think49
質問者

お礼

ありがとうございます。 メモリリークの件、prototypeの件、いろいろ考えましたが、文字数制限の事もあるので、今回は質問内容に即した prototype に絞って書きたいと思います。 > > Hello.prototype.start = function start (event) { > > }; > //上書きされない function start (event) { }; を代入する書き方は始めて見たのですが、よろしければ参考URLなど教えていただけないでしょうか? 匿名関数を代入するのはわかりますが、名前付き関数を匿名関数っぽく代入できる、というところが今ひとつわかりません。 何度もすみません。お手数おかけしてます…。m(_ _)m --------------------- 私は「関数Helloは呼び出されるまで内部コードが実行されない」という認識でいます。 ですので、 --------- function Hello () {  // この中のコードは関数が呼び出されるまで実行されない  this.a = 'after';  alert (this.a); } Hello.prototype.a = 'before'; alert (Hello.a); // undefined (Hello が呼び出されていないため、this.a は未定義) alert (Hello.prototype.a); // before --------- このように、Hello.prototype.a が先に初期化されると思うのですが、いかがでしょうか? 上記コードに少し変更を加えれば、私が 方法(3) で world.start の挙動を変更したコードに近くなります。 --------- function Hello () {  alert (this.a); // before (this.a は存在しないので、this.prototype.a を参照する)  this.a = 'after'; } Hello.prototype.a = 'before'; var h = new Hello (); alert (h.a); // after --------- 正確には、これは上書きではなく、obj.prop でプロパティ参照する際に「obj直属のプロパティから参照しにいくECMAScript仕様」を利用しています。 (上書きではないので、Obj.prototype.prop も残っています) --------- function Obj () { } Obj.prototype.prop = 0; var obj = new Obj (); alert (obj.prop); // 0 (obj.prop への参照を試みるが、存在しないので Obj.prototype.prop を参照する) obj.prop = 1; // obj直属のプロパティとして初期化 alert (obj.prop); // 1 (obj.prop への参照を試み、存在するので obj.prop を参照する) alert (Obj.prototype.prop); // 0 --------- 11.2.1 プロパティアクセス演算子 - 11 式 (Expressions) http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/11_Expressions.html#section-11.2.1 > なるほど、クロージャのstart.call()はこの(右辺の)startのことか。。。 この際、引数として渡される this.start は Hello.prototype.start を参照しています。 this.start = function () {}; とすれば、world直属のプロパティとして this.start が初期化されるので、prototype.start よりも優先される、という仕組みです。 > すみませんが、ブックマークなども削除しましたし、、、 わかりました。 メモリリークに関しては、この先、そのURLを見る機会が訪れたときにまた一考したいと思います。

think49
質問者

補足

またまた、すみません。 [お礼] に書いたコードの説明が一点間違っていたので訂正します。m(_ _)m × alert (this.a); // before (this.a は存在しないので、this.prototype.a を参照する) ○ alert (this.a); // before (this.a は存在しないので、Hello.prototype.a を参照する)

回答No.5

OKWaveの場合は他の回答者からのツッコミがないというのが最大の欠点だと思いますけどね。 回答のミスに気づかないとそのままになりますから。 余談になるのかな、、、OOPとプライベート変数/関数の話です。 すみません、グローバル関数ではなくパブリック関数でした。m(_ _)m 言語によってはグローバルと呼ぶ物もあるかもしれませんが、、、 JAVAやC++などではプライベート/パブリックの概念を解説されているところが多いのですが、 JavaScriptのようなprototypeベースのOOP(後述のTest3)では、プライベート変数は使わないのが一般的だと思います。 function Test(){ var a=0; this.get=function(){ return a; }; this.set=function(val){ a=val; }; } var t=new Test(); t.set(1); alert(t.get()); // 1 alert(t.a); // undefined。this.aは未定義。var a(プライベート変数)にはアクセスできない t.a++; // error、未定義変数のインクリメントは出来ない function Test2(){ this.a=0; this.get=function(){ return this.a; }; this.set=function(val){ this.a=val; }; } var t2=new Test2(); t2.set(1); alert(t2.get()); // 1 alert(t2.a); // 1、this.aで定義してあるためアクセスできる(パブリック) t2.a++; // OK // prototypeベースで作る場合は、プロパティは内部に定義し、メソッドは後から追加することが多いと思います。 function Test3(){ this.a=0; } test3.prototype.get=function(){ this.a; }; test3.prototype.set=function(val){ this.a=val; }; var t3=new Test3(); t3.set(1); alert(t3.get()); // 1 alert(t3.a); // 1 Test2とTest3は、最終的には同じ構造になります。 メソッドやプロパティを一気に書き換える作り方もあります。(prototype.jsやYahooUI参照) (定義していない既存プロパティがあれば削除されます) Test.prototype={ a: 0, b: function(){}, c: function(){} };

think49
質問者

お礼

ありがとうございます。 OOPの考え方が非常に参考になります。 > this.get=function(){ return a; }; こちらを読んで、PHPにゲッターメソッド、セッターメソッドという概念があることを思い出しました。 手元のPHP独習を読むと、これらは総称してアクセサメソッドと呼び、以下のメリットが得られる、とあります。 -------- PHP独習(P106)より ・読み書きの制御が可能になる ・プロパティ値を設定する際に値の検証を行える ・プロパティ値を参照する際に値の加工を行える -------- 例えば、こんな事が可能だと思います。 -------- (function () {  function TestNode () {   var doc, node;   this.get = function () { return [doc, node]; }; // プロパティ値を参照する際に値を配列に加工する   this.set = function (doc1, node1) {    if (doc1.nodeType !== 9) { throw TypeError (doc1 + ' is not a DOCUMENT_NODE'); return false; } // プロパティ値を設定する際に nodeType の検証を行う    if (!node1.nodeType) { throw TypeError (node1 + ' is not a Node'); return false; } // 同上    doc = doc1;    node = node1;   };  }  var testNode = new TestNode ();  testNode.set (document, document.getElementsByTagName('p').item(0));  var nodes = testNode.get ();  alert (nodes[0]); // [object HTMLDocument]  alert (nodes[1]); // [object HTMLHtmlElement] })(); -------- これは値の検証、加工を行えることも一つのメリットですが、メモリリークを回避できるケースもあるように思いました。 -------- // ここで function TestNode () { } が宣言されたものとする var testNode = new TestNode (); testNode.set (document, document.getElementsByTagName('p').item(0)); (testNode.get ())[0].onclick = function () {  // このクロージャは testNode への参照を保持するが、testNode.get() を実行しなければDOMノードを参照しないので、循環参照とはならない }; -------- > すみません、グローバル関数ではなくパブリック関数でした。m(_ _)m 完全ではありませんが、理解しました。 どうやらクラスの概念から来ているようですが、JavaScript言語における正式な用語ではないようですね。 考え方は役立つので、概念は理解しておこうと思います。 public … クラスにおいて、外部に公開されている事を意味する (public関数, public変数) private … クラスにおいて、外部に公開されていない事を意味する (private関数, private変数) > メソッドやプロパティを一気に書き換える作り方もあります。 最近、私も同じような書き方を使うようにしています。 var _a = function () {}, _b = function () {}; Test.prototype = { a: _a, b: _b }; ・prototypeで拡張したプロパティが一目で分かる ・コンストラクタ関数の名前変更時に書き換える部分が2箇所で済む という点で気に入っています。

回答No.4

私自身、クロージャの使用でなぜメモリリークが発生し、なぜメモリリークが発生しないのかが未だにわかっていません。 また、ブラウザのバージョン違いで全く違う現象が起きますし、 No.1お礼のtagindexのサイト、No.13のAddEventはtagindexの投稿ではメモリリークしないと書かれていますが、 他の多くのサイトでは「メモリリークするパターン」として書かれています。 (おそらくHTML-Elementを引数として渡すことがメモリリークの原因だと思います) tagindexの回答者の言い方が好きではないので行かないのですが、、、 > オブジェクトの管理者が同じなら、循環参照しても問題ありません。 htmlElement.parentNode.firstChild.parentNodeの循環がなぜメモリリークしないのかという謎がありましたが、 それが本当ならまあ、本当なんでしょう。 そうであれば、NO.3のselfを削除する必要はないはずです。 No.2を書いていたときは、方法(2)や方法(3)でなぜクロージャなんだろうという疑問がありましたが、 No.3に書いたようなthisの参照をworldに向けるためということですよね。 このクロージャも同様に、メモリリークしないパターンに該当するのではないでしょうか。

think49
質問者

お礼

何度も回答ありがとうございます。 メモリリークの件も別の視点から考えると、また参考になります。 > 他の多くのサイトでは「メモリリークするパターン」として書かれています。 差し支えなければ、「他の多くのサイト」のURLを教えていただけないでしょうか? 例えば、私が探したところでは、以下が見つかりました。 IEのメモリリーク問題 http://p2b.jp/index.php?UID=1131336575 ここでは「addEvent() の中で匿名関数を使っていることが問題」という趣旨で指摘されており、2つ目の addEvent() では匿名関数を外に追い出す事でメモリリークを解消。 とりあえず、私の認識とは一致しています。 指摘のあった No.13 ですが、こちらも匿名関数を addEvent() 内で定義しないことでメモリリークを回避しています。 http://www.tagindex.com/cgi-lib/q4bbs/patio.cgi?mode=view2&f=2586&no=13 しかし、他に多くのサイトがあるのなら、私が誤解しているのかもしれませんので、教えていただけると嬉しいです。(複雑な問題ですので…。) > tagindexの回答者の言い方が好きではないので行かないのですが、、、 手厳しいですね。(汗) 私も気をつけます。 > そうであれば、NO.3のselfを削除する必要はないはずです。 > > No.2を書いていたときは、方法(2)や方法(3)でなぜクロージャなんだろうという疑問がありましたが、 > No.3に書いたようなthisの参照をworldに向けるためということですよね。 > このクロージャも同様に、メモリリークしないパターンに該当するのではないでしょうか。 #3 は window.onunload 時にクロージャが参照するローカル変数を全て削除しているので、いずれにせよメモリリークしないパターンになると思います。 私が恐れるのは、#1 の [お礼] に書いたような、「this でDOMノードを参照できて、onunload時の処理がない状況」です。 その場合、this を self や that などのローカル変数にしてしまうと、クロージャが参照を持ち続ける条件が揃ったときに循環参照してしまいます。 「onunload時に参照を切れば済む」というのは確かにそうなんですが、手間と効率の兼ね合いで省略したい、と思うときがありまして…。(汗) #3 のお礼でも触れましたが、Hello() 側で listener を出力するのが一つの境目のように感じてきました。 いっそのこと、Hello() 側でイベント定義するのは諦めて、別のオブジェクトにイベント定義用メソッドをまとめた方が合理的だったのかもしれません。

think49
質問者

補足

すみません、書き間違えました。 以下のように訂正します。m(_ _)m × 私が恐れるのは、#1 の [お礼] に書いたような、「this でDOMノードを参照できて、onunload時の処理がない状況」です。 ○ 私が恐れるのは、#1 の [お礼] に書いたような、「ローカル変数でDOMノードを参照できて、onunload時の処理がない状況」です。

回答No.3

こうしないとthis==worldにならないのでダメかも。 document.getElementById('Test').addEventListener('click', function(){world.start.call(world);}, false); *机上デバッグより 私のやり方ですが、だいたい自分でnull代入かdeleteで消しています。 実際の所、クロージャ(self)の初期化タイミングの違いだけだと思いますが。 function Hello(val){ var self=this; var value=val; this.start=function(){alert(self.value);}; this.destroy=function(){self=value=null;}; } var world=new Hello('hello world!'); world.start(); window.onunload=world.destroy; どこまでメモリリーク対策になっているかはわかりません。 スクリプトを使わなくてもメモリリークしてるブラウザは多いですから。

think49
質問者

お礼

> document.getElementById('Test').addEventListener('click', function(){world.start.call(world);}, false); callメソッドを利用するところが 方法(1) とよく似ていますね。 その後、余所で教えていただいた方法でも、addEventListenerの宣言部分で this の参照先を変更していました。 <!-- 方法(1) の派生 / コンストラクタした後にメソッドを上書きする --> <p id="Test">test</p> <script type="text/javascript"><!-- function Hello (value){ this.value = value ? value : 'Hello, World!'; } Hello.prototype.start = function (event) { alert(this.value); } var world = new Hello(); document.getElementById('Test').addEventListener('click', { // listenerでオブジェクトを渡すと、this の参照先が渡したオブジェクトに変化する  value    : world.value,  handleEvent : world.start }, false); //--></script> インタフェース EventListener (DOM水準2で導入) - Document Object Model Events http://www.y-adagio.com/public/standards/tr_dom2_events/events.html#Events-EventListener やはり、addEventListener宣言時に this の参照先を変更するのが一般的なようです。 全てをメソッドを Hello () に含めようとしたところに、無理があったのだと感じました。 > スクリプトを使わなくてもメモリリークしてるブラウザは多いですから。 確かに…。 私は最近までFirefox3.6を使っていましたが、長時間立ち上げ続けているとタブを閉じてもメモリが解放されない不具合に悩まされました。 Google Chrome5 (乗り換えたのは4.1から) に乗り換えてからは、メモリ不足に悩まされることはなくなりました。 # 最も、マルチプロセスであるGoogle Chrome5 と Firefox3.6 を比較するのはフェアじゃないかもしれませんが…。

回答No.2

方法(2)はプライベート関数を呼び出すグローバル関数(メソッド)を作ってるだけで、特にクロージャにする必要はないと思います。 this.start = function(event){    start.call(this, event); } 方法(3)のクロージャの部分は、 その後のHello.prototype.start=function start(event){}で上書きされているため、これも意味がない気がします。 たぶんこれと同じではないでしょうか。 function Hello (value) {  this.value = value ? value : 'Hello, World!'; } Hello.prototype.start = function(event) { //function start(event)でも可  alert(this.value); }; もっとスリムにするならプライベート関数のstartが要らないんじゃないかと、、、 function Hello (value) {  this.value = value ? value : 'Hello, World!';  this.start = function(event){   alert(this.value);  }; } あと、オーバーロードではなくオーバーライドです。 (JavaScriptではオーバーロードできません。関数内でargumentsから条件分岐する必要があります。)

think49
質問者

お礼

> 方法(3)のクロージャの部分は、 > その後のHello.prototype.start=function start(event){}で上書きされているため、これも意味がない気がします。 function Hello (value) { } の内部コードは「コンストラクトする時に実行される」と認識しています。 ご指摘のコードですが、以下のコードで実験してみてください。 <p id="Test">test</p> <script type="text/javascript"><!-- function Hello (value) { this.value = value ? value : 'Hello, World!'; } Hello.prototype.start = function(event) { //function start(event)でも可 alert(this.value); // undefined alert(this); // [object HTMLParagraphElement] }; var world = new Hello (); document.getElementById('Test').addEventListener('click', world.start, false); //--></script> この場合、this の参照先が p#Test に変化していることが分かります。 イベントリスナでは通常、リスナー関数の中で this が参照するのは event.target と同じですが、それでは困るので 方法(3) では callメソッドを使用して this の参照先を変更しています。 > あと、オーバーロードではなくオーバーライドです。 失礼しました。 「オーバーロード = 多重定義(後付の定義が優先される)」「オーバーライド = 上書き」なのですね。 argumentsでオーバーロードと同様の動作を実現する件も把握しました。

think49
質問者

補足

多数の回答ありがとうございます。 お礼が遅れてすみません。 prototypeについてはまだ覚えて間もないため、オライリー本を熟読して間違いのないレスをしようとして時間がかかってしまいました。 > 方法(2)はプライベート関数を呼び出すグローバル関数(メソッド)を作ってるだけで、特にクロージャにする必要はないと思います。 グローバル関数という用語は知らなかったので調べてみました。 グローバルスコープで宣言された関数オブジェクトのことなのですね。 良い機会なのでprototypeの用語についても調べました。 プロトタイプ(prototype)によるJavaScriptのオブジェクト指向:CodeZine http://codezine.jp/article/detail/222 JavaScript - コンストラクタ関数 | Diaspar Journal http://diaspar.jp/node/94 ご指摘の件ですが、this が window を参照しており、window.start = function (event) {} の宣言が行われているのではないか、という指摘と受け止めました。 確かに通常の関数内での this は window を参照するのですが、new演算子でオブジェクトを生成するとコンストラクタ関数内の this は生成されたオブジェクトを参照するように思います。 方法(2) においての this は new Hello() で生成した「worldオブジェクト」を参照する、という認識でいます。 仮に this が window を参照しているならば、start で参照可能だと思いますが、 <!-- 方法(2) クロージャでメソッドを生成する --> <p id="Test">test</p> <script type="text/javascript"><!-- function Hello (value) {  function start (event) {   alert(this.value);  }  this.value = value ? value : 'Hello, World!';  this.start = (function(that){ // thisはコンストラクタした変数(world)を参照する   return function (event) {    start.call(that, event);    that = null;   };  })(this); } var world = new Hello('Hello, World2!'); console.log(world.start); // startはworldのプロパティとなる console.log(start); // Uncaught ReferenceError: start is not defined (グローバル空間にstartが存在しないので、ReferenceError に) document.getElementById('Test').addEventListener('click', world.start, false); //--></script> このように参照できません…。

回答No.1

thisに依存しないコードを書くのもありでしょうか? (function(){ var Hello = (function(){ var that = {}; var value; var start = function(){ alert(value); return that; }; that.start = start; var greeting = function(msg){ if(msg){ value = msg; return that; }else{ return value; } }; that.greeting = greeting; return function(msg){ greeting(msg || 'Hello, World!'); return that; }; })(); var world = Hello('Hello, World2!'); document.getElementById('Test').addEventListener('click', world.start, false); })();

think49
質問者

お礼

アドバイスありがとうございます。 > thisに依存しないコードを書くのもありでしょうか? クロージャで function を生成して返し、値を保持するのですね。 それも解決策の一つだと思います。 ただ…、本音をいいますと「出来ればクロージャを使いたくない」のです…。 後出しですみません。質問文では「クロージャを使わずに解決できるのか」とは書きましたが、理由については文字数制限の都合上削ってしまいました。 今、[お礼] という枠が出来ましたので、説明したいと思います。 Internet Explorer リーク パターンを理解して解決する http://msdn.microsoft.com/ja-jp/library/bb250448%28VS.85%29.aspx 掲示板/クロージャでメモリリークするパターン - TAG index http://www.tagindex.com/cgi-lib/q4bbs/patio.cgi?mode=view2&f=2586&no=8- IE6のメモリリークパターンはクロージャを使った事例が多く、クロージャを使わなければリークパターンを考える手間がかなり省けます。 prototypeはクロージャを考慮しなくてすむ点で有り難い存在なのですが、startメソッドを上書きする事を考えると私にはクロージャを使う方法しか思いつきませんでした…。 <p id="Test">test</p> <script type="text/javascript"><!-- function Hello (value) {  this.value = value ? value : 'Hello, World!';  this.doc  = document;  this.start = (function(that, start){   return function (event) { // このクロージャはローカル変数thatへの参照を保持する (従って、that.doc というDOMノードへの参照も保持する)    start.call(that, event);   };  })(this, this.start); } Hello.prototype.start = function start (event) {  alert(this.value); }; var world = new Hello('Hello, World2!'); document.getElementById('Test').addEventListener('click', world.start, false); //--></script> この場合、リスナー関数はローカル変数thatを通して、docを参照し続けるので循環参照してしまいます。 これが this.doc という形で参照するなら、「this はローカル変数ではないので循環参照しないだろう」という思惑がありました。 しかし、クロージャを生成する時点でローカル変数thatの参照を切れない(質問文ではnullで埋めましたが、これでは二回目以降のイベントが発動しないという問題がありました)ので、結局は循環参照してしまいます。 この場合の解決法は、今思いつく限りでは4つあります。 ・グローバル変数でDOMノードを保持する ・this でDOMノードを保持しない (必要なら関数実行時に引数で渡す。(例) world.start(document); ) ・リスナー関数で必要な値は引数で渡し、this参照しない ・window unload時に全てのイベントを削除する unload時の処理は正攻法でどんな場合にも適用できるのですが、手間と効率の兼ね合いで省略する事もあります。 ここでもう一つ、新しい回避手段が見いだせればと思い、質問に至りました。 ただ…、改めて考えてみると this.doc = document; を宣言するならば、方法(1) でもリークしてしまいます。 「クロージャを使わない方法を」と考え続けてきましたが、「考える上での原点がずれていたのかもしれない」とも思い始めています…。

関連するQ&A

  • コンストラクタとプロトタイプについて

    ネットで検索したりして調べているのですがいまいちわからなかった箇所が あるので質問させていただきます。 コンストラクタで設定するのとプロトタイプで設定する違いがいまいちわかりません。 例えば function Test { this.prop = hoge; } Test.prototype.prop1 = hogehoge; の場合 コンストラクタのほうが優先されてhogeがでるのはわかるのですが 下記の場合はプロタイプのほうが優先されてhogehogeと出てしまうのは どうしてでしょうか? <script language="javascript"> <!-- //コンストラクタ function Test(){alert("hoge"); } //prototypeでセット Test.prototype=alert("hogehoge"); //オブジェクト作成 var TEST = new Test(); window.onload=TEST; --> </script>

  • 【PHP】コンストラクタ―について

    コンストラクタ―について検索して調べると概ね以下のように説明されています。 ---------------------------------- コンストラクタ インスタンス生成時にオブジェクトを初期化したい場合にコンストラクタメソッドを使用できます。 コンストラクタメソッドは以下のように引数を指定する事もでき、インスタンス生成時に__construct()が自動的に実行されます。 ---------------------------------- イマイチ判然としません。 「newによってインスタンスを作成される時に自動的に呼び出されるもの」と自分では解釈していました。 例えば以下のような場合 public function __construct($text) { $this->text = $text; } 【$text】の値をnewされることによって自動的に「保持」されるということなのでしょうか? 実際に表示させるときは【$posts[0]->show();】(show()メソッド)で表示させるわけですよね? その上には【private $text;】でプロパティがあります。 ごく基本的な質問かと思いますが、【public function __construct($text)】 コンストラクタ―を作成する理由がよくわかりません。 初学者でも分かりやすいように解説していただけないでしょうか? 宜しくお願い致します。 <記述サンプル> ------------------------------- <?php class Post { private $text; public function __construct($text) { $this->text = $text; } public function show() { printf('%s' . PHP_EOL, $this->text); } } class SponsoredPost extends Post { private $sponsor; public function __construct($text, $sponsor) { parent::__construct($text); $this->sponsor = $sponsor; } public function showSponsor() { printf('%s', $this->sponsor); } } $posts = []; $posts[0] = new Post('hello'); $posts[1] = new Post('hello again'); $posts[2] = new SponsoredPost('hello hello', 'Yahoo'); $posts[0]->show(); $posts[1]->show(); $posts[2]->show(); $posts[2]->showSponsor();

    • ベストアンサー
    • PHP
  • thisとvar ?

    javascript初心者です。 コンストラクタ(プロトタイプ)とクロージャを学んでいますが、 コンストラクタ(プロトタイプ)では、関数内にthisで変数宣言、クロージャはvarで宣言しています。 この違いの理由は何でしょうか?漠然とした質問ですみません。 thisとvarでの変数宣言の違いなど教えていただけないでしょうか? コンストラクタ-------------------- function Person(n){ this.name = n; } Person.prototype.city = 'Tokyo'; Person.prototype.moveTo = function(c){ document.write(this.name + ': Moving to... ' + c + '<br>'); Person.prototype.city = c; } クロージャ------------------- function Person(n, a){ var name = n; var age = a; return { getName: function() { return name; }, setAge: function(i){ if( 0<= i ){ age = i; } }, getAge: function(){ return age; } } }

  • javascriptのconstructorプロパティについて

    constructorプロパティとは、 オブジェクトの初期化で使用されたコンストラクタ関数を参照 とのことなので、下記2パターンのPGを作成しました 1.prototypeの明記なし function Hoge(){ this.init = "Hogeで初期化"; this.getInit = function(){ return this.init; } } var obj = new Hoge(); alert(obj.constructor == Hoge); for(prop in obj){ alert( prop + " - " + obj[prop]); } //実行結果 True init - Hogeで初期化 getInit - function () { return this.init; } 2.prototypeの明記あり function Hoge(){ this.initialize.apply(this,arguments); this.init = "Hogeで初期化"; this.getInit = function(){ return this.init; } } Hoge.prototype ={ initialize:function(){ this.init = "Hoge.prototype.initializeで初期化"; }, getInit:function(){ return "Hoge.prototype.getInit()"; } } var obj = new Hoge(); alert(obj.constructor == Hoge); for(prop in obj){ alert( prop + " - " + obj[prop]); } //実行結果 false init - Hogeで初期化 getInit - function () { return this.init; } initialize - function () { this.init = "Hoge.prototype.initialize\u3067\u521D\u671F\u5316"; } ・質問内容 prototypeの明記なしの場合は、Hogeのコンストラ関数を参照している(結果がTrueのため) prototypeの明記ありの場合は、falseのためコンストラ関数を参照していないのですが、 prototype明記あり、なしで結果が異なる理由が分からない状態です。 (prototype.constructorにも手を出したのですが、上記が解決しないため  constructorプロパティに関してのみ質問した次第です) ネット、書籍等で調べたのですが、検討がつかない状態です。 お手数ですが、ご教授お願い致します。

  • コンストラクタと静的メソッドを簡単に定義する

    立て続けに質問することをお許し下さい。。 以下のようなPersonクラスがあるとします。 /**** Person クラス(全角スペース表記) ****/ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <HTML>  <HEAD>   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">   <META HTTP-EQUIV="Content-Script-Type" CONTENT="javascript">   <TITLE>教えて!gao</TITLE>   <SCRIPT TYPE="text/javascript">    function Person(name, age, sex, color) {     this.name = name;     this.age = age;     this.sex = sex;     this.color = color;    }    Person.prototype = {     name:null,     age:null,     sex:null,     color:null    };    Person.YELLOW = "黄色人種";    Person.BLACK = "黒色人種";    Person.WHITE = "白色人種";    function test() {     var person = new Person("ggaogg", 23, "male", Person.YELLOW);     var resultBlock = document.getElementById("resultBlock");     for (property in person) {      resultBlock.appendChild(document.createTextNode(property + " = " + person[property]));      resultBlock.appendChild(document.createElement("BR"));     }    }   </SCRIPT>  </HEAD>  <BODY ONLOAD="test()">   <DIV ID="resultBlock"></DIV>  </BODY> </HTML> /********************************************************/ これの実行結果は、以下の通りです。 name = ggaogg age = 23 sex = male color = 黄色人種 しかし、静的変数を、毎回「Person.」を付けて記述するのは面倒で、すべて{}でひとくくりになっていたほうが可読性も増すと思い、以下のようにできると思いましたが、そうしたところ new Person("ggaogg", 23, "male", Person.YELLOW); の部分がコンストラクタではないというようなエラーとなってしまいます。(Web等でこの書き方はあまり見かけないが自分は気に入っている) /************ 変更した部分 ******************/ Person = { YELLOW : "黄色人種", BLACK : "黒色人種", WHITE : "白色人種" }; /********************************************/ 多分、初めに行ったコンストラクタの定義とこの{}の定義とが競合しているためだと思うのですが、何かよい書き方ありませんでしょうか。

  • 【javascript文法】 prototype.js Ajax.Request onSuccess:に指定するfancitonの書き方

    以下HTMLは開いた時に'2'がアラート、続いて'hello world'がアラートされるものです。 動作的には期待どおりに動きますが、記述方法に難を感じています。 課題としては、Ajax.RequestのonSuccess:に指定するfancrionをprototypeを使って 宣言したfunctionで且つ、引数を与えたいです。 (Ajax.Requestが返してくる引数も使いたい。) 表現が下手ですみません。 ※行頭は全角スペースです。実際に機能させる為には  半角に修正もしくは行頭スペースは削除して下さい。 <html> <head> <script type="text/javascript" src="prototype.js"></script> <script type="text/javascript"><!-- function http(){  this.val=1; }; http.prototype.request = function(){  var url="/ruby/hello.cgi"; //実際に検証する為にはwebサーバにこのcgiを置く必要あり  new Ajax.Request(url, {   method:"get",   onSuccess: function(h){r.processRequest(h,1);}   /* ↑http.prototype.requestの中なので、r.ではなくthis.processRequest(h,1)と記述したい*/  }); }; http.prototype.processRequest = function(h,arg){  alert(this.val+arg);  alert(h.responseText); }; var r = new http(); r.request(); //--></script> </head> <body></body> </html> 以下"/ruby/hello.cgi"の例 #!/usr/bin/ruby puts "Content-Type: text/html\n\n" puts "hello world" ※実際に実行する場合はprototype.jsが必要です。 これで、期待通りの結果が得られるのですが、コメントに記載したように、 http.prototype.request = function()の中でonSuccess:に指定する関数が、 this.と記述できず、r.と記述しないと動作しません。 http.prototype.request = function()の中で、 ~~~~~~~~~~~~~~ http.prototype.processRequest = function(h,arg) ~~~~~~~~~~~~~~ を使うのですから、 r.processRequest(h,1)ではなく、 ~ this.processRequest(h,1) ~~~~ と記述したいです。 因みに同じような質問を http://oshiete1.goo.ne.jp/qa4663141.html でしており、この『応用』だとも思うのですが、名案浮かばずです。 このような場合、どう記述するのがベターなのでしょうか? よろしくお願いいたします。

    • ベストアンサー
    • AJAX
  • JQueryのclick(fnc)で指定された関数中のthis

    JQueryのclick(fnc)で関数中のthisの扱いについてお尋ねします。 下記コードでは仮に2つのクラスをモデルとコントローラとして、コントローラからモデルの関数を呼び出しています。value="HTML Tag"のボタンは期待したとおりに'chicken crows.'を表示しますが、value="JQuery"のボタンはcrow()中のthis.modelがundifinedになってしまいます。後者では$.fn.controllers.chickenController.crowをinputタグにコピーしてそれを実行しているのではないかと思います。そのためかthisはHTML Inputを指します。 thisを使用する以外にモデルのcrow()を実行する方法はありませんでしょうか?条件としてはモデルとコントローラのオブジェクトのペアは複数有りうり、コンストラクタやプロトタイプ中で一意の変数を定義できない点にご注意ください。 <script tyoe="text/javascript" src="jquery-1.3.2.min.js" ></script> <script type="text/javascript"> $(function(){ //名前空間 $.fn.models = new Object(); var models = $.fn.models; // コンストラクタ models.bird = function(type){this.type = type;} // インスタンスメソッド models.bird.prototype = {  crow: function(){ alert(this.type + ' crowed.');} } var chicken = new models.bird('chicken'); //名前空間 $.fn.controllers = new Object(); var controllers = $.fn.controllers; // コンストラクタ controllers.birdController = function(model){this.model = model;} // インスタンスメソッド controllers.birdController.prototype = {  crow: function(){   this.model.crow(); // JQuryの場合thisはHTML Inputを指します。  } } $.fn.controllers.chickenController = new controllers.birdController(chicken); $('#b').click($.fn.controllers.chickenController.crow); }); </script> <html> <body>  <input type="button" onclick="$.fn.controllers.chickenController.crow();" value="HTML Tag" />  <input type="button" id="b" value="JQuery" /> </body> </html>

  • prototypeについて

    this,prototypeを使ったメソッドの定義について質問します。 ●thisを使う場合 function SomeObject(){ ・・・・・・・・ this.f = function(){ ・・・・・ } } ●prototypeを使う場合 function SomeObject(){ ・・・・・・・・ } SomeObject.prototype.f = function(){ ・・・・・・・・ } カスタムオブジェクトのメソッドをthisキーワードを使わず、prototypeで定義する利点はどのようなことでしょうか。thisキーワードとprototypeの使い分けが分からなくて質問しました。よろしくお願いします。

  • 引数の違うメソッド

    PHPで同名で引数の違うメソッドを作成したいのですが、可能でしょうか? <?php class Test { var $test_str = ""; function set($str){ $test_str = $str; //$this->$test_str = $str; // どちらでも良い? } function Test() { // 引数なし } function Test($str) { // 引数あり $this->set($str); } } $obj = new Test(); ?>

    • ベストアンサー
    • PHP
  • ビルドインクラスにメソッドを追加したい

    たとえばこんなふうに、既存のBuild-Inクラスに、独自のメソッドを追加したいのですが・・ Array.prototype.get1 = function () {   // とくに意味なしなメソッド   return this[1] ; } ネスケ・Mozilla系やOperaはこれでうまくいくのですが、 IEだけまったく動作しません。 IEで、既存のビルドインクラスにメソッドを追加する方法って、ありませんでしょうか?

専門家に質問してみよう