Javascript范围addEventListener和this

时间:2012-12-21 19:45:27

标签: javascript scope this addeventlistener

我是一名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);

我想在我的hander中使用keys数组,但是理解我无法访问是通过使用它,因为这是该上下文中的窗口(正确吗?)。 如果我将其更改为

app.util.keyboard.keys[args.keyCode] = true;

它有效,但我不确定这是解决问题的好方法。

我找到了this question,这看起来很相似,但我不确定如何将它融入我的例子中。

感谢您的帮助!

3 个答案:

答案 0 :(得分:38)

一些事情:

  • 大多数人会建议像var self = this这样的东西,因为它快速而简单。

  • var self = this并未将视图对象视图逻辑完全分开,后者来自更正式的C#背景并查看您的代码,听起来像你想要做的事情。

  • 为了让回调只在事件触发时执行,将处理程序包装在一个函数中,以便立即对其进行评估,但仅在keydown事件触发时执行(参见代码如下)。

  • 了解JS中的范围:无论执行上下文是什么,也是当前范围。您的侦听器已添加到listen上的方法(名为Keyboard.prototype)中,但keydown事件实际上是在window上触发的 - 处理程序在不同的上下文中执行它被定义的地方;它正在调用它的上下文中执行,在本例中为window,因此除非您通过windowbind将其绑定到另一个对象,否则它的作用域为apply它被定义了。

在您的代码中,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);
}

最佳解决方案(直到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()与ECMAScript 5+兼容;如果您需要针对旧浏览器的解决方案,Mozilla已使用.bind()functions发布了.call()的绝佳替代方案:

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

编辑:以下是使用这种新的模块化解决方案的实例化keyboard对象:enter image description here

答案 1 :(得分:5)

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指向键盘对象。
  4. 使用event作为参数调用闭包,我们将其传递给键盘对象的成员函数。

答案 2 :(得分: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();