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

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

新規登録して質問してみよう
ただいま回答率
85.51%
デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

JavaScript

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Q&A

6回答

4458閲覧

javascriptのプログラム構造について

退会済みユーザー

退会済みユーザー

総合スコア0

デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

JavaScript

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

1グッド

7クリップ

投稿2016/01/16 07:50

以下のjavascript関数について良いパターンを教えてください。

関数では、ユーザ操作等のイベントに応じて、インスタンス内のメソッドでdataを更新し、表示を切り替えようとしています。
data及び表示の管理をインスタンスのみにさせ、ユーザ操作等のイベントをインスタンスに対して伝える仕様にできれば綺麗かと考えているのですが、
実際には下記はthisの仕様によりエラーとなります。
(ユーザ操作等のイベントの場合イベント発生元がthisになるので)

単にエラーを回避して動かすことはできるのですが、
出来るだけ良いプログラム構造としたいです。

※クラス・インスタンスは複数種類存在します(SampleClassA、SampleClassB…、sampleA1、sampleA2、sampleB1...)

javascript

1//呼び出し側の記載/////////// 2var sampleA1 = new SampleClassA(); 3//初期イベント 4sampleA1.initialEvent(); 5//windowサイズ変更時のイベント等 6$(window).on('resize', sampleA1.resizeEvent) 7 8 9//SampleClassA/////////// 10function SampleClassA() { 11 this.data; 12 this.object; 13} 14SampleClass.prototype = { 15 setDefaultData: function() { 16 this.data = { 17 pointA: 10, 18 pointB: 10, 19 pointC: 10, 20 pointD: 10, 21 }; 22 return true; 23 }, 24 doDisplay: function() { 25 this.object = new SampleLibrary.display(this.data); 26 return true; 27 }, 28 initialEvent: function() { 29 this.setDefaultData; 30 this.doDisplay; 31 return true; 32 }, 33 updateResizeData: function() { 34 this.data.pointA = this.data.pointA++; 35 this.data.pointC = this.data.pointC++; 36 return true; 37 }, 38 resizeEvent: function() { 39 this.updateResizeData; //エラー!!! 40 this.doDisplay; 41 return true; 42 }, 43}
miyabi-sun👍を押しています

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

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

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

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

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

guest

回答6

0

前提

おそらく、ご掲示のコードはテストしてないのではないでしょうか。
sampleA1.initialEvent() の行で TypeError になります。

JavaScript

1sampleA1.initialEvent(); // TypeError: sampleA1.initialEvent is not a function

また、様々な部分で関数呼び出しされていないため、正しく実行されません。
以上のコードを修正したとして下記コードになると思われます。
(地味に手間なのでサンプルでも動く事を確認してから掲載するようにしていただけると助かります)

JavaScript

1'use strict'; 2function SampleClassA () { ; } 3 4SampleClassA.prototype = { // (修正) SampleClassAに修正 5 setDefaultData: function setDefaultData () { 6 this.data = { 7 pointA: 10, 8 pointB: 10, 9 pointC: 10, 10 pointD: 10 11 }; 12 }, 13 doDisplay: function doDisplay (/* [window] */) { 14// this.object = new SampleLibrary.display(this.data); // (修正) ReferenceError の為、コメントアウト 15 16 if (arguments.length > 0) { 17 var window = arguments[0]; 18 console.log(window.innerWidth, window.innerHeight); 19 } 20 console.log(this.data); 21 }, 22 initialEvent: function initialEvent () { 23 this.setDefaultData(); // (修正) 関数呼び出しする 24 this.doDisplay(); // (修正) 関数呼び出しする 25 }, 26 updateResizeData: function updateResizeData () { 27 var data = this.data; 28 ++data.pointA; // (修正) インクリメントされていなかった不具合を修正 29 ++data.pointC; // (修正) インクリメントされていなかった不具合を修正 30 }, 31 resizeEvent: function resizeEvent (event) { 32 console.log(event); 33 this.updateResizeData(); // TypeError: this.updateResizeData is not a function (修正) 関数呼び出しする 34 this.doDisplay(event.target); // (修正) 関数呼び出しする 35 } 36}; 37 38SampleClassA.prototype.initialEvent = function initialEvent () { 39 this.resizeEvent = this.resizeEvent.bind(this); 40 this.setDefaultData(); // (修正) 関数呼び出しする 41 this.doDisplay(); // (修正) 関数呼び出しする 42} 43 44// (修正) 呼び出しコードを後ろに持ってくる 45var sampleA1 = new SampleClassA(); 46//初期イベント 47sampleA1.initialEvent(); 48//windowサイズ変更時のイベント等 49jQuery(window).on('resize', sampleA1.resizeEvent);

解決法

(方法1) addEventListenerhandleEvent を使う

JavaScript

1SampleClassA.prototype.handleEvent = function handleEvent (event) { 2 this.updateResizeData(); 3 this.doDisplay(); 4 return true; 5}; 6 7var sampleA1 = new SampleClassA(); 8sampleA1.initialEvent(); 9addEventListener('resize', sampleA1, false);

(方法2) Function.prototype.bind を使う

一つは onresize のイベント定義時に bind する方法。

JavaScript

1jQuery(window).on('resize', sampleA1.resizeEvent.bind(sampleA1));

もう一つは initialEvent() 時に予め bind しておく方法(Lhankor_Mhy さんのアイデア)。
jsfiddleにサンプルをUPしています。

JavaScript

1SampleClassA.prototype.initialEvent = function initialEvent () { 2 this.resizeEvent = this.resizeEvent.bind(this); // 予め bind しておく 3 this.setDefaultData(); 4 this.doDisplay(); 5} 6 7var sampleA1 = new SampleClassA(); 8sampleA1.initialEvent(); 9jQuery(window).on('resize', sampleA1.resizeEvent); // bind 済みの resizeEvent を指定する 10 11/** 12 * ただし、sampleA1.resizeEvent は bind 済みの為、後から Function.prototype.call で this 値を変更できない(意図的に汎用的ではない) 13 * this 値を変更するためには SampleClassA.prototype.resizeEvent を経由する必要がある 14 */ 15console.log(sampleA1.resizeEvent !== SampleClassA.prototype.resizeEvent); // true 16sampleA1.resizeEvent.call(null, {target:{innerWidth: 777, innerHeight: 777}}); // Function.prototype.bind で束縛された this 値は書き換え不可能な為、TypeError にならない 17SampleClassA.prototype.resizeEvent.call({updateResizeData: Function(), doDisplay: console.log.bind(console)}, {target:{innerWidth: 777, innerHeight: 777}}); // bind されていない為、this 値を変更できる

(方法3) event.data (jQuery API) を使う

JavaScript

1SampleClassA.prototype.resizeEvent = function resizeEvent (event) { 2 var thisArg = event.data.thisArg; 3 thisArg.updateResizeData(); 4 thisArg.doDisplay(); 5 return true; 6} 7 8var sampleA1 = new SampleClassA(); 9sampleA1.initialEvent(); 10jQuery(window).on('resize', {thisArg: sampleA1}, sampleA1.resizeEvent);

結論

jQuery に拘りがないのなら addEventListenerhandleEvent を使う方法が最もスマートだと思います。

更新履歴

(2016/1/19 23:28追記)
Lhankor_Mhy さんのアイデアをお借りして initialEvent() 時に予め bind しておくコードを追加しました。

Re: ゲストユーザーさん

投稿2016/01/16 11:42

編集2016/01/19 14:53
think49

総合スコア18156

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

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

0

私だったらですので、あまり参考にならないかも知れません。

###方法1 CoffeeScriptで書く
altJSの一つであるCoffeeScriptならSampleClassAをこんな風に書けます。

CoffeeScript

1class SampleClassA 2 constructor: -> 3 @data = null 4 @object = null 5 setDefaultData: -> 6 @data = 7 pointA: 10 8 pointB: 10 9 pointC: 10 10 pointD: 10 11 true 12 # 中略 13 resizeEvent: => 14 @updateResizeData() 15 @doDisplay() 16 true

CoffeeScriptでは通常のfuncitonは->ですが、それとは別にthisをコードがある場所のthisに強制的にbindしたfunctionにする=>があります。これを使えばthis違いの問題はおきません。また、CoffeeScriptならclass構文でわかりやすくクラスを書くことが出来るというのも利点の一つです。=>はECMAScript 2015にもありますが、class構文で上のように書く方法は無かったはずです。

###方法2 react.jsやangular.jsを使う
クラスが必要と言うことはそれなりに複雑なイベントと描画を繰り返す処理になっていると思います。素のJavaScriptで頑張ろうとしても、やがて破綻するのが目に見えています(少なくとも、私だったら破綻します)。それなら、最初からフレームワークに頼って、その流儀にあったイベント管理方法を取り入れた方が早いと思っています。

投稿2016/01/16 09:14

raccy

総合スコア21733

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

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

0

SampleClassAに直接、pointADのデータの保有や値の操作を担当させるのではなく、pointADに関する処理は別のモデルクラスに行わせるのは、いかがでしょうか?

SampleLibraryというのが描画を担当しているクラスなのですよね?
であれば、SampleClassはMVCのコントローラーになっているのだと思うので、
モデルクラスを設けて、SampleClassにビューとモデルの橋渡しをさせるのでいかがでしょう?

投稿2016/01/18 13:29

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

こういうイメージでしょうか。

javascript

1//クラス設定/////////// 2function SampleClassA() { 3 this.instanceOwnProperty; 4 this._init(); 5} 6function SampleClassB() { 7 this.instanceOwnProperty; 8 this._init(); 9} 10function SampleClass(){} 11 12SampleClass.prototype = { 13 _init: function(){ 14 this.data; 15 this.object; 16 var _self = this; 17 this.resizeEvent = function(){return _self._resizeEvent(_self)}; 18 }, 19 20 // ... 21 22 _resizeEvent: function(_self) { 23 _self.updateResizeData(); 24 _self.doDisplay(); 25 return true; 26 }, 27} 28SampleClassA.prototype = new SampleClass(); 29SampleClassA.prototype.ClassAProperty; 30SampleClassB.prototype = new SampleClass(); 31SampleClassB.prototype.ClassBProperty; 32 33//インスタンス生成/////////// 34var sampleA1 = new SampleClassA(); 35var sampleA2 = new SampleClassA(); 36var sampleB1 = new SampleClassB();

_selfを使わずにbindでいいような気もしますが、メモリ的にはどっちがお得なんでしょう?

投稿2016/01/18 02:36

編集2016/01/18 10:52
Lhankor_Mhy

総合スコア35811

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

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

think49

2016/01/19 14:58

_init() 時に変数束縛する発想は面白いと思います。 _resizeEvent, resizeEvent が並んでいますが、prototype-chain を利用して上書きしたらすっきり書ける気がしました。 Function#bind https://jsfiddle.net/066ko0qg/ 関数式 https://jsfiddle.net/066ko0qg/1/ Function() https://jsfiddle.net/066ko0qg/2/ どれがいいかと問われれば、「Function#bind > Function() > 関数式」だと個人的には思います(変数のスコープの狭さ的な意味で)。
Lhankor_Mhy

2016/01/20 00:27

なるほど、複雑に考えずに上書き(というか自前のプロパティを持つというか)してしまえばいいんですね。たしかに、そちらの方がコードの見通しがいいですね。 とても勉強になります、ありがとうございます。
guest

0

内容と違ってたらごめんなさい。
私の場合は、実態が一つしか存在しない場合、クラスは使いません。

例えば、サンプルクラスでしたら、

Javascript

1var Sample = {}; 2 3Sample.ClassA = { 4 5 Listener : { 6 resize : function ( evt ) { 7 var self = Sample.ClassA; 8 9 self.updateResizeData(); 10 } 11 } 12 13jQuery( window ).bind( 'resize', Sample.ClassA.Listener.resize );

のようにします。クラスというより、構造体、関数の集合体というとらえ方です。
Sample.ClassA と毎回書かなければならないのはわずらわしいですが、this に縛られることもありません。

どうしても、実体が必要な場合でしたら、デリゲータを利用してもいいかもしれません。

Javascript

1function delegater ( obj, func ) { 2 return function () { func.apply( obj, arguments ); } 3} 4 5var sampleA1 = new SampleClassA(); 6//初期イベント 7sampleA1.initialEvent(); 8 9//windowサイズ変更時のイベント等 10jQuery( window ).on( 'resize', delegater( sampleA1, sampleA1.Listener.resize ) );

それか、包括してしまうか

Javascript

1var Sample = (function ( listener ){ 2 return function ( value ) { 3 this.value = value; 4 this.Listener = listener( this ); 5 }; 6})( function ( self ) { 7 return { 8 resize : function ( evt ) { 9 console.log( self.value ); 10 } 11 }; 12} ); 13 14var s1 = new Sample( 'XXX' ); 15var s2 = new Sample( 'YYY' ); 16 17jQuery( window ).bind( 'resize', s1.Listener.resize ); 18jQuery( window ).unbind( 'resize', s1.Listener.resize ); 19jQuery( window ).bind( 'resize', s2.Listener.resize );

投稿2016/01/17 09:26

編集2016/01/17 09:28
yuux01

総合スコア34

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

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

0

すでに coffeescript で書く例が投稿されていますが、
私も coffeescript で書いてみることを提案したいです。

以下のページで動作を試せます。
http://jsdo.it/katoy/GWhY

resize イベントがなぜか拾えなかったので、 clock イベントで doDispley() が呼ばれるようにしています。

coffeescript

1class SampleClass 2 @data: null 3 # @object: null 4 5 setDefaultData: -> 6 @data = 7 pointA: 10 8 pointB: 10 9 pointC: 10 10 pointD: 10 11 true 12 13 initialEvent: -> 14 @setDefaultData() 15 @doDisplay() 16 true 17 18 resizeEvent: -> 19 @updateResizeData() 20 @doDisplay() 21 true 22 23 doDisplay: -> 24 # @object = new (SampleLibrary.display)(@data) 25 alert("data=[" + @data.pointA + ", " + @data.pointB + ", " + @data.pointC + ", " + @data.pointD) 26 true 27 28 updateResizeData: -> 29 @data.pointA++ 30 @data.pointC++ 31 true 32 33$ -> 34 sample01 = new SampleClass() 35 sample01.initialEvent() 36 37 $("#str").click -> 38 sample01.resizeEvent() 39 false

coffeescript のコードは javascript に変換できます。
javascript に変換すると次のようになります。

javascript

1var SampleClass; 2 3SampleClass = (function() { 4 function SampleClass() {} 5 6 SampleClass.data = null; 7 8 SampleClass.prototype.setDefaultData = function() { 9 this.data = { 10 pointA: 10, 11 pointB: 10, 12 pointC: 10, 13 pointD: 10 14 }; 15 return true; 16 }; 17 18 SampleClass.prototype.initialEvent = function() { 19 this.setDefaultData(); 20 this.doDisplay(); 21 return true; 22 }; 23 24 SampleClass.prototype.resizeEvent = function() { 25 this.updateResizeData(); 26 this.doDisplay(); 27 return true; 28 }; 29 30 SampleClass.prototype.doDisplay = function() { 31 alert("data=[" + this.data.pointA + ", " + this.data.pointB + ", " + this.data.pointC + ", " + this.data.pointD); 32 return true; 33 }; 34 35 SampleClass.prototype.updateResizeData = function() { 36 this.data.pointA++; 37 this.data.pointC++; 38 return true; 39 }; 40 41 return SampleClass; 42 43})(); 44 45$(function() { 46 var sample01; 47 sample01 = new SampleClass(); 48 return sample01.initialEvent(); 49}); 50 51$(window).click(function() { 52 alert("click"); 53 return true; 54});

備考:
coffeesctpt コードの作成は、実は質問文にある javascript コードを js2coffee というツールを
つかって、coffeescript に変換した結果をベースにして編集をしました。

投稿2016/01/16 11:48

katoy

総合スコア22322

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問