2011-12-20 65 views
1

我是一种新的方式JavaScript的范围,所以这是我的问题:逃生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(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对象,或者强制范围改变?

回答

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

+1 - 就是这样 – 2011-12-20 16:19:09

+0

所以基本上和我修正它的方式一样,但是使用对整个Object本身的引用?嗯......这可以工作。 – 2011-12-20 16:42:01

+0

@JonKoops最好只使用整个对象,它只是一个引用,所以它不会占用更少或更多的内存(即使多个事物指向内存,对象在内存中仍然只有一种表示形式)。这样,当你在回调中需要比'explorer.graphics'更多的东西时,你不需要重写。 – Esailija 2011-12-20 16:48:31

2

您还没有显示的功能GraphicsEntity,但我敢肯定,这个问题是这样的:

GraphicsEntity.call(this, { 

在你的函数Explorerthis将是全局对象(当然,除非Explorer也被称为与call),这是call的呼叫设置thisGraphicsEntity。这很可能就是你得到这个例外的原因。

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

编辑

正如Esailija已经说过,this价值是它在你的函数的开头是什么在你的负载情况下的回调不同。将this的值保存到函数开头的局部变量中,并在回调中使用

+0

'Explorer'使用像一个正常的对象,并且该似乎很好地工作。问题是Image.load事件中的范围。 – 2011-12-20 16:29:53

1

一个可能的解决方案是使用一个包装的功能,将创建你所需要的情况和捕获您的国家。

如:

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

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

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

它比你的解决方案更简洁一些?这是有争议的,但你可以使它足够通用,可以在其他情况下重用。

+0

这似乎工作,所以它保留在一个字段中的上下文,并返回它,以便以后可以使用。智能:) 我该如何去做这个'通用'? 不错的解决方案,我真的很感谢你的帮助。 – 2011-12-20 16:34:23

+0

通过原型继承,您可以使所有感兴趣的对象具有'wrapper()'定义。你也可以添加第二个参数作为函数本身(将wrapper方法从特定的方法实现中分离出来,就像'wrapper(me,f){return function(){f(me);};}')。这是否优于使用'self'方法是有争议的,因为它显然比较慢,但它也更加灵活(您可能需要在包装方法中进行额外的设置,以便共享)。 – lsoliveira 2011-12-20 16:47:08

+0

谢谢您的解释,也许我会在将来使用这种方法:) – 2011-12-20 17:06:48

1

您还可以使用bind将上下文绑定到函数范围

“use strict”;

功能资源管理器(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"; 

}