为什么这个指令中的所有键绑定都被最后一个覆盖?

ian*_*675 0 javascript angularjs

我正在尝试创建一个将特定按键绑定到控制器范围中指定的函数的指令,但所有回调函数似乎都被包含绑定的对象中的最后一个回调函数覆盖.我已经尝试使用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'); }
    };
});
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

为什么'd'总是被写入控制台,无论我是按'w','a','s'还是'd'?

Eev*_*vee 6

你已经陷入了一个共同的陷阱:JavaScript中的变量总是函数作用域.当你这样做:

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

有了这个bind(),你创建了四个关闭变量的闭包k- 但它们都关闭了同一个变量.每次循环运行都没有新的.该console.log作品完美,因为价值k立即使用.闭包在k实际运行之前不会进行评估,到那时,它的值已经变为循环结束时的值.

根据您的目标受众,到目前为止最简单的解决方法是使用let而不是var. let阻止范围界定(这与你期望的方式有关),但这是一个相当新的发明,我不确定它的支持程度如何.

否则,要获得新范围,您需要一个新功能:

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

这将外部传递k给函数的内部k,每次都是一个不同的变量.你也可以将它拆分成一个小工厂功能,但是对于这么小的东西,我不会打扰.