简体   繁体   English

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

[英]Why do all of the keybindings in this directive get overwritten by the last one?

I'm attempting to create a directive that binds specific keypresses to functions specified in the scope of a controller, but all of the callback functions seem to be overridden by the last callback function in the object containing the bindings. 我正在尝试创建一个指令,该指令将特定的按键绑定到控制器范围内指定的函数,但是所有回调函数似乎都被包含绑定的对象中的最后一个回调函数所覆盖。 I've tried using keymaster.js and mousetrap.js for binding the events with the same results. 我尝试使用keymaster.js和mousetrap.js绑定事件具有相同的结果。

Code in a JSFiddle JSFiddle中的代码

The Javascript code: 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'); }
    };
});

The HTML File: 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>

Why is 'd' always written to the console, regardless of whether I press 'w', 'a', 's', or 'd'? 为什么无论我按“ w”,“ a”,“ s”还是“ d”,总是将“ d”写入控制台?

You've fallen for a common trap: variables in JavaScript are always function scoped. 您已经陷入一个常见的陷阱:JavaScript中的变量始终是函数作用域的。 When you do this: 执行此操作时:

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

With that bind() , you're creating four closures that all close over the variable k —but they all close over the same variable . 使用该bind() ,您将创建四个都在变量k封闭的闭包-但它们都在同一个变量上封闭。 You don't get a new one for each run of the loop. 每次循环运行都不会得到新的。 The console.log works perfectly because the value of k is used immediately. console.log可以完美地工作,因为k可以立即使用。 The closure doesn't evaluate k until it's actually run, and by then, its value has changed to whatever it was when the loop finished. 闭包直到实际运行时才评估k ,到那时,其值已更改为循环结束时的值。

Depending on your target audience, the easiest way by far to fix this is to use let instead of var . 根据您的目标受众,到目前为止,解决此问题的最简单方法是使用let而不是var let does block scoping (which works about how you'd expect), but is a fairly recent invention, and I'm not sure how well it's supported. let确实阻止了作用域定义(这与您的预期工作有关),但这是一个相当新的发明,我不确定它的支持程度如何。

Otherwise, to get a new scope, you need a new function: 否则,要获得新的作用域,您需要一个新的功能:

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

This passes the outer k to the function's inner k , which will be a different variable every time. 这会将外部k传递给函数的内部k ,每次都会是一个不同的变量。 You could also split this out into a little factory function, but for something this tiny, I wouldn't bother. 您也可以将其拆分为一个小的工厂函数,但是对于这么小的东西,我不会打扰。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM