I want to create an object and hide some of its properties.
How do I do this?
For example, to this object:
console.log(new Path2D()); // Path2D {} empty*
In this image, the console is very crowded and confusing.
It depends why you want to do that, it's hard to give a precise answer without more details. In most cases there is no need to hide properties (what are you afraid of?), but here are two ways if you really need to:
you can use Symbol properties (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol), that is new Path2D() would return an object where the keys are not strings but Symbols. This has the advantage that a user of this object can add its own properties to the object without risking clashing with internal properties that your library relies on. It is still possible for someone to access those properties, though, if they really want to.
You can hide things in a local variable in a closure. It is much more hidden than with Symbol properties, but it might require thinking about the API differently.
Related
I want to let the user define certain variables without overriding existing ones. For example, an allowed new variable:
myJSON = 123
And an invalid one:
JSON = 123
Because it overrides the existing JSON object. Before adding I could simply check if the variable is different from undefined. But I would prefer to show the user which variables are already taken too.
How can I get a comprehensive list of the browser's built-in variables?
Note that Object.keys(window) and Object.keys(document) don't contain JSON. Which other places should I check to get a comprehensive list of taken variables?
So, originally, I thought that the limitation was that Object.keys enumerates only an object's own properties and not inherited ones. However, trying a simple for...in loop without a hasOwnProperty check failed to yield much more than Object.keys. I also tried looping over globalThis to no avail.
A bit of further research showed that JSON is part of the family of "Standard Built-In Objects". When researching that, I came upon a similar Stack Overflow question regarding how to list all standard built-in objects in Node, and the solution was to leverage getOwnPropertyNames. This proved to be the solution-- rather than getting ≈300 results, I instead got 1000+, among which were "JSON" and "Error".
const allWindowProperties = Object.getOwnPropertyNames(window);
console.log(allWindowProperties);
console.log(allWindowProperties.includes('JSON'));
console.log(allWindowProperties.includes('Error'));
So hopefully this solves your problem. My original comment, however, is still worth exploring: How has this use case manifested? Why is it a necessity to allow the user direct access to the runtime environment to declare variables at all, let alone in the global namespace? Perhaps you have a good reason, and I do think this is an interesting question, but I wonder if perhaps we have a classic X-Y problem here.
qpo.activeGame.teams.blue
qpo.blue
Above are two references/identifiers. I create an object and have the first identifier (qpo.activeGame.teams.blue) point to it. Then, I want to have the second identifier (qpo.blue) point to the same object (which will change, and both identifiers need to keep pointing to the newest version of that object.)
Do I achieve this like this?
qpo.blue = qpo.activeGame.teams.blue
Or, is it more complicated?
I apologize for my lack of a computer science background. (Is data binding even the right term for this?)
You cannot cause two variables to become "married" so that an assignment to one is effectively an assignment to the other. (There's one weirdness that could possibly be construed as that situation, but it's not something you'd ever do in practice.)
However, if two variables (or object properties) refer to the same object, then changes to the constituent properties and sub-properties of that object will be visible via either reference. That's because a reference to an object is, in fact, just a reference. Two variables sharing the same reference value both refer to the same single object.
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.
When I define an object I can reach all its properties by using the dot character and the same applies to built-in functions like String, Array, Math, etc.
But I cannot loop through them by using for(# in # for example. It says String is native code but still I can reach all its members albeit I cannot iterate through them.
I know window is iterable but their 'sub-functions' appear to be not.
Why is that? Is there a chance to call the properties without explicitly typing their names in? Can I list all its members somehow?
I am aware of that It does not look useful and no one would need it in production. I am asking it because I could not do it and I hope someone can give me some help.
You can get all names only of own(!!!) properties
Object.getOwnPropertyNames(YOUR_OBJECT)
It means, this method doesn't enumerate inherited properties. And if you want to enumerate inherited properties you can use YOUR_OBJECT.__proto__, but it works only in Mozilla
Are there some benefits of using the methods defined on the localStorage object versus accessing the object properties directly? For example, instead of:
var x = localStorage.getItem(key);
localStorage.setItem(key, data);
I have been doing this:
var x = localStorage[key];
localStorage[key] = data;
Is there anything wrong with this?
Not really, they are, basically, exactly the same. One uses encapsulation (getter/setter) to better protect the data and for simple usage. You're supposed to use this style (for security).
The other allows for better usage when names(keys) are unknown and for arrays and loops. Use .key() and .length to iterate through your storage items without knowing their actual key names.
I found this to be a great resource : http://diveintohtml5.info/storage.html
This question might provide more insight as well to some: HTML5 localStorage key order
Addendum:
Clearly there has been some confusion about encapsulation. Check out this quick Wikipedia. But seriously, I would hope users of this site know how to google.
Moving on, encapsulation is the idea that you are making little in and out portals for communication with another system. Say you are making an API package for others to use. Say you have an array of information in that API system that gets updated by user input. You could make users of your API directly put that information in the array... using the array[key] method. OR you could use encapsulation. Take the code that adds it to the array and wrap it in a function (say, a setArray() or setWhateverMakesSense() function) that the user of your API calls to add this type of information. Then, in this set function you can check the data for issues, you can add it to the array in the correct way, in case you need it pushed or shifted onto the array in a certain way...etc. you control how the input from the user gets into the actual program. So, by itself it does not add security, but allows for security to be written by you, the author of the API. This also allows for better versioning/updating as users of your API will not have to rewrite code if you decide to make internal changes. But this is inherent to good OOP anyhow. Basically, in Javascript, any function you write is a part of your API. People are often the author of an API and it's sole user. In this case, the question of whether or not to use the encapsulation functions is moot. Just do what you like best. Because only you will be using it.
(Therefore, in response to Natix's comment below...)
In the case here of JavaScript and the localStorage object, they have already written this API, they are the author, and we are its users. If the JavaScript authors decide to change how localStorage works, then it will be much less likely for you to have to rewrite your code if you used the encapsulation methods. But we all know its highly unlikely that this level of change will ever happen, at least not any time soon. And since the authors didn't have any inherent different safety checks to make here, then, currently, both these ways of using localStorage are essentially the same. Except when you try to get data that doesn't exist. The encapsulated getItem function will return null (instead of undefined). That is one reason that encapsulation is suggested to be used; for more predictable/uniform/safer/easier code. And using null also matches other languages. They don't like us using undefined, in general. Not that it actually matters anyhow, assuming your code is good it's all essentially the same. People tend to ignore many of the "suggestions" in JavaScript, lol! Anyhow, encapsulation (in JavaScript) is basically just a shim. However, if we want to do our own custom security/safety checks then we can easily either: write a second encapsulation around the localStorage encapsulate, or just overwrite/replace the existing encapsulation (shim) itself around localStorage. Because JavaScript is just that awesome.
PT
I think they are exactly the same, the only thing the documenation states is:
Note: Although the values can be set and read using the standard
JavaScript property access method, using the getItem and setItem
methods is recommended.
If using the full shim, however, it states that:
The use of methods localStorage.yourKey = yourValue; and delete
localStorage.yourKey; to set or delete a key is not a secure way with
this code.
and the limited shim:
The use of method localStorage.yourKey in order to get, set or delete
a key is not permitted with this code.
One of the biggest benefits I see is that I don't have to check if a value is undefined or not before I JSON.parse() it, since getItem() returns NULL as opposed to undefined.
As long as you don't use the "dot notation" like window.localStorage.key you are probably OK, as it is not available in Windows Phone 7. I haven't tested with brackets (your second example). Personally I always use the set and get functions (your first example).
Well, there is actually a difference, when there is no local storage available for an item:
localStorage.item returns undefined
localStorage.getItem('item') returns null
One popular use case may be when using JSON.parse() of the return value: the parsing fails for undefined, while it works for null