【问题标题】:Accessing Class Functions?访问类函数?
【发布时间】:2013-11-12 21:01:44
【问题描述】:

有人可以向我解释为什么我会得到:

Uncaught TypeError: Cannot read property 'canvas' of undefined game.js:48

Uncaught TypeError: Cannot call method 'resize' of undefined game.js:4

似乎由于某种原因 this.stage 超出了 start 函数的范围。

//index.js
var game;
function init()
{
    game    = new Game(document.getElementById('canvas'));
}
function resize()
{
    game.resize();
}

_

//game.js
function Game(canvas)
{
    this.canvas = canvas;
    this.stage  = null;
    this.loader = null;

    this.scaleCanvas();
    this.initCreateJS();
    this.loadAssets();
}

Game.prototype =
{
    resize: function()
    {
        this.scaleCanvas(this.canvas);
        this.level.resize(this.canvas.width, this.canvas.height);
        this.stage.update();
    },
    scaleCanvas: function()
    {
        this.canvas.width   = window.innerWidth;
        this.canvas.height  = window.innerHeight;
    },
    initCreateJS: function()
    {
        this.stage  = new createjs.Stage(this.canvas);
        createjs.Ticker.setFPS(30);
        this.stage.enableMouseOver(10);
        if(createjs.Touch.isSupported())
        {
            createjs.Touch.enable(this.stage);
        }
    },
    loadAssets: function()
    {
        manifest = [
            {src:"base.png", id:"base"},
            {src:"logo.png", id:"logo"}
        ];

        this.loader = new Loader(manifest, this.start);
    },
    start: function()
    {
        this.level = new Level(4,4,this.stage.canvas.width,game.stage.canvas.height);
        this.level.randomLevel();
        this.level.print();

        game.stage.addChild(this.level);
        game.stage.update();
    }
};

【问题讨论】:

  • 你是如何实例化你的对象的?
  • 客户端代码是什么(即new Game())?
  • start方法在哪里调用?
  • 好像这是在函数的范围内,试试Game.canvas,而不是this.canvas等等。
  • 此外,我不太确定我是否理解为什么在start 的结尾行中使用game.stage 而不是this.stage。

标签: javascript createjs


【解决方案1】:

考虑一下:

var Foo = function(val) {
  this.val = val;
}
Foo.prototype.log = function() {
  console.log('Me haz ' + this.val);
}

在这里,我们定义了一个非常简单的类Foo:具有一个构造函数,将其参数分配给一个名为val 的属性,以及一个处理Foo.prototype 上定义的值的单个方法log。没什么特别的:

var foo = new Foo(42);
foo.log(); // Me haz 42

现在我们定义一个简单的函数 - 它接受另一个函数作为参数,并调用这个函数。像这样的:

var fooCaller = function(cb) { cb(); }
fooCaller(function() { 
  console.log('I am called'); 
});

这很简单,对。但是现在事情突然变得复杂了:

fooCaller(foo.log); // Me haz undefined

什么?显然已经调用了正确的函数(因此Me haz...) - 但为什么this.val 是undefined,而不是42?直接调用 foo.log 和通过调用者 func 之间有什么变化?

区别是this。请看,JavaScript 的一个独特功能是能够为同一个函数切换上下文(this 引用的对象)而不触及它的主体:只有调用函数的方式很重要。有两种特殊方法允许您显式设置this 的值:Function.prototype.call 和Function.prototype.apply。例如:

var bar = new Foo(34);
bar.log(); // Me haz 34
bar.log.call(foo); // Me haz 42

如您所见,使用call,我已将this 对象从bar(其中val 属性等于34)切换到foo。请注意,哪个对象“拥有”该函数已不再重要。


现在让我们回到您自己的代码。这一行……​​

new Loader(manifest, this.start);

... 显然有问题:将未绑定的对象方法作为回调传递几乎总是一个坏主意。在这种情况下,Loader 最终会调用存储在 this.start 中的函数 - 但当它调用时,this 将指向另一个对象。

为防止这种情况,您需要绑定上下文:将new Loader 传递给另一个由this 的当前值定义的函数。最简单的方法是使用Function.prototype.bind:

new Loader(manifest, this.start.bind(this));

... 这基本上会创建另一个函数,其固定值为this(由其当前值定义)。


这虽然相当广泛,但只是快速浏览一下 this 在 JavaScript 中的功能(不仅仅是关于功能,不是双关语)))。我建议检查this answer - 以及其中提到的文章,它们肯定会很有启发性。 )

【讨论】:

    猜你喜欢
    • 2020-02-07
    • 2017-11-11
    • 2011-11-23
    • 2018-10-14
    • 1970-01-01
    • 2011-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多