JavaScript中如何将数据传递给匿名函数?

5
当我将'this'传递给匿名函数时,代码如下:
MyClass.prototype.trigger = function(){
    window.setTimeout(function(){this.onTimeout();},1000);
}

我遇到了一个“this.onTimeout不是函数”的错误。我猜测在匿名函数执行时,'this'已经不再可用了?所以我一直在这样做:

MyClass.prototype.trigger = function(){
    var me = this
    window.setTimeout(function(){me.onTimeout();},1000);
}

您确定这样做是正确的吗?虽然有点可行,但感觉很奇怪。

接下来我们有这个例子:

$(function(){
    function MyClass(){
        this.queue = new Array();
    }
    MyClass.prototype.gotAnswer = function(count){
        $('body').append("count:"+count+"<br/>");
    }
    MyClass.prototype.loadAll = function(){
        var count = 0;
        var item;
        while(item = this.queue.pop()){
            count++;
            var me = this;
            $.getJSON("answer.html",{},function(data){me.gotAnswer(count);});
        }
    }

    var o = new MyClass();
    o.queue.push(1);
    o.queue.push(2);
    o.loadAll();

});

这将输出:
2
2

不应该输出为:
1
2

那么我发现将$.getJSON语句放在另一个函数中可以使它正常工作:

MyClass.prototype.loadAll = function(){
    var count = 0;
    var item;
    while(item = this.queue.pop()){
        count++;
        this.newRequest(count);
    }
}
MyClass.prototype.newRequest = function(count){
    var me = this;
    $.getJSON("answer.html",null,function(data){ me.gotAnswer(count); });
}

这将输出:

1
2

(或者反过来。)这里发生了什么?传递变量给匿名函数的正确方法是什么?
抱歉,文章有些混乱和冗长。
3个回答

5
您所经历的是正确的行为 - 这不是一个好的行为,但它是语言的一部分。 "this" 的值在每个函数定义内部被重置。有四种调用函数的方式,它们设置 "this" 的方式不同。
  1. 常规函数调用
    myFunc(param1, param2);
    这种调用函数的方式总是将 "this" 重置为全局对象。这就是您的情况。
  2. 作为方法调用
    myObj.myFunc(param1, param2);
    这不足为奇地将 "this" 设置为方法被调用的任何对象。在这里, "this" == "myObj"。
  3. 应用方法调用
    myFunc.apply(myObj, [param1, param2])
    这是一个有趣的方法 - 这里 "this" 被设置为您将作为 apply 方法的第一个参数传递的对象 - 就像在没有该方法的对象上调用方法一样(请注意,函数默认都具有 apply 方法)。
  4. 作为构造函数(使用 "new")
    myNewObj = new MyConstructor(param1, param2);
    当您以这种方式调用函数时,"this" 初始化为从函数的原型属性继承方法和属性的新对象。在这种情况下,新对象将继承自 MyConstructor.prototype。此外,如果您没有显式返回值,则将返回 "this"。

您使用的解决方案是推荐的解决方案 - 将 "this" 的外部值分配给另一个变量,该变量仍将在函数内部可见。我唯一要更改的是像 Török Gábor 所说的将变量称为 "that" - 这是一种事实上的标准,可能会使其他程序员更容易阅读您的代码。


太棒了!非常感谢! 嗯,我想我对js的工作方式相当熟悉。它有点像LUA。只是我一直在想“Java”,但那并不适用。 - 0scar

3
你对闭包感到困惑。
对于第一个问题,是的,你是正确的,它可以这样做。唯一的区别在于有一个约定来命名变量that,它保存了this
MyClass.prototype.trigger = function(){
    var that = this;
    window.setTimeout(function(){that.onTimeout();},1000);
}

在StackOverflow上已经有一个关于这个问题的很好的讨论。请查看How does a javascript closure work?中的答案。

你的第二个问题是Javascript closure inside loops - simple practical example的一个准确的副本。


0

如果在您的新方法newRequest中需要使用“for”或“while”语句,那么您将遇到相同的问题。 另一种解决方案可能是创建一个闭包:

像这样:

$.getJSON("answer.html",{},(function(me){return function(data){me.gotAnswer(count);}})(this));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接