質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

3732閲覧

prototypeに関数を定義する価値は?

i50

総合スコア227

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

9クリップ

投稿2015/10/19 02:55

メインがPHPで最近javascriptを勉強し始めた者です。
私の理解がそもそも違うということであれば、
指摘頂けるととても嬉しいです!

prototypeに関数を定義する価値ってどのような場合にあるのでしょうか。
以下の例でいうqux関数です。

javascript

1var Hoge = function(){ 2 var foo = null; 3 this.bar = null; 4 this.baz = function(){}; 5}; 6Hoge.prototype = { 7 qux:function(){} 8}; 9Hoge.xyzzy = function(self){} 10

まずオブジェクト指向的に、隠すべき変数は隠すべきと思っています。
その為、bar変数ではなくて、foo変数を使うはずです。
(getter/setterが対であるような場合だけbarでいいかもしれませんが)
そして、そのfooを参照し処理を行うので、baz関数が必要です。
qux関数ではfooを参照できないので使用できないです。

オブジェクト指向は置いておいて…ということであれば、qux関数の価値は分かります。
fooを使わなければqux関数で事足りますし、
newするときにbazの生成にかかるコストが不要という価値があると思っています。
しかし、newを考えている時点でオブジェクト指向を意識していると思いますし、
そうなるとやはりfooを使うのでしょうから、やはりquxの使いどころがなさそうに思います。

qux関数の性質を考えてみますと、
quxは継承してもプロトタイプチェーンで呼び出せ、
qux関数内部で、thisで自身のインスタンスを参照できます。
しかし、qux関数を呼び出せる時点でthis相当の値を渡す事が出来るはずですし、
そもそもqux内部のみで呼び出せる変数は外部からも参照できるため、
quxの内部構造はいわゆるユーティリティ関数的であるはずで、インスタンスに結びついている必要はないように思います。
それであればxyzzyに必要な引数を渡せばいいのではないかと思います。

ここでいうqux関数はどういう時に定義し、使用するのでしょうか…。
よろしくおねがいします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

まずオブジェクト指向的に、隠すべき変数は隠すべきと思っています。

「隠すべき変数は隠すべき」ですが、「車は車です」といっているような違和感が…。

まず、**なぜインスタンスプロパティを隠すべきか?**を考えてみてはどうでしょうか?
私としては「隠すべき理由」に「意図せずに書き換えられて他のプロパティとの整合性が取れなくなる」があげられると考えます。
ただし、全てのプロパティを隠すべきとは思いませんし、ECMAScript ではいくつかの解が設けられています。


Array#length は公開されていますが、length プロパティを書き換えると配列の要素数も書き換わる(setter)為、問題なく機能します。

JavaScript

1var array = [1, 2, 3, 4, 5]; 2array.length = 1; 3console.log(array); // [1]

Function#length は公開されていますが、length プロパティは読み取り専用の為、不正に書き換わることはありません。

JavaScript

1function sum (a, b) { return a + b; } 2sum.length = 3; // TypeError: Cannot assign to read only property 'length' of function sum(a, b) { return a + b; } 3console.log(sum.length); // 2 4console.log(Object.getOwnPropertyDescriptor(sum, 'length')); // Object {value: 2, writable: false, enumerable: false, configurable: true}

Array.prototype を拡張すると for-in で拡張した Array.prototype 上のプロパティが列挙される(いわゆる Array.prototype 汚染)がありますが、既存の Array.prototype.slice 等は for-in で列挙されません。
なぜなら、Array.prototype.slice は列挙不可能(enumerable === false)だからです。

JavaScript

1var array = [], keys = []; 2for (key in array) { // enumerable なプロパティは列挙しない 3 keys.push(key); 4} 5console.log(keys); // [] 6console.log(Object.getOwnPropertyDescriptor(Array.prototype, 'slice')); // {writable: true, enumerable: false, configurable: true}

いずれも、Object.defineProperty を使うことで実装可能です。
Object.defineProperty は IE7 で使えず、IE8 で DOM ノードにしか使えない機能制約がある為、事実上、IE9+ が対象ブラウザでなければ使えません。
制作ポリシーによっては2016/01/12までは使えない判断もあると思います。


どうしても、プライベートプロパティが必要であれば、ES6 の WeakMap を使用する事で実装可能です。
WeakMap は ES3 の範囲内でも polyfill を書くことが出来る為、現在でも十分に実用的です。

JavaScript

1var Hoge = (function () { 2 var privateMap = new WeakMap; 3 4 function Hoge (x, y) { 5 privateMap.set(this, {x: Number(x), y: Number(y)}); 6 } 7 8 Object.defineProperty(Hoge.prototype, 'sum', {writable: true, configurable: true, enumerable: false, value: function sum () { 9 var obj = privateMap.get(this); 10 11 return obj.x + obj.y; 12 }}); 13 14 return Hoge; 15})(); 16 17var hoge = new Hoge(2, 4); 18 19console.log(hoge.sum()); // 6

投稿2015/10/19 15:47

編集2015/10/20 03:57
think49

総合スコア18162

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

i50

2015/10/20 03:18

回答ありがとうございます! Object.definePropertyというものすごい機能があることに驚いています…。 悩みは解消しました! レスしてまいります。 >「隠すべき変数は隠すべき」ですが、「車は車です」といっているような違和感が…。 表現が変で申し訳ありません…。隠すべき変数と思っているのは、 ・ユーザに変更させたくない(責任範囲を明確にする) ・ユーザに見せるには実装寄りすぎる(処理内容を抽象的にする) といった変数で、当然全てを隠す必要はないと思っています。 とはいえ、Object.definePropertyでのコントロールで、多くはなんとかなりそうな気がしています。 また、後述のメモリリークの問題を考えると、 javascriptという言語とプライベート変数は相性が悪そうですね…。 >どうしても、プライベートプロパティが必要であれば、ES6 の WeakMap を使用する事で実装可能です。 追加で2点ほど質問という形になってしまって申し訳ないです…。 あえてWeakMapを使用されているように見受けられますが、 弱参照でないとメモリリークの危険性があるという事でしょうか。 例えば、privateMapの実装を強参照で自作した場合、 hoge変数にnullを入れて参照を切ったつもりでいても、 キーであるthis(実体はhoge)を、 Hogeが保持しているprivateMapが強参照で保持している為、 hogeの実体がGCに回収されずメモリリークする、という理解で良いのでしょうか。 もう1点ですが、私の様な書き方ではなくて、例に挙げられたような var Hoge = (function () { //プライベートプロパティ var privateMap = new WeakMap; //コンストラクタ function Hoge (x, y) {} //メソッド Object.defineProperty(Hoge.prototype, 'sum', {writable: true, configurable: true, enumerable: false, value: function sum () { }}); return Hoge; })(); という書き方が一般的なのでしょうか。 どうにも、javascriptでのオブジェクトの定義の書き方が色々あるように思えて これだというのがつかめておりません…。 よろしくお願い致します。
think49

2015/10/20 08:51 編集

> ・ユーザに変更させたくない(責任範囲を明確にする) > ・ユーザに見せるには実装寄りすぎる(処理内容を抽象的にする) - なぜ「ユーザに変更させたくない」のか。責任分界点の判断基準はどこか。 - 具体的にはどのような基準で「実装寄り」になるのか。 を明確にすると更に具体的な考えに繋がると思います。 > あえてWeakMapを使用されているように見受けられますが、弱参照でないとメモリリークの危険性があるという事でしょうか。 WeakMap を使用している理由にメモリリークは関係なく、他のインスタンスのプライベートプロパティに参照不可能にする為です。 仮に WeakMap を Map で代替した場合、Map.prototype.entries で全ての Iterator を参照出来る為、不正に書き換わる可能性があります。 > 例に挙げられたような ... という書き方が一般的なのでしょうか。 一般的かどうかは知りませんが、他人が使っているかどうかを気にする必要はないと思います。 重要なのは自分が書いたコードが期待通りに動作する事です。 # Function#length のコードを修正しました。
i50

2015/10/20 05:11

レスありがとうございます! >明確にすると更に具体的な考えに繋がると思います。 おっしゃる通り、基準が明確ではないですね…。 このあたりはjavascriptに限らない話だと思っていますので、良く考えてみます! >WeakMap を使用している理由にメモリリークは関係なく、他のインスタンスのプライベートプロパティに参照不可能にする為です。 なるほど。意図が分かりました。 弱参照という性質を保つため、iterate出来ないことを利用しているのですね。 >重要なのは自分が書いたコードが期待通りに動作する事です。 ありがとうございます。おっしゃる通り期待通りの動作をしなくては意味が無いですね…。 私事ではありますが、今は一人でコードを書いているのですが、 後任の方が来られた場合に「なんでこんな書き方をしているんだ?」 という疑問が発生しないようにと、一般的な書き方があれば近づけたいとの思いからでした。
guest

0

PHPはよく分からないのですが、こういうことでは。

javascript

1var Hoge = function(){ 2 var foo = null; 3 this.bar = null; 4 this.baz = function(){return Hoge.corge(foo)}; 5 this.quux = function(){return this.qux(foo)}; 6}; 7Hoge.prototype = { 8 qux:function(x){return x} 9}; 10Hoge.xyzzy = function(self){return self.foo} 11Hoge.corge = function(x){return x} 12 13var instance = new Hoge(); 14Hoge.xyzzy(instance); //undefined 15instance.baz(); //null 16instance.quux(); //null

クラスメソッド的な定義をするとインスタンスのprivateなメンバを参照できませんし、インスタンスメソッドから呼ぶのもなんか気持ちが悪いです←
もちろん、quxの処理をコンストラクタに書いても同じことができますが、インスタンスごとに作成されてしまいますから、効率が悪い場合もあるのかなあ、と。

投稿2015/10/19 08:18

編集2015/10/19 08:25
Lhankor_Mhy

総合スコア36115

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

i50

2015/10/19 23:54

回答ありがとうございます! >クラスメソッド的な定義をするとインスタンスのprivateなメンバを参照できません >インスタンスごとに作成されてしまいますから、効率が悪い場合もある ありがとうございます!このあたりは承知しております。 ポイントは >インスタンスメソッドから呼ぶのもなんか気持ちが悪い >this.baz = function(){return Hoge.corge(foo)}; あたりとなるのでしょうか。 私はこの書き方も、場合によってはありえると思っています。 corgeの処理がHogeの責任範囲と十分関係があり、かつインスタンスの特定が不要な場合 このような書き方も発生しえるかなと考えています。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問