转义JavaScript事件范围

时间:2011-12-20 16:03:11

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

我对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";

}

这将抛出异常:Uncaught TypeError:无法调用未定义的方法'drawImage'

我知道这是由于JavaScript范围的方式,因为'this'现在指的是'_texture'字段。 正如您所看到的,Explorer(某种类似于播放器的对象)继承自GraphicsObject,而GraphicsObject又具有属性图形(canvas元素的上下文)。

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

"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对象,或强制范围改变?

4 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:2)

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

GraphicsEntity.call(this, {

在您的函数Explorer中,this将成为全局对象(除非Explorer也使用call调用),这就是对{{call的调用1}}将this设置为GraphicsEntity。这可能是你得到这个例外的原因。

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

修改

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

答案 2 :(得分:1)

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

如:

function draw(me) {
    me.graphics.drawImage(_texture, 0, 0);
}

function wrapper(me) {
    return function () { draw(me) };
}

...
_texture.addEventListener('load', wrapper(this));
...

它比你的解决方案更简洁吗?这是有争议的,但你可以使它足够通用,它可以在其他环境中重复使用。

答案 3 :(得分:1)

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

"使用严格&#34 ;;

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

}.bind(this), false);

_texture.src = "explorer.png";

}