【问题标题】:Escape JavaScript event scope转义 JavaScript 事件范围
【发布时间】:2012-01-24 14:01:22
【问题描述】:

我不熟悉 JavaScript 作用域的方式,所以这是我的问题:

"use strict";

function Explorer(xPos, yPos) {

    // Inheritance
    GraphicsEntity.call(this, {
        x: xPos,
        y: yPos,
        width: 32,
        height: 32
    });

    // Local fields
    var _texture = new Image();

    // Contruct
    _texture.addEventListener("load", function() {

        this.graphics.drawImage(_texture, 0, 0);

    }, false);

    _texture.src = "explorer.png";

}

这会抛出异常:

未捕获的类型错误:无法调用未定义的方法“drawImage”

我知道这是由于 JavaScript 作用域的方式,因为“this”现在指的是“_texture”字段。 如您所见,Explorer(某种类似玩家的对象)继承自 GraphicsObject,而 GraphicsObject 又具有属性 graphics(画布元素的上下文)。

我找到了解决这个问题的方法,但在我看来它有点脏:

"use strict";

function Explorer(xPos, yPos) {

    // Inheritance
    GraphicsEntity.call(this, {
        x: xPos,
        y: yPos,
        width: 32,
        height: 32
    });

    // Local fields
    var _texture = new Image();
    var _graphics = this.graphics;

    // Contruct
    _texture.addEventListener("load", function() {

        _graphics.drawImage(_texture, 0, 0);

    }, false);

    _texture.src = "explorer.png";

}

所以现在一切都按预期工作了,图像被整齐地绘制到画布元素上。 唯一的问题是我现在有了另一个我并不真正想要的“图形”的引用。

所以我的问题是: 有没有办法让“this”引用 Explorer 对象,或者强制改变范围?

【问题讨论】:

    标签: javascript html scope html5-canvas dom-events


    【解决方案1】:

    你也可以使用 bind 将上下文绑定到函数作用域

    “使用严格”;

    函数资源管理器(xPos, yPos) {

    // Inheritance
    GraphicsEntity.call(this, {
        x: xPos,
        y: yPos,
        width: 32,
        height: 32
    });
    
    // Local fields
    var _texture = new Image();
    
    // Contruct
    _texture.addEventListener("load", function() {
    
        this.graphics.drawImage(_texture, 0, 0);
    
    }.bind(this), false);
    
    _texture.src = "explorer.png";
    

    }

    【讨论】:

      【解决方案2】:

      你还没有显示函数GraphicsEntity,但我很确定问题是这样的:

      GraphicsEntity.call(this, {
      

      在您的函数Explorer 中,this 将是全局对象(好吧,除非还使用call 调用了Explorer),这就是对call 的调用将this 设置为GraphicsEntity。这可能就是您收到该异常的原因。

      另外,GraphicsEntity 是一个对象,还是一个函数?我问是因为call 期待一个函数call 调用给定的函数,将第一个参数设置为this 值,随后的参数作为该函数的参数传递。

      编辑

      正如 Esailija 已经说过的,this 值在您的加载事件回调中与函数开头的值不同。将 this 的值保存到函数开头的局部变量中,并在回调中使用 that

      【讨论】:

      • Explorer 像普通对象一样使用,这似乎工作正常。问题在于 Image.load 事件的范围。
      【解决方案3】:

      一种可能的解决方案是使用包装函数,该函数将创建您需要的上下文并捕获您的状态。

      如:

      function draw(me) {
          me.graphics.drawImage(_texture, 0, 0);
      }
      
      function wrapper(me) {
          return function () { draw(me) };
      }
      
      ...
      _texture.addEventListener('load', wrapper(this));
      ...
      

      它是否比您的解决方案更简洁?这值得商榷,但您可以使其足够通用,以便在其他情况下重复使用。

      【讨论】:

      • 这似乎可行,因此它将上下文保留在字段中并返回,以便以后使用。聪明 :) 我将如何使这个“足够通用”?很好的解决方案,非常感谢您的帮助。
      • 通过原型继承,您可以使所有感兴趣的对象都定义为wrapper()。您还可以添加第二个参数作为函数本身(将包装方法与特定方法实现分开,如wrapper(me, f) { return function () { f(me); }; })。这是否比使用 self 方法更好值得商榷,因为它显然更慢但也更灵活(您可能在包装方法中有额外的设置,您可能想要分享)。
      • 谢谢你的解释,也许我以后会用这个方法:)
      • @JonKoops 当然。最佳解决方案始终是最适合您的特定用例的解决方案。很高兴能提供帮助。
      【解决方案4】:

      this 始终是函数的本地函数,每次调用函数时都会根据函数的调用方式对其进行评估。在这种情况下,浏览器调用函数并将this 设置为_texture 指向的图像对象。

      由于普通变量是从外部范围查找的,您可以简单地将旧的this 保存到某个变量中:

      function Explorer(xPos, yPos) {
          var self = this;
      
          // Inheritance
          GraphicsEntity.call(this, {
              x: xPos,
              y: yPos,
              width: 32,
              height: 32
          });
      
          // Local fields
          var _texture = new Image();
          // Contruct
      
          _texture.addEventListener("load", function () {
              self.graphics.drawImage(_texture, 0, 0);
          }, false);
      
          _texture.src = "explorer.png";
      }
      

      【讨论】:

      • 所以基本上与我修复它的方式相同,但使用对整个对象本身的引用?嗯...这可以工作。
      • @JonKoops 最好只使用整个对象,它只是一个引用,所以它不会占用更少或更多的内存(内存中仍然只有一个对象的表示,即使有多个东西指向它)。这样,当您在回调中需要 explorer.graphics 以外的其他内容时,您无需重写。
      • @Esailija 好的,我只是担心会严重破坏内存。我会使用这种方法,非常感谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      • 2018-02-14
      • 2013-03-11
      相关资源
      最近更新 更多