Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding Javascript's difference between calling a function, and returning the function but executing it later

I'm trying to understand the difference between foo.bar() and var fn = foo.bar; fn();

I've put together a little example, but I dont totally understand why the failing ones actually fail.

var Dog = function() {
    this.bark = "Arf";
};

Dog.prototype.woof = function() {
    $('ul').append('<li>'+ this.bark +'</li>');
};

var dog = new Dog();


// works, obviously
dog.woof();

// works
(dog.woof)();

// FAILS
var fnWoof = dog.woof;
fnWoof();


// works
setTimeout(function() {
    dog.woof();
}, 0);

// FAILS
setTimeout(dog.woof, 0);

Which produces:

  • Arf
  • Arf
  • undefined
  • Arf
  • undefined

On JSFiddle: http://jsfiddle.net/D6Vdg/1/

So it appears that snapping off a function causes it to remove it's context. Ok. But why then does (dog.woof)(); work?

It's all just a bit confusing figuring out whats going on here. There are obviously some core semantics I'm just not getting.

like image 707
Alex Wayne Avatar asked Jan 10 '11 01:01

Alex Wayne


1 Answers

The problem is with context and the this keyword.

Functions don't inherently "belong" to an object. For example, I can create a cat object and copy the woof function over:

var cat = {
    bark: "meow",
    woof = Dog.prototype.woof
};

Now cat.woof will give me "meow". Because of this flexibility of functions to be copied and re-assigned, var fnWoof = dog.woof; disassociates fnWoof from dog - it has no context. Therefore the context, and the this keyword, defaults to window. Since window doesn't have a bark property, you get undefined.

If you give window a bark property:

window.bark = "Arf";

Then your code will work (although erroneously):

fnWoof(); // "Arf"

To make it work as expected, you can pass in the context explicity:

fnWoof.call(dog);
like image 116
David Tang Avatar answered Sep 22 '22 13:09

David Tang