21

What is the recommended way to get a handle to the global object in ES5 strict mode in an unknown host environment?

ECMAScript doesn't provide a built-in way to reference the global object that I'm aware of. If it does, this is the answer I'm looking for.

In a known environment, the global object usually has a self-referential property. Since the global object is the VO for the global scope, properties of the global object are global variables, so we can use them get a handle to the global object from anywhere:

  • In a web browser, we can use window or self.

  • In node.js, we can use global.

However, this is not necessarily the case in all host environments. As far as I know, Windows Script Host does not provide any way to access the global object. The recommended way to get the global object in WSH seems to be to use the this keyword in a context where it does not resolve to an object. For example:

var GLOBAL = (function(){return this}());

This technique will work for any host environment, but not in strict mode, because an undefined this does not reference the global object in strict mode:

If this is evaluated within strict mode code, then the this value is not coerced to an object. A this value of null or undefined is not converted to the global object and primitive values are not converted to wrapper objects. The this value passed via a function call (including calls made using Function.prototype.apply and Function.prototype.call) do not coerce the passed this value to an object (10.4.3, 11.1.1, 15.3.4.3, 15.3.4.4).

As expected, the following code results in undefined:

(function(){
    "use strict";
    var GLOBAL = (function(){return this}());
    console.log(GLOBAL);
}());

So, what is the proper way to get a handle to the global object in any environment, regardless of strict mode?

By the way, my current approach is to sniff for global variables referencing the global object like this:

var self, window, global = global || window || self;

...and then just use global. I think this is a bad solution for a number of reasons, most of which are fairly obvious, and it doesn't address the WSH problem.

Dagg Nabbit
  • 75,346
  • 19
  • 113
  • 141
  • 1
    couldn't you do `'use strict'; (function(global) { })(this)`? (I didn't test this whatsoever). – Marshall Mar 10 '12 at 00:50
  • That works in Node.js, at least, @Marshall –  Mar 10 '12 at 01:17
  • @DavidEllis woops, that's my platform of choice O_o I guess I should check it in others before I say anything ;) – Marshall Mar 10 '12 at 01:27
  • @Marshall, sorry if you're confused about what I said. I just tested in Node.js is what I meant; nothing more than that. I'd assume it works in Firefox and IE, but I'm not sure about this Windows Script Host environment (is that what Metro runs for HTML-based apps?) –  Mar 10 '12 at 01:30
  • @Marshall that should work in browsers, but it means my code needs to be able to run in the global scope. Can you explain how that works in node.js? My understanding was that `this` resolved to the `module` object in the "module scope" of a node.js script, so shouldn't your anonymous function just return that `module` object? (I only have a theoretical understanding of node, not a practical one). – Dagg Nabbit Mar 10 '12 at 01:35
  • @DavidEllis I really don't care about WSH, it just made a good example for the purposes of this question... I might want to write some generic library code that someone happen to want to use in a WSH script, that's sort of what I'm trying to work out. I don't even know if WSH has strict mode, I think it is JScript. – Dagg Nabbit Mar 10 '12 at 01:37
  • @GGG In a modules you would append things to `module.exports` (or just `exports`) to expose them to the app requiring them. Interestingly, `this` in a module is, as assumed, global to that module. But is not equal to `module`. So yes, it's like an isolated global scope to that given module / app. – Marshall Mar 10 '12 at 01:44
  • @Marshall are you sure it is not equal to the module? Try `this.exports = {test:1}` - see this question: http://stackoverflow.com/questions/9484763/ – Dagg Nabbit Mar 10 '12 at 01:46
  • @GGG: I forgot about the module scoping with Node.js. I was only testing with the REPL, where ``this === global``. [You're absolutely right for normal scripts](https://gist.github.com/2009680) –  Mar 10 '12 at 01:49
  • @DavidEllis love the name of that gist ;) – Dagg Nabbit Mar 10 '12 at 01:50
  • @GGG: Since you mentioned that it's for a script you're writing, why do you need to access the global scope explicitly, anyways? There be dragons down that path... –  Mar 10 '12 at 01:52
  • @GGG: [I just updated the gist](https://gist.github.com/2009680/d604ddf1874d41062982edf0981f52964be967ed). ``this`` in Node.js appears to just be a placeholder empty object and doesn't reference the global scope of the module at all. –  Mar 10 '12 at 01:57
  • @DavidEllis http://chat.stackoverflow.com/transcript/message/2893039#2893039 – Dagg Nabbit Mar 10 '12 at 01:58
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/8726/discussion-between-ggg-and-david-ellis) – Dagg Nabbit Mar 10 '12 at 01:59
  • @GGG I may be wrong. I will try that and see. – Marshall Mar 10 '12 at 02:05

4 Answers4

29

In ES5, you can get a reference to global object from within strict mode via indirect eval call:

"use strict";
var global = (1,eval)('this');

Take a look at my article; particularly at this section on strict mode.

kangax
  • 38,898
  • 13
  • 99
  • 135
  • thanks for the response, I've read many of your articles (which are excellent by the way) but I must have missed this one. I thought about using `eval`, but didn't know about this indirection trick (just using `eval` without indirection evaluates in the function scope, obviously). – Dagg Nabbit Mar 10 '12 at 15:23
  • Thanks for kind words :) Speaking of eval trick, I've heard something about Node.js not following ES5 behavior in that regard and that there's some other magic going on behind the scenes. Never had a chance to test it, so you might want to double check. It does work in all browsers I ever tried, though, and it _should_ work in theory, since that's how it's spec'ed in ES5. – kangax Mar 10 '12 at 15:34
  • I'll test it out in node... if it is broken in node, I can just have it check for an already-existing `global` to work around that (and rename `global` in the example to something else so it won't shadow existing `global` if called from function scope). This is exactly what I was looking for, thank you. – Dagg Nabbit Mar 10 '12 at 15:39
  • Hey, I just had a thought. I can get a reference to the global object's *[[Prototype]]* and simply do stuff like `constructor.prototype.foo = 'bar'`. This should be enough to set properties of the global object, although it won't work as well for getting properties obviously. What do you think? This won't work in the console unless you `eval` it by the way, looks like the console shadows `constructor`. I think we could also use this trick to make sure we have the real `eval` and not an overwritten one. – Dagg Nabbit Mar 10 '12 at 15:54
  • 4
    upvoted. minor note the CSP restrictions: https://developer.chrome.com/extensions/contentSecurityPolicy#JSEval – bitoiu Jul 03 '14 at 12:45
7

In global code, the thisBinding is set to the global object regardless of strict mode. That means you can pass it from there into your module IEFE:

// "use strict"; or not
(function(global) {
    "use strict";
    …
    console.log(global);
    …
}(this));
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Actually it's similiar to @DavidEllis' answer, but without polluting the global scope :-) – Bergi Jun 04 '13 at 00:04
1

In strict mode, the way to get a reference to the global object is to assign a variable in the global object referencing itself.

That is this means the global object when in the global context, so the solution is simply:

"use strict";
var global = global || this;
(function() { global.hello = "world"; })();
console.log(hello); // Outputs 'world' as expected

This does mean that you have to pollute the global namespace with a reference to itself, but like you say, it should already have been there.

  • Wait, won't the reference to `global` fail unless you use a `typeof` check? – Wil Moore III Feb 06 '13 at 17:36
  • 2
    @wilmoore: No, because the `var` statement is hoisted. `var global = global || this;` is effectively processed as `var global; global = global || this;`. If `global` already exists, `var global` is a no-op, otherwise it is created with an initial value of `undefined`, which is then overwritten in the next statement. – Tim Down Aug 25 '14 at 09:43
  • Yeah, when I originally posted, I knew about `var` hoisting, but to be honest, I totally did not put it together in this particular context. Thanks for the heads up :) – Wil Moore III Aug 26 '14 at 05:20
0

Mathias Bynens has an excellent article on the subject. The following works in all environments, use strict or no, including ones with CSP enabled, like Chrome extensions.

(function() {

    // A globalThis polyfill | # | Adapted from https://mathiasbynens.be/notes/globalthis

    if (typeof window !== 'undefined' && window && window.window === window) { return window } // all browsers
    else { // webworkers, or server-side Javascript, like Node.js
        try {
            Object.defineProperty( Object.prototype, '__magic__', { // hocus pocus
                get: function() {
                    return this;
                },
                configurable: true // This makes it possible to 'delete' the getter later
            });
            __magic__.globalThis = __magic__;
            delete Object.prototype.__magic__;

            return globalThis;
        } catch (e) {
            // we shouldn't ever get here, since all server-side JS environments that I know of support Object.defineProperty
            return (typeof globalThis === 'object') ? globalThis : ( (typeof global === 'object') ? global : this );
        }
    }
})();
Kithraya
  • 358
  • 2
  • 10