0

特定のキー押下をコントローラーのスコープで指定された関数にバインドするディレクティブを作成しようとしていますが、すべてのコールバック関数が、バインディングを含むオブジェクトの最後のコールバック関数によってオーバーライドされているようです。イベントを同じ結果にバインドするために、keymaster.jsとmousetrap.jsを使用してみました。

JSFiddle のコード

Javascript コード:

angular.module('app', ['directives', 'controllers']);

angular.module('directives', [])
.directive('keypress', [function () {
    return function (scope, element, attrs) {
        // console.log(scope, element, attrs);
        var attribute = scope.$eval(attrs.keypress || '{}');
        for (var k in attribute) {
            console.log('binding ' + k + ' as ' + attribute[k]);
            Mousetrap.bind(k, function() { return attribute[k](scope, element); });
        }
    };
}]);

angular.module('controllers', [])
.controller('TodoController', function($scope) {
    $scope.shortcuts = {
        'w': function () { console.log('w'); },
        's': function () { console.log('s'); },
        'a': function () { console.log('a'); },
        'd': function () { console.log('d'); }
    };
});

HTML ファイル:

<html>
  <head>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.js"></script>
    <script src="https://raw.github.com/ccampbell/mousetrap/master/mousetrap.min.js"></script>
    <script src="/javascript/app.js"></script>
  </head>
  <body>
    <div ng-app="app">
      <div ng-controller='TodoController' keypress='shortcuts'>Foo</div>
    </div>
  </body>
</html>

「w」、「a」、「s」、または「d」のどれを押しても、常に「d」がコンソールに書き込まれるのはなぜですか?

4

1 に答える 1

6

あなたは一般的な罠に陥っています:JavaScriptの変数は常に関数スコープです。これを行うとき:

for (var k in attribute) {
    console.log('binding ' + k + ' as ' + attribute[k]);
    Mousetrap.bind(k, function() { return attribute[k](scope, element); });
}

これによりbind()、すべて変数を閉じる4つのクロージャーを作成しますkが、それらはすべて同じ変数を閉じます。ループの実行ごとに新しいものを取得することはありません。のがすぐに使用されるconsole.logため、完全に機能します。クロージャは実際に実行されるまで評価されず、その時点で、その値はループが終了したときの値に変更されています。kk

ターゲットオーディエンスによっては、これを修正する最も簡単な方法は、のlet代わりにを使用することですvarletブロックスコープ(これはあなたが期待する方法で機能します)をブロックしますが、かなり最近の発明であり、それがどれだけうまくサポートされているかはわかりません。

それ以外の場合、新しいスコープを取得するには、新しい関数が必要です。

for (var k in attribute) {
    (function(k) {
        console.log('binding ' + k + ' as ' + attribute[k]);
        Mousetrap.bind(k, function() { return attribute[k](scope, element); });
    })(k);
}

これにより、外部kが関数の内部に渡されkます。これは、毎回異なる変数になります。これを小さなファクトリ関数に分割することもできますが、これほど小さなものについては、気にしません。

于 2013-01-15T03:14:47.347 に答える