1

I am working on flexible javascript widget. In the course of some testing, I noticed that the context of this changes based on whether the parenthesis used to call a function are placed inside an object or in the global context of its call. Why do the parenthesis matter? And why would parenthesis inside the object refer to window and when placed in global context refer to the object. It seems like it would be the other way around.

Also undefined is returned in both instances. Is there a way to execute the function without returning anything?

I feel like I'm missing something important about this and don't want to miss out.

//this refers to window

var dataSource = {
    read: read()  
};

function read(){
  console.log(this);   
}

dataSource.read;

//this refers to dataSource object

var dataSource = {
    read: read  
};

function read(){
  console.log(this);   
}

dataSource.read();
carter
  • 5,074
  • 4
  • 31
  • 40

3 Answers3

3

Your code is doing two different things.

The first example is executing read() as the object definition is executed (read() is available because it's a function declaration and is hoisted, though this isn't related to the problem you're experiencing). It is called without any context so its this is window (as per the specification, where window is the browser's global object).

The second example has a reference to read(), which is then executed at the end of the block. Because it's executed as a property of dataSource, its this will become that. However, if you first assigned that reference to somewhere else and then invoked it via that reference, you'd again lose that this context.

For fine-grained control of this, take a look at bind(), call() and apply().

Also undefined is returned in both instances. Is there a way to execute the function without returning anything?

A function always has a return value (undefined if not explicitly set), but you're free to ignore it.

alex
  • 479,566
  • 201
  • 878
  • 984
  • This has nothing to do with hoisting. You can merge both examples and the behavior will be the same. Look at the example in my answer. – slebetman Nov 26 '13 at 01:30
  • @slebetman My two examples return two different results, that's why I got confused as to what the parenthesis are actually doing. – carter Nov 26 '13 at 01:33
  • @slebetman Sure, the setting of `this` has nothing to do with hoisting, but if you make that knee-jerk reaction then you probably didn't read my answer carefully (where I was just tangentially mentioning why it didn't blow up). Of course, had it been a function expression, it'd be `undefined` at the attempt to invoke it. – alex Nov 26 '13 at 01:33
  • @alex so far this seems to make some sense for me, but what exactly do you mean by "executing read() as the object definition is executed"? – carter Nov 26 '13 at 01:34
  • 1
    @carter I mean your first example doesn't assign a reference to `read`, it *invokes* `read()` and then attempts to assign its return value to the `read` property on your object. I'm not 100% sure, but I don't think that's what you're trying to do. – alex Nov 26 '13 at 01:35
  • @carter: may I edit your two examples into just one example to clarify the behavior for you? – slebetman Nov 26 '13 at 01:39
  • @alex Yes, I have been curious about why undefined is getting returned in both cases. I want someone to be able to call dataSource.read or dataSource.read() which will load the dataSource object with other properties and such. – carter Nov 26 '13 at 01:40
  • @alex: Both the first and second examples are hoisted, therefore mentioning hoisting is likely to confuse people because it appears to hint that it has something to do with the difference. – slebetman Nov 26 '13 at 01:44
  • @slebetman Possibly, but you've left a helpful comment here now to pick it out so that should cut down on any possible confusion. – alex Nov 26 '13 at 01:48
  • Ah, but long list of comments become hidden after some time. So the answer may still be confusing a couple of months from now. – slebetman Nov 26 '13 at 01:51
  • @slebetman Hopefully your answer will bubble up the top if you believe it's more useful. I'll leave a note in the answer regarding your concern. – alex Nov 26 '13 at 01:53
2

The scoping of this can be a tricky topic in javascript. That said, I can expand my answer on the general rules regarding the scope of this if need be.

But to answer your specific question, whenever you reference this inside an object literal, it by default, refers to the object literal itself.

Edit: as long as the function was invoked as a property of the object literal.

Where as, almost in any other situation I can call to mind, this will refer to the window object unless specified when invoking said function using apply() or call().

Matthew Cox
  • 13,566
  • 9
  • 54
  • 72
  • 1
    *But to answer your specific question, whenever you reference this inside an object literal, it by default, refers to the object literal itself.* only if the function was invoked as a property of that object. E.g. `var a = dataSource.read; a()`. – alex Nov 26 '13 at 01:29
  • @alex That is true. I neglected to mention that condition. Editing – Matthew Cox Nov 26 '13 at 01:30
  • @alex This is the case however with the OP's code which may explain my forgetfullness a bit =P – Matthew Cox Nov 26 '13 at 01:31
0

When this is used outside of objects it refers to the global object which in browser environment is window. Otherwise it refers to the last bareword before the last dot in the invocation.

For example:

function foo () {return this};
var bin = {bar:{foo:foo},foo:foo};

foo();         // returns window
bin.foo();     // returns bin
bin.bar.foo(); // returns bar
//   ^
//   |
//   '------ last bareword before the last dot in the invocation

Now, as to why the location of the parenthesis matter. I think you should be able to guess by now:

When we add a parenthesis to a word (variable/name/reference) what we're doing is make a function call:

foo(); // call foo

If we don't add the parenthesis, what we're doing is refer to the object:

foo; // this contains the function foo

Note that not adding the parens is not calling the function. Therefore it should be obvious that when you do:

var bar = { foofoo : foo() }

What you're doing is passing the result of the function foo to bar.foofoo. The function is invoked without any "dots" in its invocation path. Therefore it doesn't belong to any object and therefore the rule of this == window applies.

On the other hand if you do:

var bar = { foo : foo }

What you're doing is assign the function foo to bar.foo. When you later call it as:

bar.foo()

the invocation contains a "dot" therefore the rule about the last object before the last dot applies.


See my previous answer to a related question for a detailed explanation on how this works in javascript: How does the "this" keyword in Javascript act within an object literal?

Community
  • 1
  • 1
slebetman
  • 109,858
  • 19
  • 140
  • 171
  • 1
    Is it good to explain everything in terms of *dots*? What about `obj["prop"]()`? – alex Nov 26 '13 at 01:45
  • Well, that's just an alternative syntax for the dot operator. And yes, the "dot" is not just pretty syntax in javascript, it is a full-fledged operator that has a specific meaning. So yes, in my opinion it is a valid way to explain it. Especially as it uses simpler language that has less potential to confuse. – slebetman Nov 26 '13 at 01:50