2

Update

For clarity: @FelixKing: Yes, I expected this to still be undefined when calling window.foo(), and here's why: since, in JavaScript:

function foo()
{
    console.log('I am a function');
}

Is (almost) the same thing as:

var foo = function()
{
    console.log('I am a function');
}

and foo === window.foo evaluates to true, I'd expect them both to behave alike. If functions are variables, and JS falls back to the global object (inside function x, a variable isn't declared but you use it, JS bubbles up through all scopes, up to the global object, until it finds the variable in question, or it creates it on a global level), it shouldn't matter if specify the window object up front or not. Yet, when you do, the behaviour changes, which I didn't expect.
I have a gut feeling as to why this is the case (I'm defining methods, rather then variables) but then again, for global objects there is very little difference between them. var c = 'Variable'; console.log(window.c === c); logs true etc... but I'd like to know what the difference actually is, and how it works (on all levels).

I'm prepared to go as far as to accept that foo.apply(this,[]); or foo.apply(window,[]); allows you to force this to point to the global object, but not window.foo();. If strict is there to shield the global object, I'd say this is leaving the back door wide open. I occasionally find myself calling functions, depending on the value of a variable, for example. For that I use window[myVar]();, strict or not, that means this will point to the global object, whereas it doesn't if I call the function directly. That is, to my mind, an inconsistency.


I've encountered some weird behaviour with the this keyword in strict mode. Don't get me wrong I know this is undefined in strict functions. What I find confusing, is that this can be forced to point to the global object (or any other object, for that matter), effectively undermining the safety-net, provided by strict mode. This has other implications, too. Consider this code:

'use strict';//using strict everywhere
(function(Global)
{
    var closureObject = {};
    Global.foo = function()
    {
        closureObject.setIn = closureObject.setIn || 'Set in foo';
        console.log(this);
        console.log(this === Global);
        bar();
        bar.apply(this);
        return (this !== Global ? this : undefined);
    };
    Global.bar = function()
    {
        closureObject.setIn = closureObject.setIn || 'set in bar';
        console.log(this);
        return (this !== Global ? this : undefined);
    };
})(this);
var undef = (Math.ceil(Math.random()*10)%2 ? foo() : bar());
foo();//undefined --- false​​​​​​​​​​​​​​​​​ --- undefined --- undefined
window.foo();//Window --- true --- undefined --- window
foo.apply(this,[]);//same as window.foo

Same applies for custom objects:

function Foo(n)
{
    this.name = n;
}
Foo.prototype.func = foo;//or window.foo
//other objects:
var d = new Date();
foo.apply(d,[]);//Date --- false --- undefined --- date

This, In my opinion, is a possible source of hacks and pitfalls. What's more: it makes it rather hard to determine where the call is coming from: if foo() was called from the global object (window.foo();), that context is of course not passed on to bar, unless bar is called using bar.apply(this,[]);

The reason why I might want to have a cut & dry, safe and reliable way to determine the caller context is simple: I'm using a closure to avoid those pesky globals, but at the same time I'm setting up a couple of functions that do serve as event handlers.
I know not using strict mode, or setting a global are easy fixes, but strict mode is here to stay, and I like what it brings to the party (well, most of it). I firmly believe that this is the way JS is going to evolve, and I'd hate to find myself weeping of my own broken code because of not wanting to bother with strict. That might not happen too soon, but I just want to keep my knowledge up-to-date.

I've read the MDN pages on strict, as well as John Resig's blog post, I've watched quite a few of DC's video's and read a lot of his articles and I haven't found a definitive explanation for the behaviour I described above. I haven't read through the entire ECMAScript standard (Boy, that stuff is so dry, it could drain the Sahara desert), but perhaps someone here could point me in the right direction to help me understand this better.

Community
  • 1
  • 1
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • 3
    What exactly is the inconsistent behaviour here? Did you expect `this` to be `undefined` as well if you call `window.foo()`? – Felix Kling Sep 03 '12 at 15:45
  • The same question from me, as `this` can be happily referred to when not in functions (there's no `main()` in JS). And as I see it, it's exactly what happens here. – raina77ow Sep 03 '12 at 15:52
  • From what I've read `this` doesn't point to undefined in strict mode, to avoid globals being set when they shouldn't (for example; constructors without `new` keyword). I was sort of expecting `window.foo()` to _not_ change that, if the whole point is not to set globals, then a global context should be kept off-limits, shouldn't it? I mean, consistency _is_ what strict mode is all about, right? why then enable hacky work-arounds? – Elias Van Ootegem Sep 03 '12 at 16:00
  • 2
    The whole point is to not set the global object as the context when there is no direct definition. When you do `object.method`, you are defining `this` directly. If it resulted in `undefined`, it would be a major hole WRT prototypal inheritance. – gray state is coming Sep 03 '12 at 16:14
  • I agree with user160068... the *implicit* reference to the global object was dangerous / confusing / error-prone / etc. But *explicitly* referring to the global object is fine and I would find it more confusing if that did not work. – Felix Kling Sep 03 '12 at 16:28
  • @FelixKling @user160068: I've updated my question: there are cases in which you _inadvertently_ use `window.foo();` to call your function, setting `this` to point to the global object, where it really shouldn't. To my mind, opening the global object should be possible, but only with a function call that shows definite intent (like `strictFunction.call(window);` or something. – Elias Van Ootegem Sep 03 '12 at 16:37

1 Answers1

2

I'm not sure if I'm reading your question right, but it seems as if you are having trouble with the fact that func.apply(context, arguments) can take context as a parameter which will be then be reffered by this inside the function.

I would argue that passing the context is necessary and JavaScript couldn't work properly without it.

Off the top of my head, since there is no super the only way to use inheritance properly is to use something like BaseClass.prototype.baseFunction.apply(this, arguments).

Removing that functionality would create a language that is very different from JavaScript today and that is not what strict mode is about.

Otto Allmendinger
  • 27,448
  • 7
  • 68
  • 79
  • Sorry, I think my question could be clearer. It's not the `apply`, or `call`, Not even the setting of the context that is the issue. It's just the fact that `window.foo()` opens up the global object, whereas `foo()` doesn't, even though `foo()` implies the nearest `foo` definition, in this case: global. Allowing `window.foo();` to set `this` to point to global is somewhat risky. Many people use `window[someVar]()` to call functions. That opens up the global object, and is risky. It should be possible, but only with `call` and `apply` bc that shows clear intent, doesn't it? – Elias Van Ootegem Sep 03 '12 at 16:45
  • 1
    @Elias: You can use the same argument for `a.b()` though. I use `foo.bar()` because I want `this` to point to `foo` inside `bar`. It shows my clear intent as well. Granted, you could see `window.foo()` as exception because `window` is an object as well as the global object. In any case, I don't think SO right place to discuss this, maybe it's better suited for some ECMA mailing list. Or what would expect from an answer? An explanation for why it is like this? (because calling functions assigned to properties work like this); Why `window` was not included from this? Or consent? – Felix Kling Sep 03 '12 at 20:33
  • @Elias if `this` in the context of `window.foo()` *wouldn't* point to `window`, I'd call it inconsistent. – Otto Allmendinger Sep 03 '12 at 23:11
  • @FelixKling: you're right, when I was updating my question, I thought a bit more about this _"inconsistency"_, it does make sense. Although `window[someVar]();` is still somewhat dangerous, this isn't as much a question as it is a topic for the ECMA mailing list. I'd like to +1 everybody who chipped in, but I'm going to close this question, because it's more of a discussion then a question. I'll leave this open for a couple of hours so everybody can post an answer and get an upvote for their contribution. Thanks! – Elias Van Ootegem Sep 04 '12 at 07:00