JavaScript:从回调函数中调用对象内的函数

3
当我试图从回调函数中使用“this”调用对象内的函数时,会出现方法未定义的错误。我该如何解决这个问题!
 var object_log = {
    user: "",
    pass: "",
    error_message: "an error occured while connecting",
    init: function(user, pass) {
        this.user = user;
        this.pass = pass;
    },
    login: function() {
        remote_submit(identify, this.success, this.error);
    },
    error: function() {
        alert(this.error_message);
    },
    success: function() {
        alert("success");
    }
};

this.success 定义在哪里? - elclanrs
我的代码太长了,因为我创建了一个示例。非常抱歉,请让我编辑它。 - Moussawi7
2个回答

4
你需要在回调函数上使用.call().apply()方法,以指定方法被调用时的上下文。
当回调方法remote_submit调用回调函数时,它不再知道this代表什么,因此当它调用回调函数时,它们会像普通函数一样执行,而非作为对象上的方法执行。
你可以通过包装函数来“绑定”它们:
var self = this;
remote_submit(
  identify,
  function() { return self.success.apply(self, arguments); },
  function() { return self.error.apply(self, arguments); }
);

这样可以在匿名函数的闭包中传递上下文,并使用独占的this上下文执行回调函数。

在EMCAScript5+中,似乎可以使用bind函数将其绑定以在回调函数中使用:

remote_submit(identify, this.success.bind(), this.error.bind())

然而,从MDN文档中可以看到:

bind函数是ECMA-262第5版的最新补充内容。因此,它可能不会在所有浏览器中出现。您可以通过在脚本开头插入以下代码来部分解决此问题,在不支持原生bind()功能的实现中使用大部分bind()功能。

这里提供了shim/polyfill:

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      // closest thing possible to the ECMAScript 5 internal IsCallable function
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(this instanceof fNOP && oThis
                                 ? this
                                 : oThis,
                               aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

更新:
回答您的附加问题,让我们首先查看 callapply 的文档并分解它们的工作原理:
基本上它们的工作方式相同,唯一的区别是它们如何使用它们的参数:
myfunc.call(target, param1, param2, param3);

将会使用target作为this,调用myfunc(param1, param2, param3)

var args = [param1, param2, param3];
myfunc.apply(target, args);

将使用target作为this参数,调用myfunc(param1, param2, param3)

基本上区别在于.apply()需要传入一个数组作为参数,而call函数需要你在代码中写入参数。

接下来,如果我们看一下我给你的例子:

function() { return self.success.apply(self, arguments); }

这将返回一个函数,该函数将通过传递匿名函数中传递的所有参数(arguments变量)到apply函数,从而调用您的回调函数。因此:

var a = function() { return self.success.apply(self, arguments); };
a(1,2,3,4);

这将使用this作为self来调用self.success(1,2,3,4)函数。如果您想要增加一些特定的参数,例如,如果您想要a(1,2,3,4)调用self.success(self.test, 1, 2, 3, 4)函数,那么您需要提供一个增强的数组给apply函数:

var a = function() {
  var args = [self.test];
  for(var i = 0; i < arguments.length; i++) args[] = arguments[i];
  return self.success.apply(self, args);
}

非常感谢,你非常有帮助。 还有一个问题:是否有一种方法可以发送其他数据。 例如:function() { self.success.apply(self,"Hello", arguments); }, - Moussawi7

1
当您将函数作为回调传递时,请按照以下方式进行:
  whatever( object_log.login.bind( object_log ) );

那个对 .bind 方法的调用会返回一个函数,该函数确保你的“login”函数被调用时,this 引用 "object_log" 对象。
对于旧浏览器,有一个很好的 .bind shim 在 MDN 文档站点上

我没有给你点"踩"的意思,但是 bind 只在 ECMAScript5 + 中可用,这可能取决于浏览器而成为一个问题,这很有可能就是原因。 - Aren
@Aren 嗯,它在所有现代浏览器中都可用,并且有一个很好的 shim - 我会添加参考文献。 - Pointy
不幸的是,我们中的许多人被困在支持IE8上,我给了你一个赞成票来抵消-1,因为你是正确的。 - Aren

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