下記のようなソースを作っては見たものの、期待した動きができていません、ご指導いただけないでしょうか。
var fun = new abc(2,3);
alert(fun.a); // 2を期待
alert(fun.b); // 3を期待
alert(fun.c); // 6を期待
fun.a = 4;
alert(fun.a); // 4を期待
alert(fun.b); // 3を期待
alert(fun.c); // 12を期待
function abc(arg1,arg2){
this.a ={
_value:0,
get function(){
return this._value;
},
set function(val){
this._value = val;
this.c = calcab(this._value, this.a);
}
};
this.b ={
_value:0,
get function(){
return this._value;
},
set function(val){
this._value = val;
this.c = calcab(this.a, this._value);
}
};
this.a = arg1;
this.b = arg2;
function calcab(arg1, arg2){
return arg1 * arg2;
}
}
今のところはECMAScript5で標準化されたObject.create
, Object.defineProperty
, Object.defineProperties
を使ってください。
なお、IE8以下ではこれらのメソッドは定義されておらず、getter, setterを定義する方法はありません(IE8はDOMオブジェクトに限り__defineGetter__
, __defineSetter__
によってgetter, setterを定義することができます)。
function abc(arg1, arg2) { this.a = arg1; this.b = arg2; } Object.defineProperties(abc.prototype, { a: { get: function () { return this._a; }, set: function (value) { this._a = value; this.c = calcab(value, this.b); }, configurable: true, enumerable: true }, b: { get: function () { return this._b; }, set: function (value) { this._b = value; this.c = calcab(this.a, value); }, configurable: true, enumerable: true } }); function calcab(arg1, arg2) { return arg1 * arg2; } var fun = new abc(2, 3); alert([fun.a, fun.b, fun.c]); // "2,3,6" fun.a = 4; alert([fun.a, fun.b, fun.c]); // "4,3,12"
c
をgetterにしてみる。
function Abc(a, b) { this.a = a; this.b = b; } Object.defineProperty(Abc.prototype, 'c', { get: function () { return this.a * this.b; }, configurable: true, enumerable: true }); var fun = new Abc(2, 3); alert([fun.a, fun.b, fun.c]); fun.a = 4; alert([fun.a, fun.b, fun.c]);
プロパティ定義は、new時にthisに対して行うのではなく、newよりも前の時点でprototype属性に対して行います。
その時点でget/setキーワードを利用します。
(これらのキーワードはブラウザ依存だし、javascriptらしいやり方ではなくC#のコードみたいに見えますし、それに使わなくてもクラスは作れるので、余りおすすめはしません。)
new時には、(定義ではなく)実際の値の代入処理をしましょう。
下記のサンプルコードが役立つと思いますが。
http://hondou.homedns.org/pukiwiki/pukiwiki.php?Javascript%20get%2Fset%20%B1%E9%BB%BB%BB%D2
こちらの最初のFooのサンプルみたいに、
prototype内でクロージャの性質を使ってgetter/setterを作るほうが、JSらしいやり方です。
これなら、firefox以外のどのブラウザでも動作が保証されます。
http://d.hatena.ne.jp/kazu-yamamoto/20071101/1193884132
今のところはECMAScript5で標準化されたObject.create
, Object.defineProperty
, Object.defineProperties
を使ってください。
なお、IE8以下ではこれらのメソッドは定義されておらず、getter, setterを定義する方法はありません(IE8はDOMオブジェクトに限り__defineGetter__
, __defineSetter__
によってgetter, setterを定義することができます)。
function abc(arg1, arg2) { this.a = arg1; this.b = arg2; } Object.defineProperties(abc.prototype, { a: { get: function () { return this._a; }, set: function (value) { this._a = value; this.c = calcab(value, this.b); }, configurable: true, enumerable: true }, b: { get: function () { return this._b; }, set: function (value) { this._b = value; this.c = calcab(this.a, value); }, configurable: true, enumerable: true } }); function calcab(arg1, arg2) { return arg1 * arg2; } var fun = new abc(2, 3); alert([fun.a, fun.b, fun.c]); // "2,3,6" fun.a = 4; alert([fun.a, fun.b, fun.c]); // "4,3,12"
c
をgetterにしてみる。
function Abc(a, b) { this.a = a; this.b = b; } Object.defineProperty(Abc.prototype, 'c', { get: function () { return this.a * this.b; }, configurable: true, enumerable: true }); var fun = new Abc(2, 3); alert([fun.a, fun.b, fun.c]); fun.a = 4; alert([fun.a, fun.b, fun.c]);
「クラス定義とメインコードを明確に分離し、クラス定義→メインコードの順で実行されるようにする」とした方がいいと思います。
<script src="abc.js"></script> <script> (function () { // 即時関数 var fun = new abc(2, 3); // 省略 })(); </script>
そうはいかない理由があるのであれば、コンストラクタ関数内ですべてのプロパティを定義してしまえば手っ取り早いです。グローバル変数として定義したくないcalcab
も関数内に突っ込んでしまえばしまいです。
ただし、abc
関数が呼び出される度にdefineProperty
の処理であったりcalcab
関数の実体化であったりが行われるため、何百・何千とインスタンスが作られるようなクラスでは避けた方が良いでしょう。
また、コンストラクタ関数内がしっちゃかめっちゃかになってしまうのもいまいちだと思います。
var fun = new abc(2, 3); alert([fun.a, fun.b, fun.c]); // "2,3,6" fun.a = 4; alert([fun.a, fun.b, fun.c]); // "4,3,12" function abc(arg1, arg2) { Object.defineProperties(this, { a: { get: function () { return this._a; }, set: function (value) { this._a = value; this.c = calcab(value, this.b); }, configurable: true, enumerable: true }, b: { get: function () { return this._b; }, set: function (value) { this._b = value; this.c = calcab(this.a, value); }, configurable: true, enumerable: true } }); function calcab(arg1, arg2) { return arg1 * arg2; } // コンストラクタとしての処理はここで this.a = arg1; this.b = arg2; }
私は使った経験がないため解説できませんが、Require.jsなどの依存関係を解決するためのライブラリについて調べてみると解決できるかもしれません。
ありがとうございます。
思い通りの処理が実現できました。
「クラス定義とメインコードを明確に分離し、クラス定義→メインコードの順で実行されるようにする」とした方がいいと思います。
そうはいかない理由があるのであれば、コンストラクタ関数内ですべてのプロパティを定義してしまえば手っ取り早いです。グローバル変数として定義したくない
calcab
も関数内に突っ込んでしまえばしまいです。ただし、
abc
関数が呼び出される度にdefineProperty
の処理であったりcalcab
関数の実体化であったりが行われるため、何百・何千とインスタンスが作られるようなクラスでは避けた方が良いでしょう。また、コンストラクタ関数内がしっちゃかめっちゃかになってしまうのもいまいちだと思います。
私は使った経験がないため解説できませんが、Require.jsなどの依存関係を解決するためのライブラリについて調べてみると解決できるかもしれません。
2013/12/28 13:17:19ありがとうございます。
2013/12/29 18:25:57思い通りの処理が実現できました。