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?
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?
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.
'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/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With