3

when i try to call a function inside the object using "this" from a callback function, an error occur saying that the method is undefined. How can I solve this issue!.

 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");
    }
};
2
  • Where is this.success defined? Commented Jan 8, 2014 at 22:49
  • My code is too Long, that because I create an example. so sorry, let me edit it Commented Jan 8, 2014 at 22:50

2 Answers 2

4

You need to use the .call() or .apply() methods on the callback to specify the context which the method is called upon.

The callback method remote_submit does not know what this will be anymore and thus when it calls the callback methods they're executed like normal functions not on an object.

You can "Bind" your functions by wrapping them on the way out:

var self = this;
remote_submit(
  identify,
  function() { return self.success.apply(self, arguments); },
  function() { return self.error.apply(self, arguments); }
);

This allows you to pass the context in the closure of the anonymous function and execute the callbacks with an exclusive this context.

It appears that in EMCAScript5+ you can use bind on the function to bind it for use in a callback:

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

However from the MDN Documentation:

The bind function is a recent addition to ECMA-262, 5th edition; as such it may not be present in all browsers. You can partially work around this by inserting the following code at the beginning of your scripts, allowing use of much of the functionality of bind() in implementations that do not natively support it.

The shim/polyfill is here:

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;
  };
}

Update:

To answer your additional question, let's first look at the call and apply documentation and break down how they work:

Fundamentally they work the same, the only difference is how they take their arguments:

myfunc.call(target, param1, param2, param3);

Will call myfunc(param1, param2, param3) with target as this.

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

Will call myfunc(param1, param2, param3) with target as this.

Basically the difference is that .apply() takes an array of arguments, where the call function requires you to write in the arguments in the code.

Next, if we look at the example i gave you:

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

This returns a function that will call your callback by passing all the arguments (arguments variable) that were passed into the anonymous function, onto the apply function. So:

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

This will call self.success(1,2,3,4) with self as this. If you'd like to augment the arguments with something specific for example if you wanted a(1,2,3,4) to call self.success(self.test, 1, 2, 3, 4) then you'll have to provide an augmented array to the apply function:

var a = function() {
  var args = [self.test];
  for(var i = 0; i < arguments.length; i++) args[] = arguments[i];
  return self.success.apply(self, args);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot, you was very helpful. another question please:does there are a way to send other data. exp. something like this:function() { self.success.apply(self,"Hello", arguments); },
1

When you pass the function as a callback, do it like this:

  whatever( object_log.login.bind( object_log ) );

That call to the .bind method will return a function that'll make sure your "login" function will be called such that this references the "object_log" object.

There's a good shim for .bind for older browsers at the MDN documentation site.

3 Comments

I didn't down-vote, but bind is only available in EMCASCript5 + which could be a problem depending on the browser, and that's likely the reason.
@Aren well it's available in all modern browsers, and there's a good shim - I'll add reference for that.
Unfortunately a lot of us are stuck supporting IE8, I gave you an upvote to counter the -1 because you are correct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.