What is 'global symbol registry'? - javascript

var sym = Symbol();
is window['sym'] which is already global scope.
But MDN says:
The above syntax using the Symbol() function will not create a global symbol that is available in your whole codebase. To create symbols available across files and in a global scope-like environment, use the methods Symbol.for() and Symbol.keyFor() to set and retrieve symbols from the global symbol registry.
sym is already in global scope in a browser, with above declaration syntax.
What is global symbol registry?
Each html document is tied with window object.
So,
In a browser world, How does this scope of symbol availability across files/realms different from global scope(window object)?

var sym = Symbol();
is creating a new property sym in dictionary(window), which is in global scope, where value can be accessed as window['sym'].
Well, no. It does create a symbol and assigns it to a local variable named sym. Only if you are executing this code in the global scope (which you usually wouldn't, for modularity) it does create a property on the global object of your realm (js environment). Notice that this global object is not always window like in web pages, it depends on your environment.
What is global symbol registry?
It's a registry (think: dictionary) for symbols that you can access via a string key. And "global" does in this case mean even more global than a global scope, the global symbol registry does span all realms of your engine. In a browser, the web page, an iframe, and web worker would all have their own realm with own global objects, but they could share symbols via this global registry.
And this sharing is exactly the purpose. If you'd otherwise put
var sym1 = Symbol("shared");
var sym2 = Symbol("shared");
in two places, then sym1 !== sym2. If you've got a shared object, using the symbols as property keys would create two different properties. If however you do
var sym1 = Symbol.for("shared");
var sym2 = Symbol.for("shared");
then sym1 === sym2 and when you use it you'll always get the same property.
See also Crossing realms with symbols on 2ality and Symbols and why they're awesome for more examples, including the well-known symbols which are similarly global.

The global symbol registry is just a convenient global repository for symbol instances. You could implement one yourself if you wanted to, but having such a repository built-in means that the runtime can use it as a place to publish symbol instances that have particular meaning for a given context.
In your own application, you can decide that some types of objects will have certain properties accessible via some symbol. All your code can find those symbols via Symbol.for():
var SPECIAL_PROPERTY = Symbol.for("mySpecialProperty");
// ...
var specialVal = someObject[SPECIAL_PROPERTY];
Because the registry is global, that works regardless of scope or compilation unit.
By making the registry part of the runtime, an environment like Node.js can use the symbol mechanism to extend objects without fear of causing problems for legacy code. Like, if Node wanted to make it such that you could find out how much memory an object used, they could invent a symbol, put it in the registry, and document the registry key. Any code could then use that:
var objectSize = myObject[Symbol.for("memory_use")];
(That's totally made up; it might make no sense at all for Node to do that particular thing.) Because of the way symbols work as property keys, code that doesn't know about that won't experience any weird issues should the objects it manipulates suddenly start carrying around that extra property.
(Of course, the namespace of the symbol registry itself is just a namespace, so collisions would have to be dealt with there in pretty much exactly the same way we deal with name collisions in the window object.)

Global symbol registry exists across all iframes in a window. (As symbols can't be passed across workers, there's no observable concept of it being identical across workers, barring the existence of sidechannel inspections, eg by memory probing.)
<script>
document.head.appendChild(document.createElement('iframe'))
.src=`javascript:
alert(parent.Symbol===Symbol) /*false*/
,alert(parent.Symbol.for('a') === Symbol.for('a')) //true
`
</script>
Symbol.for is not much different from implementing your own cache using an object reference. It's merely in-built and thus more convenient. Instead of Symbol.for('a'), you can simply do:
obj['a']? obj['a'] : obj['a'] = Symbol()
.and maintain a ref to obj.
In fact, since javascript does not provide an API to remove symbols in the global registry, its beneficial to do it the manual-caching way if you need to manually manage the memory of the registry.

Related

Global "window" object

The global window object stores properties specific to the environment (for example, window.innerHeight).
But if we print window to the console, we will see the properties highlighted in a different color.
These include Array, Object, and so on, although i thought they are internal objects.
I have a few questions:
What are these "implicit" properties?
Where do JS boundaries end and environment begin?
Can I refer to Array in Node.js, for example?
Thank you.
In the early days of Javascript, there was one master global symbol and everything that was available in the global scope whether it was part of the language itself or part of the browser environment was a property of that master global.
In the browser, that master global is the window object. Thus, you see many things on the window object that are available globally like the Array constructor.
I would guess that this is a side effect of that fact that Javascript was first designed to run in a browser and was not initially a completely stand-alone language with a stand-alone specification.
More recent revisions to the language specification are not adding some new things to the window object any more. For example, when you declare an ES6 class such as:
class Foo {
constructor(greeting) {
this.greeting = greeting;
}
}
You will not find Foo on the window object even though it may be globally available in the browser.
As others have said, the color difference in the debug output has to do with whether the symbols are enumerable or not which is a technical distinction on whether the property shows up in something like Object.keys() or a for/in loop. The property is still there and is accessible either way.

Truly safe `eval` using ES6 `Proxy` and `with`?

It's a well known fact that neither Javascript's eval keyword nor Function objects created from strings should ever for any reason be used to run untrusted code.
However, I'm wondering if ES6 proxies change that. Consider:
let env = {eval: eval};
let proxy = new Proxy(env, { has: () => true });
with(proxy) {eval('...')}
The proxy object pretends to have all possible properties, which means that it blocks the search of higher scopes. Within the with block, any properties not set on env appear undefined, and any global properties set inside the with block are actually set on env.
This seems to allow me to set up a completely controlled and isolated environment for the evaled code to run in. What are the risks?
Here are a few concerns I can see:
Don't put anything that references window, or document, or localStorage, or anything else sensitive, into env.
Don't put any mutable object into env unless you're ok with untrusted code mutating it.
Solution: make deep copies if necessary.
Code inside the with block has no access to anything outside it. If it needs things like Math, Object, or String, they have to be put in env - which means these can be modified by malicious code. Even the eval function in my minimal example above can be modified.
Solution: Create proxies for these objects to white-list read-only access to specific properties.
As long as you follow these guidelines, is this actually safe? Are there other concerns?
It is quite easy to break out of this environment, via a number of different ways, some or all of which might possibly be mitigated:
Object, Array, and RegExp literals ({ }, [ ], and /.../) are unimpeded by the Proxy and allow access to (and mutation of) Object.protoype, Array.prototype, and RegExp.prototype. You can, however, lock these with Object.freeze before running your eval.
You must delete env.eval within your evaled string, or else the script can execute global code by renaming the eval function like globalEval = eval;
You cannot prevent the creation of new functions, which may use a global this object: (function () { this.globalFunc(); })(). Possibly enforcing strict mode by appending "use strict"; to your evaled input could eliminate this escape vector.
Any access to the Function constructor (via (a=>a).__proto__.constructor) allows execution of global code. You can delete Function.constructor to prevent this, but there may be other ways to access Function.

Symbol.for(string) in ECMAScript 6

It took me a while but I finally figured out what the purpose of symbols in ECMAScript 6 is: avoiding name collision when attaching properties to shared objects - HTML elements e.g. (In case you're stuck on the same question, I recommend this article.)
But then I stumbled upon Symbol.for(). Apparently ECMAScript 6 will maintain a global symbol registry which you can query with this function by providing the symbol description. Come again? If I use symbols to avoid name collisions, why would I want code other than my own to use them? (*) And how would I avoid name collisions in that global registry? Sharing of symbols seems to completely subvert the concept and a global registry doubly so.
(*) Yes, I know symbols aren't truly private, but that's besides the point.
If you don't want your symbols to be available in GlobalSymbolRegistry, just don't use Symbol.for.
Only use it if you want to allow other codes to use your symbol.
In the following example, I create a symbol to store data in DOM elements. And I may want every other code (e.g. internal raw uncompiled handlers) to read that data. So I make the symbol globally available.
var sym = Symbol.for('storeDataInDOM');
document.querySelector('button')[sym] = 'Hello, world!';
<button onclick="alert(this[Symbol.for('storeDataInDOM')])">Click me</button>
It's like creating global variables: should be avoided in general, but has its advantages. But with symbols instead of strings.
If I use symbols to avoid name collisions, why would I want code other than my own to use them?
That's not the only use case of symbols. The two most important other ones are:
they don't collide with string-keyed properties
they are not enumerated by the usual mechanics
Sharing of symbols seems to completely subvert the concept and a global registry doubly so.
Not necessarily. Right from that article you read: "The registry is useful when multiple web pages, or multiple modules within the same web page, need to share a symbol." The best example for these are the intrinsic symbols - they guarantee interoperability across realms, that's why the global symbol registry is more global than your global scope.
For example you might have a library that is loaded in a web page, an iframe and a web worker. If you share data between those environments (realms), all of the three instances of your library would want to use the same symbol.
There also is a real need interoperability between different libraries, which might not even know about each other. Good examples are transducers, algebraic structures or promises. Would ES6 already be in use, all of these would have agreed on common names in the global symbol registry, instead of relying on strings like these or the then method.
Another good example would be custom hooks defined by your engine, e.g. a Symbol.inspect = Symbol.for("inspect") that you can use to define custom stringification behavior to be used by console.log. Admittedly, that symbol does not necessarily need to be made available through the global symbol registry, it could as well be put on that specific library object (e.g. console.inspect = Symbole("console.inspect")).
And how would I avoid name collisions in that global registry?
Just like you previously did with properties, or global module objects: by using very long, very descriptive names - or by good faith. Also there are some naming conventions.
I invented the most useful feature of Symbol.for() call. If there is using symbols in your code sometimes it is difficult to use conditional breakpoints while debugging. For example, you need to catch if the variable equals the value which is of symbol type and this value binded in the different module. The first difficult way is to use this value as a constant and export it from that module. In this case, the condition of the breakpoint will look:
catchedVariable === exportedSymbolConst
But the easiest way is to temporarily change the code inside the module adding .for to Symbol. Then you can write the condition:
catchedVariable === Symbol.for('string_key')
After the successful debugging you will be changing the code back just removing .for part.

Nodejs global variables and modules

Are there any security issuses or something with using global variables and assignig modules to global variables in nodejs?
example:
client = new Client();
Without the var statement, so i can use it everywhere?
It's not that it's a security problem, it's considered bad practice though and it can lead to other problems such as trying to override a Node.js global variable.
Try not to pollute the global namespace and if you really really need global variables, just use one with sub-objects.
I don't think there are security issues per se, but you will be polluting the global namespace. As your project grows (especially with more than one programmer) the risk of conflicts gets bigger. And what if you later on add a third party library to your project that has a variable named 'client'?
I've been using Node for a couple of years and I had the same "problem" you have and is frustrating. Nevertheless I can give you the solution I reached and it works for me.
Node doesn't allows global variables in the way you ask since variables defined as global in a module are global only for that module. But exists a GLOBAL object that ban be used for what you need.
Global variables are a bad idea in general (always), but having a global cache of useful functions in it's own namespace is not a crime at all since it will not override anything and lets you use it along your code. So I'll tell you what I do to share functions and objects between modules and keep source organized (that's important for me at least!):
1st Create a resource file where you place all important functions and objects you want to share across your code. I call mine "R.js", R from Resources
2st Define the "R" object that will store all functions and objects and assign it to node's GLOBAL object:
var R = {};
GLOBAL.R = R; // Adds resource module to global
3rd For sake of simplicity and avoid extra requires all arround the code, I do all needed requires inside R.js. After that you only need to use them with R.require_variable.function_or_property
// All needed requires
R.fs = require('fs');
R.net = require('net');
R.http = require('http');
R.dbClient = require('mysql').Client;
...
4th Create the shared functions, variables and objects you like inside the R object
5th Where needed arround your code, require the R.js file and access to it's member by using the R object
R.<property_or_function>
2 warning notes.
Remember to always call shared functions or user shared objects placind "R." in front of it
Althought you can assign new functions, objects and properties to R object anywhere this can lead to the same inconsistencies you would have with global variables if you don't plan it in advance - i.e. you call a shared function before assigning it to R - so as a methodology, create everything in the R.js file instead of doing it all arround your code. This way all shared things will be in R.js and that is the only place to look for shared code.

Firefox extension JSMs and namespace ettiquite

So in Firefox extensions it's encouraged for your extension's objects to live in sub-objects like com.contoso.myExtension . That way you have not put any objects in the global namespace and extensions generally stay out of each other's hair. (At least in the common browser.xul window)
But from what I have understand about Javascript code modules (JSMs), is that while the module itself is working in a separate namespace, the symbols that it exports will end up in the global namespace of whatever code imports it. Furthermore, it's impossible for an extension to be "nice" and only try to build sub-objects; those exported symbols will just whack whatever global variables already existed. Also you can't export a symbol like com.contoso.myExtension. It's only a simple global variable.
So what's the protocol for playing nice when using JSMs? Just make really long variable names and hope they won't collide?
First off, I haven't seen a hard a true standard for how to handle this. But we can definitely do much better than just long variable names...
You are correct about the Javascript Code Modules living in a separate namespace (so to speak), however when you import them, you don't have to import them into the global namespace. If you look at the Components.utils.import documentation, you see that you can import onto a specific scope. That is, you don't have to pollute the global namespace at all.
You can collect your modules into a myExtension namespace.
var myExtension = {};
Components.utils.import("resource://.../module.jsm", myExtension);
And wrapping this inside a self-executing function doesn't let any variables leak into the global namespace, not even myExtension!
(function(){
var myExtension = {};
Components.utils.import("resource://.../module.jsm", myExtension);
})();

Categories

Resources