从onload事件调用对象函数会使其丢失上下文

时间:2012-03-14 05:01:34

标签: javascript html5 object prototype onload

我想在加载所有需要的图像时调用函数。图像的数量是预先知道的,所以我尝试将函数调用附加到每个图像的onload事件并计算它被调用的次数。

<html>

<head>
    <script>

    var tractor;

    function Tractor()
    {
        this.init_graphics();
    }

    Tractor.prototype.init_graphics = function()
    {
        this.gr_max = 3;
        this.load_count = 0;

        this.loading_complete(); // #1 test call, works OK

        this.img1 = new Image();
        this.img1.onload = this.loading_complete; // #2 gets called, but gr_max = undefined, load_count = NaN
        this.img1.src = "http://dl.dropbox.com/u/217824/tmp/rearwheel.gif"; //just a test image
    }

    Tractor.prototype.loading_complete = function()
    {
        this.load_count += 1;
        alert("this.loading_complete, load_count = " + this.load_count + ", gr_max = " + this.gr_max);
        if(this.load_count >= this.gr_max) {this.proceed();}
    };

    function start()
    {
        tractor = new Tractor();
    }
    </script>
</head>

<body onload="start();">
</body>

</html>

当它刚从对象的另一个函数调用时(参见#1),它就像我预期的那样工作。但是,当从onload事件调用它时(参见#2),变量变为“未定义”或“NaN”等。发生了什么?我究竟做错了什么?我如何使它工作?

我不记得曾经在Javascript中创建自己的对象,所以我当然对这个“我的代码有什么问题”这个问题深表歉意。我主要使用this article作为参考,第1.2节。

以防万一,我在http://jsfiddle.net/ffJLn/

上添加了相同的代码

3 个答案:

答案 0 :(得分:7)

bind回调的上下文:

this.img1.onload = this.loading_complete.bind(this);

请参阅:http://jsfiddle.net/ffJLn/1/(与您的相同,但添加此内容)

以下是对bind详细工作方式的解释:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

基本思想是它使绑定函数中的this等于你传递给bind的参数。

另一个选择是创建一个闭包:

var self = this;
this.img1.onload = function() { self.loading_complete() };

闭包是保持对其上下文的引用的函数(事实上,javascript中的所有函数都以这种方式工作)。所以在这里你要创建一个匿名函数来保持对self的引用。因此,这是维护上下文的另一种方式,loading_complete拥有正确的this

请参阅:http://jsfiddle.net/ffJLn/2/(与您的相同,但有第二种可能性)

答案 1 :(得分:2)

当#2被调用时,您的this已发生变化。 this现在指的是new Image()而不是Tractor对象。

尝试更改......

this.img1.onload = this.loading_complete;

var that = this;
this.img1.onload = function() { that.loading_complete(); };

答案 2 :(得分:0)

您现在可以使用provide lexical binding的es6箭头功能:

this.img1.onload = () => { this.loading_complete(); };