5

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?

4 Answers 4

4

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?

Sign up to request clarification or add additional context in comments.

1 Comment

It seems to be purposeful so "strict mode makes it impossible to accidentally create global variables" (Converting mistakes into errors) and because "exposing the global object in browsers is a security hazard". ("Securing" JavaScript) developer.mozilla.org/en-US/docs/JavaScript/Reference/…
1

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.

5 Comments

I wrote "global scope" but doesn't necessarily have to be a global variable, depending on where/how the wrapper is called.
I considered that, and it may be my best option, but it's not ideal because it requires an edit to the file for non-browser environments.
I'm quite familiar with AMD (the model requirejs uses), as I've written an implementation myself for work. How does that solve my problem? You just mean the ability to use exports? I want to write something that can literally be dropped anywhere. I don't want to depend on a module model.
Yep, I meant ability to use exports. You want Symbol to get defined somewhere and picked and used elsewhere. Creating global vars can be bad practice, so if you agree to follow some pattern/impl, like requirejs, then at least there's a documented, semi-standardish way you are following so others that may have to maintain the code don't need to noodle thru spaghetti code.
But I don't really want to define a global variable. I want to provide a shim for something that will be available in ES6 so that other libs can use it today. In ES6 they won't have to require it. As a shim, it shouldn't have to be required.
1
'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/

7 Comments

"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."
It's a nice attempt, but I don't think it really works due to var hoisting. Replace Symbol with something that really does already exist, like Array, to see what I mean: jsfiddle.net/6jhFr
This looks like an attractive possibility. I have a couple questions. (1) Is this really ES5-strict compliant? Will this by default be the global object in ES5 if it's run from the global scope? (2) Will this work in non-browser environments (like Node.js)? Does this in the global scope in Node refer to the global object? Is that behavior part of the ES5 spec? Thanks!
No, in Node this does not reffer to the global object. In node you can explicity use global in my example instead of window and this. In Node console.log(this, this === global, this === module); will output {} false false
I installed Node and tested it out. this === global is true for me. +1, though. Thanks for the help. Ultimately not the solution I chose, but it helped point me where in some good places.
|
1

Why does it need to be in an anonymous function?

// assuming global context if (typeof this.Symbol === 'undefined') { this.Symbol = function () { // ... }; } 

or in a function, pass this as described here

(function (t) { if (typeof t.Symbol === 'undefined') { t.Symbol = function () { // ... }; } })(this); 

1 Comment

+1, Thanks. The link you posted, and a couple Google searches which it inspired eventually led me to the answer I was looking for.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.