Skip to content Skip to sidebar Skip to footer

Javascript Function Calls: Regular Call Vs Call Vs Bind Call

My question is simple: I'm passing a function to some other function to be call later (sample callback function), the question is when, why and what is the best practice to do it.

Solution 1:

I don't think there's any "best" practise.

You use call if the function you're calling cares what this is.

You use bind if you want to ensure that the function can only be called with the specified value of this.

[There's some overhead to both, i.e. at least one depth of function calls / scope]

Otherwise you just call the function.

Simples :)

Solution 2:

The this object is the context of the function. It's like you make a machine that something for you, and the this object would be the place that the machine works in, like your house. You can move it as you like.

We have 4 ways setting this objects.

Calling the function that is not a method:

fn(someArguments)

This way the this object is set to null or probably the window object.

Calling the function as a method:

someObject.fn(someArguments)

In this case the this object will point to someObject and it's mutable.

Calling with call or apply methods of the function.

fn.call(anotherObject, someArguments)
someObject.call(anotherObject, someArguments)
someObject.apply(anotherObject, [someArguments])

In this case the this object will point to someObject here. You are forcing it to have another context, when calling it.

Binding a the function

var fn2 = fn.bind(anotherObject, someArguments)

This will create another function that is binded to that this object we gave it(anotherObject). No matter how you call it, the this object is going to be the same.

Use Cases

Now you can do some tricky stuff knowing this. The reason that why we have it here(I think it came first from C++) is that methods of an object need to access to their parent. The this object provides the access.

var coolObject = {
  points : ['People are amazing'],
  addPoint : function (p) { this.points.push(p) }
}

So if you do the following it won't work:

var addPoint = coolObject.addPoint;
addPoint('This will result in an error');

The error will be thrown because the this object is not our coolObject anymore and doesn't have the points property. So at times like this, you can something like this:

var addPoint = coolObject.addPoint;
addPoint.call({points : []}, 'This is pointless');

This is pointless, but the function will work, even the this object is not what its supposed to be.

var anotherCoolObject = {
  points : ['Im a thief!'],
  addPoint : coolObject.addPoint
}
anotherCoolObject.addPoint('THIS IS CALL STEALING');

Still the function will work if you call it like that, since the this object will point to anotherCoolObject which has the points property.

The most popular use case I've seen is slicing the arguments object:

functionreturnHalf() {
  return [].slice.call(arguments, 0, arguments.length / 2);
}

returnHalf('Half', 'is', 'not', 'awesome');
// >> [Half', 'is']

So you see, arguments object is not an instanceof array. If we do arguments.slice(...) then you're gonna be killed by the compiler. But here we use the array's method on arguments object, since it's array like.

Sometimes you don't want your function context to be changed or you wanna add your own arguments, you use bind.

For example when you add a listener for an event with jquery, when jquery calls your function, the this object will be the element. But sometimes you wanna do tricky stuff and change it:

var myElement = {
  init : function () {
    $(this.element).click(this.listener.bind(this));
  },
  view : "<li>${Name}</li>",
  name : 'ed',
  element : $('#myelement'),
  listener : function () {
    this.element.append($.tmpl( this.view, this ));
  }
}

myElement.init();

So here, you bind it to the myElement, so you can have access to the object properties to render the view. Another examples would be the following:

for (var i = 0; i < 10; i++) {
  setTimeout(function () {console.log(i)}, 10)
}

// All of them will be 10.for (var i = 0; i < 10; i++) {
  setTimeout((function () {console.log(this.i)}).bind({ i : i }, 10)
}

If you have put an asynchronous function call in a loop, by the time the callback is called, the loop is finished, and the counter have reached the end, you can use bind to cleanly bind the current counter to your callback.

Another good use case of it, that I use a lot is when passing my functions with arguments to async module, without creating closures.

async.parallel({
  writeFile : function (cb) {
    fs.writeFile('lolz.txt', someData, cb);
  }, 
  writeFile2 : function (cb) {
    fs.writeFile('lolz2.txt', someData, cb);
  }
}, function (err){ 
    console.log('finished')
});

async.parallel({
  writeFile : fs.writeFile.bind(fs, 'lolz.txt', someData),
  writeFile2 : fs.writeFile.bind(fs, 'lol2z.txt', someData),
}, function (err){ 
    console.log('finished')
});

These two implementations are identical.

Performance

Just check these out:

http://jsperf.com/bind-vs-call2

http://jsperf.com/js-bind-vs-closure/2

http://jsperf.com/call-vs-closure-to-pass-scope/10

bind has a big performance overhead comparing to other types of calling, but make sure you don't sacrifice performance with maintainability with pre-mature optimizations.

Also you can have a look at this article.

Post a Comment for "Javascript Function Calls: Regular Call Vs Call Vs Bind Call"