Javascript 范围 addEventListener 和 this [英] Javascript scope addEventListener and this

查看:19
本文介绍了Javascript 范围 addEventListener 和 this的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一名 C# 开发人员,正在试验 JavaScript,我正在努力了解范围 :)

I am a C# developer experimenting with JavaScript and I'm trying to get my head around the scope :)

我有以下代码,其中包含一个 addEventListener,我想在其中使用我对象中的一个字段:

I have the following code which contains an addEventListener in which I want to use a field from my object:

(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 数组,但我知道我无法访问它,因为这是该上下文中的窗口(对吗?).如果我把它改成

I would like to use the keys array in my hander, but understand that I cannot access is by using this, because this is the window in that context (correct?). If I change it to

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

它有效,但我不确定这是修复它的好方法.

it works, but I'm not sure that's a good way to fix it.

我发现了这个问题,看起来很相似,但我不是确定如何将其融入我的示例中.

I found this question, which seems rather similar, but Im not sure how I can fit it into my example.

感谢您的帮助!

推荐答案

一些事情:

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

  • Most people will suggest something like var self = this because it's fast and easy.

但是var self = this并没有将视图对象视图逻辑完全分开,后者来自更正式的C# 背景和查看您的代码,听起来像是您想做的事情.

But var self = this does not separate the view object entirely from the view logic, which coming from a more formal C# background and looking at your code, sounds like something you want to do.

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

In order to have the callback execute only when the event fires, wrap the handler in a function, so that it's evaluated right away, but only executed when and if a keydown event fires (see the code below).

理解JS中的作用域:无论执行上下文是什么,也是当前作用域.您的侦听器是在 Keyboard.prototype 上的一个方法(称为 listen)中添加的,但 keydown 事件实际上是在 window<上触发的/code> -- 处理程序在与定义不同的上下文中执行;它在调用它的上下文中执行,在本例中为 window,所以它的范围是 window,除非你通过 bindapply 在定义时.

Understanding scope in JS: Whatever the execution context is, is also the current scope. Your listener was added in a method (called listen) on Keyboard.prototype, but the keydown event is actually fired on window -- the handler is executing in a different context than where it was defined; it's executing within the context of what is invoking it, in this case, window, so it's scoped to window unless you bind it to another object via bind or apply when it's defined.

在您的代码中,window 是用户与之交互的视图,而 Keyboard 是该视图的控制器.在 MVC 模式中,就像您在 C#/.NET 中可能习惯的那样,当事情发生时,视图不会告诉自己该做什么,控制器会告诉视图该做什么.因此,如果您像许多人一样使用 var self = this 为控制器分配一个引用,则视图将自行管理——但仅适用于 keydown<的特定处理程序/code> 事件.这是不一致的,并且在大型项目中会变得难以管理.

In your code, window is the view a user's interacting with, and Keyboard is that view's controller. In MVC patterns like what you're probably used to in C#/.NET, views don't tell themselves what to do when things happen, controllers tell views what to do. So, if you were to assign a reference to the controller by using var self = this like so many do, the view would be managing itself -- but only for that specific handler for keydown events. This is inconsistent and would become hard to manage in a large project.

解决方案:

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 准备就绪):

The best solution (until ES6 class is ready):

// 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 已经发布了一个使用 functions.call() 来替代 .bind() 的好方法:
    • Note: .bind() is compatible with ECMAScript 5+; if you need a solution for older browsers, Mozilla has posted a great alternative to .bind() using functions and .call():
    • https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind

      这是使用这个新的模块化解决方案实例化的 keyboard 对象的样子:

      Here's what your instantiated keyboard object will look like using this new, modular solution:

      这篇关于Javascript 范围 addEventListener 和 this的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆