23

私は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;

それは機能しますが、それがそれを修正する良い方法かどうかはわかりません。

私はこの質問を見つけました。これはかなり似ているようですが、私の例にどのように当てはめることができるかわかりません。

ご協力いただきありがとうございます!

4

3 に答える 3

50

いくつかのこと:

  • var self = thisほとんどの人は、速くて簡単なので、のようなものを提案します。

  • ただし、ビューオブジェクトをビューロジックから完全にvar self = this分離するわけではありません。ビューロジックは、より正式なC#の背景から来ており、コードを見ると、やりたいことのように聞こえます。

  • イベントが発生したときにのみコールバックを実行するには、ハンドラーを関数でラップして、すぐに評価されるようにしますが、keydownイベントが発生したときにのみ実行されます(以下のコードを参照)。

  • JSのスコープを理解する:実行コンテキストが何であれ、現在のスコープでもあります。リスナーはのメソッド(と呼ばれるlisten)に追加されましたKeyboard.prototypeが、keydownイベントは実際にはwindowで発生します。ハンドラーは、定義された場所とは異なるコンテキストで実行されています。呼び出し元のコンテキスト内で実行されているため、この場合は、定義されたときに、または定義されたときに別のオブジェクトにバインドしない限り、windowスコープが適用されます。windowbindapply

コードで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()ECMAScript5+と互換性があります。古いブラウザのソリューションが必要な場合は、Mozillaがandを.bind()使用する代わりの優れた方法を投稿しています。functions.call()

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind

編集:keyboardこの新しいモジュラーソリューションを使用すると、インスタンス化されたオブジェクトは次のようになります。ここに画像の説明を入力してください

于 2013-03-24T03:32:34.290 に答える
7
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
    });
}

このコードの仕組み:

  1. 変数への参照を格納する変数selfを作成していthisます。
  2. 内部関数はクロージャであるため、自己への参照があります。
  3. クロージャ関数が呼び出されたとき:thisdomオブジェクトをself指し、keyboardオブジェクトを指します。
  4. クロージャーはevent、キーボードオブジェクトのメンバー関数に渡すパラメーターとして呼び出されます。
于 2012-12-21T19:48:25.227 に答える
3

どうですか

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();
于 2012-12-21T19:56:24.127 に答える