JavaScriptにおいて、JavaやC++で言うところのメンバ関数を実現しようとすると、コンストラクタ関数内で定義する方法と、プロトタイプにメソッドを追加する方法があると思います。これについて、前者はオブジェクトが生成されるたびにメソッドが追加されるため効率が悪い、と聞きました。例えば末尾に記した例では、new Person()が呼ばれるたびにsay()が追加されることが冗長である、ということだと理解しています。
この話を聞いて以降、私はプロトタイプにメソッドを定義するようにしています。幸いなことにまだ困った状況に遭遇していません(Javaのクラス/オブジェクトのように使えている)。「コンストラクタ関数内でメソッドを定義せざるを得ないケースはほとんど無い」とさえ思っています。
しかし、ふと気付きました。これは勉強不足だ、単に経験不足だと。
そこで書籍を読んでいるところなのですが、まだ「コンストラクタ関数内でメソッドを定義したほうが良い」と思える状況に出会えていません。
コンストラクタ関数内でメソッドを定義した方が良い、せざるを得ないような状況というか、イディオムを紹介していただけないでしょうか?
コンストラクタ関数内にメンバ関数を定義:
javascript
1var Person = function(name) { 2 this.name = name; 3 this.say = function() { // こうやってメソッドを定義することはあるのだろうか? 4 return "I am " + this.name; 5 }; 6};
プロトタイプにメソッドを追加する:
javascript
1var Person = function(name) { 2 this.name = name; 3}; 4Person.prototype.say = function() { 5 return "I am " + this.name; 6};
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
プライベートメンバーを実現したい場合なんかどうでしょうか。
javascript
1var Person = function(name) { 2 var _name = name; 3 this.say = function() { 4 return "I am " + _name; 5 }; 6};
この例だと、クロージャ内に閉じ込められた変数_name
にsay
メソッド以外からアクセスする方法はありません。
(実行時にはブレークポイントでいくらでも覗けますが、開発時に限ってはプライベートです。)
開発時にプライベートメンバーに間違ってアクセスすることを防ぐことには成功していますが、そもそも分かりづらい本末転倒感があります。
this._name
みたいに_
から始まるものはプライベートというコーディング規約を決めておくほうが現実的でしょうし、ある程度市民権も得ています。
そもそも、「JSのプロトタイプの仕組みを使用した継承機構」と、「プライベートメンバー」は両立できないという結論が定説のようです。
というのも、JSでプライベートを実現するにはクロージャによる閉じ込みくらいしか方法がありません。
つまり、インスタンスごとに異なるクロージャを用意する必要があるため、ひとつのプロトタイプメンバーを使いまわしていては実現不可能というわけです。
上の例では、メソッドsay
はPerson
をインスタンス化する度に別々に定義されてしまい無駄がありますが、別々だからこそ、それぞれが異なる_name
変数にアクセスできるというわけです。
投稿2015/08/14 08:33
総合スコア790
0
ベストアンサー
コンストラクタ内でメソッドを定義するという方法を初めて知りました。
逆に目から鱗です。
今まで知らなかったということは、私自身は一度も必要になる場面に
出会ったことがないのだと思います。
一応、こういう場面なら必要ではというのを考えてみました。
なお、functionとかprototypeとかの綴りが面倒だったのでCoffeeScriptで書いています。
http://coffeescript.org/ の TRY COFFEESCRIPT で変換して下さい。
classで書いていますが、JavaScriptで変換したのを見ていただければ、
意味は同じであるとわかると思います。
- コンストラクタ内のローカル変数を束縛しておきたい!
CoffeeScript
1class Counter 2 constructor: -> 3 i = 0 4 @count = -> 5 return ++i 6 7a = new Counter() 8console.log a.count() # => 1 9console.log a.count() # => 2 10console.log a.count() # => 3
呼び出すたびにカウントが増えるというよくあるやつです。
コンストラクタ内のローカル変数i
を束縛するには上のようにするしかありません。
ただ、このパターンですと、そもそもi
をインスタンス変数にすればいいだけです。
CoffeeScript
1class Counter2 2 constructor: -> 3 @i = 0 4 count: -> 5 return ++@i
- コンストラクタの引数によってメソッドの動作を変えたい!
下記はコラッツの問題の変換処理で次を求めていく整数のラッパクラスです。
CoffeeScript
1class Collatz 2 constructor: (@num) -> 3 if @num % 2 == 0 4 @next = -> new Collatz(@num / 2) 5 else 6 @next = -> new Collatz(3 * @num + 1) 7 8c = new Collatz(6) 9console.log c.num # => 6 10console.log c.next().num # => 3 11console.log c.next().next().num # => 10 12console.log c.next().next().next().num # => 5 13console.log c.next().next().next().next().num # => 16
数字が偶数か奇数かで次を求める式が変わるようにして、
効率よく?計算しているような気だけします。
ただこれも、単に毎回場合分けしたほうが、スッキリとかけます。
CoffeeScript
1class Collatz2 2 constructor: (@num) -> 3 next: -> 4 if @num % 2 == 0 5 new Collatz(@num / 2) 6 else 7 new Collatz(3 * @num + 1)
もし、@num
がインスタンス変数でなかったとしても、1.と同じで、
インスタンス変数にすればいいだけの話です。
以上の2つが考えつきましたが、どちらも適当なインスタンス変数を持てばいいだけですし、
動作も複雑になって、かえって見通しが悪いプログラムになってしまいました。
必須になるような場面は無いのではないでしょうか?
投稿2015/08/14 08:29
総合スコア21735
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
物によって処理が違う場合とかなら使いそうですがコンストラクタ内で定義と言うのかというと微妙。
クロージャ作る時が一番使う可能性あるのかな。
javascript
1function A(){ 2 A.prototype.getName=function (){ 3 return this.name; 4 }; 5 A.prototype.setMethod=function (_funcName,_func){ 6 this[_funcName]=_func; 7 }; 8 A.prototype.name="Jon"; 9}; 10 11var c=new A(); 12c.setMethod( 13 "getName", 14 function (){ 15 return this.name+" : nice name"; 16 } 17) 18c.getName();//Jon : nice name 19 20/* 21c.getName=function (){ 22 return this.name+" : nice name"; 23}; 24と同じなのでsetMethod()なんて作らなくてもいい気もする 25*/
投稿2015/08/14 15:36
総合スコア730
0
まず、プロパティとメソッドを区別する必要はありません。
コンストラクタ処理の中で決定されるプロパティ、メソッドはコンストラクタ内で定義します。
JavaScript
1function Person (name, callbackfn) { 2 this.name = name; // nameプロパティはコンストラクタ処理中に値が決定される 3 this.hoge = callbackfn; // hogeメソッドはコンストラクタ処理中に値が決定される 4}
インスタンスが生成される度に値が動的に変化するプロパティ、メソッドはコンストラクタ内で定義してやり、常時同じ処理、値になるプロパティ、メソッドは prototype 上で定義してやります。
ECMAScript 5 には private 変数がありません。
従って、private 変数と同様の処理をする場合にはコンストラクタ内のローカル変数にクロージャで値を閉じ込める必要があります。
JavaScript
1function Person (name) { 2 this.getName = function getName () { return name; }; // private変数 name への getter 関数 3} 4 5new Person('Ken').getName(); // "Ken"
getName メソッドは常時同じ処理を行うメソッドですが、ローカル変数 name を参照する為にはコンストラクタ内で定義してやる必要があります。
インスタンスが生成される度にメモリを消費するので効率がよくありませんが、ECMAScript 5 の範囲内ではどうしようもありません。
ECMAScript 6 にも private 変数はありませんが、WeakMap を使うことで private 変数のようなものを定義できるようになりました。
JavaScript
1var Person = (function () { 2 var privateMap = new WeakMap; 3 4 function Person (name) { 5 privateMap.set(this, name); 6 } 7 8 Person.prototype.getName = function getName () { 9 return privateMap.get(this); 10 } 11 12 return Person; 13}()); 14 15console.log(new Person('Ken').getName()); // "Ken" 16console.log(new Person('Alice').getName()); // "Alice"
投稿2015/08/14 08:43
編集2015/08/14 08:48総合スコア18164
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/08/14 10:49
2015/08/14 11:55 編集
2015/08/16 00:39
0
2019年時点では、「イベントハンドラなど、クラス外部へ引き回せるように、this
をインスタンスに固定したメソッドを作りたい」という場合に、コンストラクタ内での生成(あるいはそれに相当するclass property + arrow function)がよく行われています。
jsx
1class SomeComponent extends React.Component{ 2 constructor(props) { 3 super(props); 4 // 別なところに引っ張り出しても、thisはこのインスタンスから切れない 5 this.handler1 = () => console.log(this); 6 } 7 8 // まだ提案中だけど、こんな書き方もある 9 handler2 = () => console.log(this); 10}
投稿2019/03/03 04:17
総合スコア145184
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/08/14 11:02
退会済みユーザー
2015/08/14 15:56
2015/08/21 02:47