Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I define a global variable only if it doesn't exist in ES5 strict?

I'm writing an implementation of ES Harmony Symbol/Name in ES5. I'm going to use the name Symbol, but I want the browser to use any pre-existing Symbol it has in the case that it already exists (in future browsers). I want my code to be ES5 strict compliant and portable to other projects.

Here's one way (of many) to do what I want to in ES3/ES5 non-strict:

(function() {

    // If Symbol already exists, we're done.
    if(typeof Symbol != 'undefined') return;

    // This becomes global because it wasn't declared with var
    Symbol = function() {
        // ...
    };

})();

However, it's not ES5 strict compliant because Symbol is not defined explicitly.

Other ways to accomplish this would involve accessing the window object (window.Symbol = ...), but this is no good either because I don't want my code to assume its running in a browser environment.

How can this be done in ES5 strict?

like image 984
Nathan Wall Avatar asked Oct 11 '12 22:10

Nathan Wall


3 Answers

The answers posted by other users led me to a similar StackOverflow question which gave me the right terms to search in Google to find my answer. The solution:

I eventually was able to solve this problem with the use of an indirect eval, described here.

Using an indirect eval, covered in detail in the article linked above, executes code in global scope, as per the ES5 spec. I have chosen to go with this approach because it conforms to the ES5 spec and it allows the code to literally be dropped anywhere, even inside another function by a package manager, and still find the global object (which the other answers provided could not do).

The solution is something along these lines:

(function() {

    'use strict';

    var _global = (0, eval)('this');

    // If Symbol is already defined, there's nothing to do.
    if(_global.Symbol) return;

    _global.Symbol = function() {
        // ...
    };

})();

The key is to use an indirect eval to retrieve the global object (this in the context of an indirect eval).

This should work in anything which is ES5 compliant, including modern browsers and non-browser environments, as I had wanted.

Thanks for all the help everyone!

The only caveat is that it does seem a little hackish to have to use eval (that's bad enough) in this indirect way (now its worse) in order to access the global object. Should a global identifier or some other method to access the global object not be in the spec?

like image 77
Nathan Wall Avatar answered Nov 14 '22 22:11

Nathan Wall


How about you pass in the global scope that you want Symbol to be added to?

(function(global){
 if(typeof global.Symbol != 'undefined') return;

    // This becomes global because it wasn't declared with var
    global.Symbol = function() {
        // ...
    };

})(window);  

this adds it to window, but could be some other scope or var.

like image 25
mrk Avatar answered Nov 14 '22 21:11

mrk


'use strict';

var Symbol = 1; // try to comment this line and run the script again

var Symbol = (function(Symbol) {


    if(typeof Symbol != 'undefined') return Symbol;

    Symbol = function() {
        // ...
    };

    return Symbol;

})(Symbol);

alert(typeof Symbol);

http://jsfiddle.net/f0t0n/yATJW/

'use strict';

(function(g) { // g is a global context (this passed)

    if(typeof g.Array != 'undefined') {
        return;
    }

    g.Array = function() {
        // ...
    };

    g.Array.prototype.foo = function() {
        console.log('bar');
    };
})(this);

console.log(this.Array);​

http://jsfiddle.net/f0t0n/prwaP/

like image 37
Eugene Naydenov Avatar answered Nov 14 '22 23:11

Eugene Naydenov