0

I thought I know JavaScript, it seems I don't.

I want to define an object like this. (Example from http://www.phpied.com/3-ways-to-define-a-javascript-class/ )

var apple = {
    type: "macintosh",
    color: "red",
    getInfo: function () {
        return this.color + ' ' + this.type + ' apple';
    }
}

Well, this looks fine, let's print the info...

apple.getInfo() //returns "red macintosh apple" as expected

OK, now take the function out and run it again...

var func = apple.getInfo; func(); //returns "undefined undefined apple"

Well, that is not what I expected. Apparently, this is interpreted as window. Which is not what I wanted.

My question is -- what is the idiomatic, preferred way to rewrite the apple literal, so that apple.getInfo returns a function, that could be run separately, but still used the object properties?

Karel Bílek
  • 36,467
  • 31
  • 94
  • 149

2 Answers2

6

The way this works is that it depends on how it is called. A quick tip to know what this is is to look at the object before the function name in the call.

Doing apple.getInfo() makes this refer to apple. But calling it as func() is as if calling window.func() (assuming it's in the global space), which makes this refer to window.

If you want to "force" the value of this on a function, then do bind. It creates a copy of the function with this forced as the first parameter passed.

var func = apple.getInfo.bind(apple);
// all calls to `func` will have `this` "forced" as `apple`

If you want to dictate this on the call but not permanently tamper this on func (like how bind does it), you can go with call or apply:

var func = apple.getInfo;
func.call(apple);
func.apply(apple);
// Both function will have `this` as `apple` only for the call.
Joseph
  • 117,725
  • 30
  • 181
  • 234
  • Hm, interesting! I didn't know it is this complex. I needed a function to call to give as a callback, I thought I could just use `apple.getInfo` and not create anonymous functions. OK, I will then just make another anonymous function then. Thank you! – Karel Bílek Jun 28 '14 at 15:09
  • Addendum: I actually ended up using .bind instead of anonymous function, because it just "seems" cleaner. (Not sure if it's faster/more efficient, or not.) – Karel Bílek Jul 01 '14 at 16:02
  • Huh, I was wrong! The closure is *really* more efficient than binding - on all browsers. http://jsperf.com/bind-vs-self-closure/7 – Karel Bílek Jul 01 '14 at 16:15
1

@Joseph The Dreamer has put up a great answer.

Since the OP is looking for a way to have apple.getInfo always return a function that doesn't have the undefined problem, I figured I'd throw up this alternative.

var apple = {
    type: "macintosh",
    color: "red"
};

apple.getInfo = (function () {
    return this.color + ' ' + this.type + ' apple';
}).bind( apple );

This way you don't have to call apple.getInfo.bind(apple) every time you want to create a new reference to the function.

Note that you can't put getInfo within the original object literal because at that point, apple doesn't have a value assigned to it and .bind( apple ) won't work properly.

go-oleg
  • 19,272
  • 3
  • 43
  • 44