Javascript 范围 addEventListener 和 this

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13996263/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 15:22:16  来源:igfitidea点击:

Javascript scope addEventListener and this

javascriptscopethisaddeventlistener

提问by Raf

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

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

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

我有以下代码,其中包含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);

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

我想在我的hander 中使用keys 数组,但我知道我无法访问它,因为这是该上下文中的窗口(对吗?)。如果我把它改成

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.

我发现了这个问题,它看起来很相似,但我不确定如何将它放入我的示例中。

Thanks for your help!

谢谢你的帮助!

回答by Benny

A few things:

一些东西:

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

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

  • 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 keydownevent fires (see the code below).

  • 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 keydownevent 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 windowunless you bind it to another object via bindor applywhen it's defined.

  • 大多数人会建议类似的东西,var self = this因为它既快速又简单。

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

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

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

In your code, windowis the view a user's interacting with, and Keyboardis 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 = thislike so many do, the view would be managing itself -- but only for that specific handler for keydownevents. This is inconsistent and would become hard to manage in a large project.

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

A solution:

一个办法:

Keyboard.prototype.listen = function() {
    window.addEventListener('keydown', function(e) {
        this.handle_keydown(e);
    }.bind(this), false);
}

A better solution:

更好的解决方案:

Keyboard.prototype.view = window;

Keyboard.prototype.listen = function() {
    this.view.addEventListener('keydown', function(e) {
        this.handle_keydown(e);
    }.bind(this), false);
}

The best solution (until ES6 classis ready):

最佳解决方案(直到 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();
  • Note: .bind()is compatible with ECMAScript 5+; if you need a solution for older browsers, Mozilla has posted a great alternative to .bind()using functionsand .call():
  • 注意:.bind()兼容 ECMAScript 5+;如果你需要对旧版浏览器的解决方案,Mozilla已经发布到一个伟大的选择,.bind()使用functions.call()

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

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

Edit:Here's what your instantiated keyboardobject will look like using this new, modular solution: enter image description here

编辑:这是keyboard使用这个新的模块化解决方案实例化对象的样子:在此处输入图片说明

回答by closure

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
    });
}

How this code works:

此代码的工作原理:

  1. We are creating variable self, which stores reference to thisvariable.
  2. The inner function is a closure, hence it has reference to self.
  3. When the closure function is called: thispoints to the dom object, while selfpoints to keyboard object.
  4. The closure is called with eventas a parameter that we pass on to the member function of the keyboard object.
  1. 我们正在创建变量 self,它存储对this变量的引用。
  2. 内部函数是一个闭包,因此它引用了 self。
  3. 调用闭包函数时:this指向dom对象,whileself指向键盘对象。
  4. 闭包event作为参数被调用,我们传递给键盘对象的成员函数。

回答by Musa

How about

怎么样

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();