私はJavaScriptを試すC#開発者であり、スコープを理解しようとしています:)
オブジェクトのフィールドを使用するaddEventListener
を含む次のコードがあります。
(function(window) {
function Keyboard() {
this.keys = {};
}
Keyboard.prototype.handle_keydown = function(args) {
this.keys[args.keyCode] = true;
}
Keyboard.prototype.listen = function() {
window.addEventListener('keydown', this.handle_keydown);
}
app.util.keyboard = new Keyboard();
})(window);
ハンドラーでkeys配列を使用したいのですが、これはそのコンテキストのウィンドウであるため、これを使用するとアクセスできないことを理解しています(正しいですか?)。に変更した場合
app.util.keyboard.keys[args.keyCode] = true;
それは機能しますが、それがそれを修正する良い方法であるかどうかはわかりません。
私は この質問 を見つけましたが、これはかなり似ているようですが、どのようにして自分の例に当てはめることができるかわかりません。
ご協力いただきありがとうございます!
いくつかのこと:
高速で簡単なので、ほとんどの人は_var self = this
_のようなものを提案します。
しかし、_var self = this
_は、ビューオブジェクトをビューロジックから完全に分離しません。コードを見ると、やりたいことのように聞こえます。
イベントが発生したときにのみコールバックを実行するには、ハンドラーを関数にラップして、すぐに評価されるようにしますが、keydown
イベントが発生したときにのみ実行されます(以下のコードを参照)。
JSのスコープの理解:実行コンテキストが何であれ、現在のスコープでもあります。リスナーは_Keyboard.prototype
_のメソッド(listen
と呼ばれます)に追加されましたが、keydown
イベントは実際にはwindow
で発生します-ハンドラーは定義された場所とは異なるコンテキストで実行されています。呼び出し元のコンテキスト(この場合はwindow
)内で実行されているため、定義時にwindow
またはbind
を介して別のオブジェクトにバインドしない限り、スコープはapply
になります。
コードでは、window
はユーザーが操作するビューであり、Keyboard
はそのビューのコントローラーです。 C#/。NETでおそらく慣れているようなMVCパターンでは、何かが発生したときにビューは何をすべきかを自分自身に伝えません。コントローラーはビューに何をすべきかを伝えます。したがって、多くの場合のように_var self = this
_を使用してコントローラへの参照を割り当てる場合、ビューはそれ自体を管理しますが、keydown
イベントの特定のハンドラに対してのみです。これは一貫性がなく、大規模なプロジェクトでは管理が難しくなります。
解決策:
_Keyboard.prototype.listen = function() {
window.addEventListener('keydown', function(e) {
this.handle_keydown(e);
}.bind(this), false);
}
_
より良い解決策:
_Keyboard.prototype.view = window;
Keyboard.prototype.listen = function() {
this.view.addEventListener('keydown', function(e) {
this.handle_keydown(e);
}.bind(this), false);
}
_
最良の解決策(ES6 class
の準備ができるまで):
_// define
function addViewController(view) {
function ViewController() {
this.handle_keydown = function(args) {
// handle keydown events
};
this.listen = function() {
this.view.addEventListener('keydown', function(e) {
this.handle_keydown(e);
}.bind(this), false);
};
this.view = view;
return this;
}
return new ViewController(view);
}
// implement
var keyboard = addViewController(window);
keyboard.listen();
_
.bind()
はECMAScript 5+と互換性があります。古いブラウザのソリューションが必要な場合、Mozillaはfunctions
と.bind()
を使用した.call()
の優れた代替案を投稿しています。https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
編集:インスタンス化されたkeyboard
オブジェクトは、この新しいモジュラーソリューションを使用して次のようになります。
Keyboard.prototype.listen = function() {
var self = this;
window.addEventListener('keydown', function(event) {
self.handle_keydown(event);
// self is your Keyboard object. You can refer to all your properties from this
});
}
このコードの仕組み:
this
変数への参照を格納する変数selfを作成しています。this
はdomオブジェクトを指し、self
はキーボードオブジェクトを指します。event
を指定して呼び出されます。いかがですか
function Keyboard() {
this.keys = {};
var self = this;
this.handle_keydown = function(args) {
self.keys[args.keyCode] = true;
}
this.listen = function() {
window.addEventListener('keydown', this.handle_keydown);
}
}
app.util.keyboard = new Keyboard();