Timeout with bind, call & apply methods

1.5k views Asked by At

Up until now, I've always used var self = this before creating a function that would need access to its parent. However the bind() method seems like a more appropriate way to do so and I'm exploring that option, along with the apply() and call() methods.

This is what I came up with to compare all three:

jsFiddle

(function(){

    this.say = function(text){
        
        console.log(text);
    }
        
    this.run = function(){
        
        console.clear();
        
        setTimeout(function(){
            this.say('bind');
        }.bind(this), 1000);
        
        setTimeout(function(){
            this.say('call');
        }.call(this), 1000);
        
        setTimeout(function(){
            this.say('apply');
        }.apply(this), 1000);
    }
    
    this.run();
    
})();

But the script leaves me with some questions:

  1. Why don't the call() and apply() methods respect the timeout like the bind() method does and which one should I use?

  2. Is there any difference between the following syntaxes which behave similarly:

    setTimeout( function(){ this.say('bind'); }.bind(this) , 1000);
    
    setTimeout( (function(){ this.say('bind'); }).bind(this) , 1000);
    
    setTimeout( (function(){ this.say('bind'); }.bind(this)) , 1000);
    
2

There are 2 answers

0
Josh Beam On BEST ANSWER

You're missing a key point here (but the key point is very obfuscated in documentation).

You know whenever you write a function like this:

function something() {
    // ... whatever
}

It's just a function reference sitting there by itself. It hasn't been called yet.

When you write something like this:

(function something() {

})()

Or this:

function something() { ... }

something();

You've now called the function. Those are two very completely different concepts.

In the first one where the function is just sitting there without having been called, it's referred to as a function reference, and that's exactly what .bind returns: a function reference.

.call and .apply return the return value of whatever function, in the new context. So in fake JavaScript la-la land, it would look something like this:

function() {

}.bind(this)

// returns a function reference: function() {  }

Whereas:

function() {
    return 'hello';
}.call(this)
// returns hello... NOT A FUNCTION REFERENCE!

You see...

You would probably never do something like this:

function hello() {
    return true;
}

setTimeout(hello(), 100);

You'd get an error: setTimeout expected a Function, and it received a Boolean, or something like that.

function error

^ That's not a very semantic error, but it's an error nonetheless.

But, what you would do is something like this:

function hello() {
    return true;
}

setTimeout(hello, 100);

See the difference? That last example is okay, because you passed in a function reference.

Because internally, setTimeout does something like this:

window.setTimeout = function(callback) {
    // a bunch of other stuff, and...

    callback();
};

As far as your second question goes...

Those are pretty much all equivalent. Closures make sense though whenever you're declaring variables that you don't want to give other objects access to.

To help you understand closures a little bit, let's pretend we're not even talking about JavaScript.

In math, you know how you can do something like a + b * c?

Well, when you group them by parentheses, it kind of changes the behavior: (a + b) * c.

Now, obviously that's not quite related to JavaScript in the sense that in JavaScript we're not worried about order of operations (unless you're actually doing math in JavaScript), but the whole idea is that those parentheses just act as a container (what we call a closure) for whatever is inside of it.

So when you put a function inside parentheses, you're just hiding that function from the outside world, but it can still return stuff, and it can still have access to parent scopes (like window, for example).

A cool example:

var name = (function(name) {
    return name
})(function() {
    return 'Jane';
}());

console.log(name); // => Jane
0
K. P. MacGregor On

Both .call and .apply execute the functions on which they are called immediately. What this means is that in those cases you are passing the result of call or apply on your anonymous function to setTimeout and that result is undefined because those functions don't return anything.

bind on the other hand returns a new function, which you are then correctly passing to setTimeout. To get call and apply to behave similarly, move them inside the anonymous function:

    setTimeout(function(){
        this.say.call (this, 'call');
    }, 1000);

for example.

  1. Afaik those are equivalent.