23

我是一名 C# 开发人员,正在尝试使用 JavaScript,我正在尝试了解范围 :)

我有以下代码,其中包含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 中的作用域:无论执行上下文是什么,也是当前作用域。您的侦听器已添加到 on 的方法(称为listen)中Keyboard.prototype,但keydown实际上触发了该事件window-处理程序在与其定义位置不同的上下文中执行;它在调用它的上下文中执行,在这种情况下,window它的范围是 ,除非您通过或在定义它时window将它绑定到另一个对象。bindapply

在您的代码中,window是用户与之交互的视图,并且Keyboard是该视图的控制器。在 MVC 模式中,就像您在 C#/.NET 中可能习惯的那样,视图不会告诉自己当事情发生时要做什么,控制器会告诉视图要做什么。因此,如果您要像许多人那样使用分配对控制器的引用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);
}

最好的解决方案(直到 ES6class准备好):

// 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 已经发布了一个很好的替代方法来.bind()使用functionsand .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. 内部函数是一个闭包,因此它引用了 self。
  3. 调用闭包函数时:this指向dom对象,而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 回答