Why not just check for properties instead of using ES6 Symbols - javascript

From what I've read ES6 symbols "only use is to avoid name clashes between properties"..
If this is the case and I wanted to check for name collisions on an object, why not just use a simple function to check, and then use a different property name if the check returns true? Why the introduction to an entire new data type ? (I'm sure I'm naive in asking this, but I don't understand)
Example:
var obj = {
prop1: "some prop",
prop2: "some prop",
prop3: "some prop"
}
function propChecker(propName) {
if (propName in obj) {
console.log("Pick a different property");
}else{
console.log("Property name is available");
}
}
In this link the author of the accepted answer says "EcmaScript itself can now introduce extension hooks via certain methods you can put on objects (e.g. to define their iteration protocol) without running the risk of clashing with user names."
I am curious what this means in more simple-speak.
I'm guessing the "real" use of symbols is that of a tool to help those connected to the standards body implement new features without breaking preexisting code.
[1]: https://stackoverflow.com/questions/21724326/why-bring-symbols-to-javascript

Why not just use a simple function to check, and then use a different property name if the check returns true?
Because that would mean everyone needed to do these checks. And there needed to be a protocol on how to use a different property name.
Also, one needs to assume that existing code already uses all possible property names. If you want to introduce a new, standard method name to use in an iteration protocol, which one could you choose? There is no name that you can pick where it is assured that it doesn't collide with anything.
I'm guessing the "real" use of symbols is that of a tool to help those connected to the standards body implement new features without breaking preexisting code.
No, you don't have to be connected to the standards body to use symbols. Everyone can introduce his own property names that don't clash with other code. They're useful for all kinds of libraries that need to work with arbitrary objects and don't want to break encapsulation.

Related

What's the purpose of Symbol in terms of unique object identifiers?

Notes about 'Not a duplicate':
I've been told this is a duplicate of What is the use of Symbol in javascript ECMAScript 6?. Well, it doesn't seem right to me. The code they've given is this:
const door = {};
// library 1
const cake1 = Symbol('cake');
door[cake1] = () => console.log('chocolate');
// library 2
const cake2 = Symbol('cake');
door[cake2] = () => console.log('vanilla');
// your code
door[cake1]();
door[cake2]();
The only thing that makes this work is because cake1 and cake2 are different (unique) names. But the developer has explicitly given these; there is nothing offered by Symbol which helps here.
For example if you change cake1 and cake2 to cake and run it, it will error:
Uncaught SyntaxError: Identifier 'cake' has already been declared
If you're already having to manually come up with unique identifiers then how is Symbol helping?
If you execute this in your console:
Symbol('cake') === Symbol('cake');
It evaluates to false. So they're unique. But in order to actually use them, you're now having to come up with 2 key names (cake1 and cake2) which are unique. This has to be done manually by the developer; there's nothing in Symbol or JavaScript in general which will help with that. You're basically creating a unique identifier using Symbol but then having to assign it manually to...a unique identifier that you've had to come up with as a developer.
With regards to the linked post they cite this as an example which does not use Symbol:
const door = {};
// from library 1
door.cake = () => console.log('chocolate');
// from library 2
door.cake = () => console.log('vanilla');
// your code
door.cake();
They try to claim this is a problem and will only log "vanilla". Well clearly that's because door.cake isn't unique (it's declared twice). The "fix" is as simple as using cake1 and cake2:
door.cake1 = () => console.log('chocolate');
door.cake2 = () => console.log('vanilla');
door.cake1(); // Outputs "chocolate"
door.cake2(); // Outputs "vanilla"
That will now work and log both "chocolate" and "vanilla". In this case Symbol hasn't been used at all, and indeed has no bearing on that working. It's simply a case that the developer has assigned a unique identifier but they have done this manually and without using Symbol.
Original question:
I'm taking a course in JavaScript and the presenter is discussing Symbol.
At the beginning of the video he says:
The thing about Symbol's is that every single one is unique and this makes them very valuable in terms of things like object property identifiers.
However he then goes on to say:
They are not enumerable in for...in loops.
They cannot be used in JSON.stringify. (It results in an empty object).
In the case of point (2) he gives this example:
console.log(JSON.stringify({key: 'prop'})); // object without Symbol
console.log(JSON.stringify({Symbol('sym1'): 'prop'})); // object using Symbol
This logs {"key": "prop"} and {} to the console respectively.
How does any of this make Symbol "valuable" in terms of being unique object keys or identifiers?
In my experience two very common things you'd want to do with an object is enumerate it, or convert the data in them to JSON to send via ajax or some such method.
I can't understand what the purpose of Symbol is at all, but especially why you would want to use them for making object identifiers? Given it will cause things later that you cannot do.
Edit - the following was part of the original question - but is a minor issue in comparison to the actual purpose of Symbol with respect to unique identifiers:
If you needed to send something like {Symbol('sym1'): 'prop'} to a backend via ajax what would you actually need to do in this case?
I replied to your comment in the other question, but since this is open I'll try to elaborate.
You are getting variable names mixed up with Symbols, which are unrelated to one another.
The variable name is just an identifier to reference a value. If I create a variable and then set it to something else, both of those refer to the same value (or in the case of non-primitives in JavaScript, the same reference).
In that case, I can do something like:
const a = Symbol('a');
const b = a;
console.log(a === b); // true
That's because there is only 1 Symbol created and the reference to that Symbol is assigned to both a and b. That isn't what you would use Symbols for.
Symbols are meant to provide unique keys which are not the same as a variable name. Keys are used in objects (or similar). I think the simplicity of the other example may be causing the confusion.
Let us imagine a more complex example. Say I have a program that lets you create an address book of people. I am going to store each person in an object.
const addressBook = {};
const addPerson = ({ name, ...data }) => {
addressBook[name] = data;
};
const listOfPeople = [];
// new user is added in the UI
const newPerson = getPersonFromUserEntry();
listOfPeople.push(newPerson.name);
addPerson(newPerson);
In this case, I would use listOfPeople to display a list and when you click it, it would show the information for that user.
Now, the problem is, since I'm using the person's name, that isn't truly unique. If I have two "Bob Smith"'s added, the second will override the first and clicking the UI from "listOfPeople" will take you to the same one for both.
Now, instead of doing that, lets use a Symbol in the addPerson() and return that and store it in listOfPeople.
const addressBook = {};
const addPerson = ({ name, ...data }) => {
const symbol = Symbol(name);
addressBook[symbol] = data;
return symbol;
};
const listOfPeople = [];
// new user is added in the UI
const newPerson = getPersonFromUserEntry();
listOfPeople.push(addPerson(newPerson));
Now, every entry in listOfPeople is totally unique. If you click the first "Bob Smith" and use that symbol to look him up you'll get the right one. Ditto for the second. They are unique even though the base of the key is the same.
As I mentioned in the other answer, the use-case for Symbol is actually fairly narrow. It is really only when you need to create a key you know will be wholly unique.
Another scenario where you might use it is if you have multiple independent libraries adding code to a common place. For example, the global window object.
If my library exports something to window named "getData" and someone has a library that also exports a "getData" one of us is going to override the other if they are loaded at the same time (whoever is loaded last).
However, if I want to be safer, instead of doing:
window.getData = () => {};
I can instead create a Symbol (whose reference I keep track of) and then call my getData() with the symbol:
window[getDataSymbol]();
I can even export that Symbol to users of my library so they can use that to call it instead.
(Note, all of the above would be fairly poor naming, but again, just an example.)
Also, as someone mentioned in the comments, these Symbols are not for sharing between systems. If I call Symbol('a') that is totally unique to my system. I can't share it with anyone else. If you need to share between systems you have to make sure you are enforcing key uniqueness.
As a very practical example what kind of problem Symbols solve, take angularjs's use of $ and $$:
AngularJS Prefixes $ and $$: To prevent accidental name collisions with your code, AngularJS prefixes names of public objects with $ and names of private objects with $$. Please do not use the $ or $$ prefix in your code.
https://docs.angularjs.org/api
You'll sometimes have to deal with objects that are "yours", but that Angular adds its own $ and $$ prefixed properties to, simply as a necessity for tracking certain states. The $ are meant for public use, but the $$ you're not supposed to touch. If you want to serialise your objects to JSON or such, you need to use Angular's provided functions which strip out the $-prefixed properties, or you need to otherwise be aware of dealing with those properties correctly.
This would be a perfect case for Symbols. Instead of adding public properties to objects which are merely differentiated by a naming convention, Symbols allow you to add truly private properties which only your code can access and which don't interfere with anything else. In practice Angular would define a Symbol once somewhere which it shares across all its modules, e.g.:
export const PRIVATE_PREFIX = Symbol('$$');
Any other module now imports it:
import { PRIVATE_PREFIX } from 'globals';
function foo(userDataObject) {
userDataObject[PRIVATE_PREFIX] = { foo: 'bar' };
}
It can now safely add properties to any and all objects without worrying about name clashes and without having to advise the user about such things, and the user doesn't need to worry about Angular adding any of its own properties since they won't show up anywhere. Only code which has access to the PRIVATE_PREFIX constant can access these properties at all, and if that constant is properly scoped, that's only Angular-related code.
Any other library or code could also add its own Symbol('$$') to the same object, and it would still not clash because they're different symbols. That's the point of Symbols being unique.
(Note that this Angular use is hypothetical, I'm just using its use of $$ as a starting point to illustrate the issue. It doesn't mean Angular actually does this in any way.)
To expand on #samanime's excellent answer, I'd just like to really put emphasis on how Symbols are most commonly used by real developers.
Symbols prevent key name collision on objects.
Let's inspect the following page from MDN on Symbols. Under "Properties", you can see some built-in Symbols. We'll look at the first one, Symbol.iterator.
Imagine for a second that you're designing a language like JavaScript. You've added special syntax like for..of and would like to allow developers to define their own behavior when their special object or class is iterated over using this syntax. Perhaps for..of could check for a special function defined on the object/class, named iterator:
const myObject = {
iterator: function() {
console.log("I'm being iterated over!");
}
};
However, this presents a problem. What if some developer, for whatever reason, happens to name their own function property iterator:
const myObject = {
iterator: function() {
//Iterate over and modify a bunch of data
}
};
Clearly this iterator function is only meant to be called to perform some data manipulation, probably very infrequently. And yet if some consumer of this library were to think myObject is iterable and use for..of on it, JavaScript will go right ahead and call that function, thinking it's supposed to return an iterator.
This is called a name collision and even if you tell every developer very firmly "don't name your object properties iterator unless it returns a proper iterator!", someone is bound to not listen and cause problems.
Even if you don't think just that one example is worthy of this whole Symbol thing, just look at the rest of the list of well-known symbols. replace, match, search, hasInstance, toPrimitive... So many possible collisions! Even if every developer is made to never use these as keys on their objects, you're really restricting the set of usable key names and therefore developer freedom to implement things how they want.
Symbols are the perfect solution for this. Take the above example, but now JavaScript doesn't check for a property named "iterator", but instead for a property with a key exactly equal to the unique Symbol Symbol.iterator. A developer wishing to implement their own iterator function writes it like this:
const myObject = {
[Symbol.iterator]: function() {
console.log("I'm being iterated over!");
}
};
...and a developer wishing to simply not be bothered and use their own property named iterator can do so completely freely without any possible hiccups.
This is a pattern developers of libraries may implement for any unique key they'd like to check for on an object, the same way the JavaScript developers have done it. This way, the problem of name collisions and needing to restrict the valid namespace for properties is completely solved.
Comment from the asker:
The bit which confused me on the linked OP is they've created 2 variables with the names cake1 and cake2. These names are unique and the developer has had to determine them so I didn't understand why they couldn't assign the variable to the same name, as a string (const cake1 = 'cake1'; const cake2 = 'cake2'). This could be used to make 2 unique key names since the strings 'cake1' !== 'cake2'. Also the answer says for Symbol you "can't share it" (e.g. between libraries) so what use is that in terms of avoiding conflict with other libraries or other developers code?
The linked OP I think is misleading - it seems the point was supposed to be that both symbols have the value "cake" and thus you technically have two duplicate property keys with the name "cake" on the object which normally isn't possible. However, in practice the capability for symbols to contain values is not really useful. I understand your confusion there, again, I think it was just another example of avoiding key name collision.
About the libraries, when a library is published, it doesn't publish the value generated for the symbol at runtime, it publishes code which, when added to your project, generates a completely unique symbol different than what the developers of the library had. However, this means nothing to users of the library. The point is that you can't save the value of a symbol, transfer it to another machine, and expect that symbol reference to work when running the same code. To reiterate, a library has code to create a symbol, it doesn't export the generated value of any symbols.
What's the purpose of Symbol in terms of unique object identifiers?
Well,
Symbol( 'description' ) !== Symbol( 'description' )
How does any of this make Symbol "valuable" in terms of being unique object keys or identifiers?
In a visitor pattern or chain of responsibility, some logic may add additional metadata to any object and that's it (imagine some validation OR ORM metadata) attached to objects but that does not persist *.
If you needed to send something like {Symbol('sym1'): 'prop'} to a backend via ajax what would you actually need to do in this case?
If I may assure you, you won't need to do that. you would consider { sym1: 'prop' } instead.
Now, this page even has a note about it
Note: If you are familiar with Ruby's (or another language) that also has a feature called "symbols", please don’t be misguided. JavaScript symbols are different.
As I said, there are useful for runtime metadata and not effective data.

Codacy secure error [duplicate]

I'm currently developing a little game in Javascript and I'm using Codacy to review my code and help me cleaning it.
One of the most seen error is Generic Object Injection Sink (security/detect-object-injection).
It happens when I'm trying to access a value in an array using a variable. Like in this example :
function getValString(value)
{
var values = ["Mis&eacuterable", "Acceptable", "Excellente", "Divine"];
return values[value];
}
This function is used to display on screen the value's string of an item. It receives a "value" which can be 0, 1, 2 or 3 and returns the string of the value.
Now here's my problem :
Codacy is telling me that use of var[var] should be prohibited because it causes security issues and since I'm rather new to Javascript, I was wondering why and what are the good practices in that kind of situation.
The security issue present here is that the stringified value of value may be accessing a property that is inherited from the Object's __proto__ hierarchical prototype, and not an actual property of the object itself.
For example, consider the scenario when value is a string literal of "constructor".
const property = "constructor";
const object = [];
const value = object[property];
The result of value in this context will resolve to the Array() function - which is inherited as part of the Object's prototype, not an actual property of the object variable. Furthermore, the object being accessed may have overridden any of the default inherited Object.prototype properties, potentially for malicious purposes.
This behavior can be partially prevented by doing a object.hasOwnProperty(property) conditional check to ensure the object actually has this property. For example:
const property = "constructor";
const object = [];
if (object.hasOwnProperty(property)) {
const value = object[property];
}
Note that if we suspect the object being accessed might be malicious or overridden the hasOwnProperty method, it may be necessary to use the Object hasOwnProperty inherited from the prototype directly: Object.prototype.hasOwnProperty.call(object, property)
Of course, this assumes that our Object.prototype has not already been tampered with.
This is not necessarily the full picture, but it does demonstrate a point.
Check out the following resources which elaborates in more detail why this is an issue and some alternative solutions:
https://github.com/nodesecurity/eslint-plugin-security/blob/master/docs/the-dangers-of-square-bracket-notation.md
Securely set unknown property (mitigate square bracket object injection attacks) utility function
By itself this is not a bad practice, because you do want to develop a system and make it secure. It's difficult to imagine a higher security risk to a system than one which causes the nonexistence of that system.
Yet, not being allowed to use a variable to dynamically create/use/update an index practically reduces your options of hard-coding any indexes that you may use to refer items of an array or members of an object.
Not allowing indexes greatly reduces your options, so much that it threatens with the nonexistence of any system that you may want to create in Javascript. Let's see some of the use-cases:
Numbered loops:
for (let index = 0; index < arr.length; index++) {
//do whatever with arr[index]
}
Of course, this is true to while loops as well.
in loops
for (let index in variable) {
//do whatever with arr[index]
}
of loops
for (let item of variable) {
// do whatever with item
}
see
finding dynamically a value
This is virtually used in quasi infinitely many ways, all the examples above are specific cases of this. Example:
function getItem(arr, index) {
return arr[index];
}
summary
The fear of exploits because of dynamic indexing is the equivalent of the fear from a meteor hitting into the exact place and the exact time one is in. Of course, we cannot exclude it, but one can not live in constant fear of low-chanced catastrophes. Similarly, programming is also impossible with unreasonable, paranoid fears. So, instead of rejecting dynamic indexing altogether because of the possibility of the exploits, we must refer to the actual exploits that could be possible. If we are not allowed to use dynamic instances, then whatever system we are to develop, if it's not simple as pie, will not come to existence. So, whatever threats we are afraid of should be protected against otherwise.
Example: You retrieve values from a data-source and have a field for credit card IBAN. Yeah, if that's shown to a user who is not the owner, that's a high risk. But you should protect against this by making IBAN unavailable by the mere of a use of an index by external sources, such as POST requests sent by the browser of a user.

Are methods with a "method could be static" warning considered bad form in JavaScript/TypeScript?

I have noticed that if I write a method that only acts on local variables of calling methods (does not directly interact with a class variable) then I get a warning that the method can be static.
Sometimes it's nice to abstract a large piece of code into a separate method. Is this somehow consider bad practice in JavaScript/TypeScript?
Since I keep getting these warnings I'm taking a chance that the post police will pounce on me for asking a question that could invite opinions. Let me defend against that by saying that the warnings I'm getting are not opinions. They are definite warnings. That suggests that there exists an answer that is not opinion, at least from the perspective of the people who decided to create those warnings.
EDIT:
I was asked to put code here to provide a valid reason as to why I would want to do this. I personally don't think this adds any clarity to question I asked but here's an example of a method that was producing the warning in WebStorm.
//Change object array in *.content objects to values array
//noinspection JSMethodCanBeStatic
transformData(visibleData) {
const ret: any = {};
ret.headings = visibleData.headings;
ret.checkbox = this.checkBox; //add if the table needs checkboxes
ret.content = [];
for (let i = 0; i < visibleData.content.length; i++) {
ret.content.push(_.values(visibleData.content[i]));
}
return ret;
}
The point of this function is to take a clone of the instance, so as not to modify the instance itself, and create a different object, mainly a values only array, which will be used to databind in the template. I did this so my table template could be reusable since columns vary in number.
Code which does not interact with instance should be a free function or at least a static method of a class when you believe it is tightly coupled with class API.
Every additional method you add to API has to be supported. So larger classes require more support than smaller classes. In some languages, like Java, you can't have free function, so you have to attach function to some class, but TypeScript and JS are more flexible, so there is no need to pollute class API.
And you could consider it from the performance optimization point of view.
When JS evaluates class method it looks it up in the object instance, than in the object prototype, than in the parent's prototype, etc. Every lookup eats cpu time, so when you think about performance, free function is your choice.

Can an object be undefined but yet have a defined property? (Javascript)

I have a slightly embarrassing question relating to a simple Javascript function I have come across:
Passport.prototype.use = function(name, strategy) {
if (!strategy) {
strategy = name;
name = strategy.name;
}
if (!name) throw new Error('authentication strategies must have a name');
this._strategies[name] = strategy;
return this;
};
I believe the purpose of this function is to give a strategy a name, overriding a default name it may have.
I believe the first part of the function is essentially assigning strategy.name to name, given the case that strategy is undefined if(!strategy){}. This does not feel intuitive to me. How can strategy.name be defined if this code is only ran if strategy is not defined? Ie. can an undefined object have a defined property—or am I looking at this incorrectly?
As a side note—I've been scouring the web trying to figure out the use of the _ throughout javascript. I know the underscore.js library is pretty popular, but that library hasn't been loaded so this underscore must signify something else.
Anyways, any help is appreciated. Thanks!
For organization sake's, let's formalize the comments section (btw, why do so many people answer via comments nowadays?)
Argument overloading
This is (unfortunately) quite common in Javascript. It's a way to provide two interfaces with the same function. In my opinion, this generally leads to confusion.
Nonetheless, in this case, the author wants to offer two signatures:
Prototype.use(strategy: Object)
and
Prototype.use(name: String, strategy: String)
Allowing a caller to either:
Passport.use("name", "strategy");
or
Passport.use({ "name": "name" });
Therefore, if the second argument is falsy (if (!strategy)) then use the first argument instead (strategy = name;).
"""Private""" variables
Javascript lacks "private" variables (except for closures) so using an underscore to prefix your _property indicates that it should not be accessed by external code, i.e. "use at your own peril, may break".

Javascript: why Object.keys(someobject), rather than someobject.keys?

I frequently get an array of an objects keys using:
Object.keys(someobject)
I'm comfortable doing this. I understand that Object is the Object constructor function, and keys() is a method of it, and that keys() will return a list of keys on whatever object is given as the first parameter. My question is not how to get the keys of an object - please do not reply with non-answers explaining this.
My question is, why isn't there a more predictable keys() or getKeys() method, or keys instance variable available on Object.prototype, so I can have:
someobject.keys()
or as an instance variable:
someobject.keys
And return the array of keys?
Again, my intention is to understand the design of Javascript, and what purpose the somewhat unintuitive mechanism of fetching keys serves. I don't need help getting keys.
I suppose they don't want too many properties on Object.prototype since your own properties could shadow them.
The more they include, the greater the chance for conflict.
It would be very clumsy to get the keys for this object if keys was on the prototype...
var myObj: {
keys: ["j498fhfhdl89", "1084jnmzbhgi84", "jf03jbbop021gd"]
};
var keys = Object.prototype.keys.call(myObj);
An example of how introducing potentially shadowed properties can break code.
There seems to be some confusion as to why it's a big deal to add new properties to Object.prototype.
It's not at all difficult to conceive of a bit of code in existence that looks like this...
if (someObject.keys) {
someObject.keys.push("new value")
else
someObject.keys = ["initial value"]
Clearly this code would break if you add a keys function to Object.prototype. The fact that someObject.keys would now be a shadowing property breaks the code that is written to assume that it is not a shadowing property.
Hindsight is 20/20
If you're wondering why keys wasn't part of the original language, so that people would at least be accustomed to coding around it... well I guess they didn't find it necessary, or simply didn't think of it.
There are many possible methods and syntax features that aren't included in the language. That's why we have revisions to the specification, in order to add new features. For example, Array.prototype.forEach is a late addition. But they could add it to Array.prototype, because it doesn't break proper uses of Array.
It's not a realistic expectation that a language should include every possible feature in its 1.0 release.
Since Object.keys does nothing more than return an Array of an Object's enumerable own properties, it's a non-essential addition, that could be achieved with existing language features. It should be no surprise that it wasn't present earlier.
Conclusion
Adding keys to Object.prototype most certainly would break legacy code.
In a tremendously popular language like JavaScript, backward compatibility is most certainly going to be an important consideration. Adding new properties to Object.prototype at this point could prove to be disastrous.
I guess an answer to your question is "Because the committee decided so", but I can already hear you ask "why?" before the end of this sentence.
I read about this recently but I can't find the source right now. What it boiled down to was that in many cases you had to use Object.prototype.keys.call(myobject) anyway, because the likelihood of myobject.keys already being used in the object for something else.
I think you will find this archived mail thread interesting, where for example Brendan Eich discuss some aspects of the new methods in ECMAScript 5.
Update:
While digging in the mail-archive I found this:
Topic: Should Object.keys be repositioned as Object.prototype.keys
Discussion: Allen argued that this isn't really a meta layer operation
as it is intended for use in application layer code as an alternative
to for..in for getting a list of enumerable property names. As a
application layer method it belongs on Object.prototype rather than on
the Object constructor. There was general agreement in principle, but
it was pragmatically argued by Doug and Mark that it is too likely
that a user defined object would define its own property named "keys"
which would shadow Object.prototype.keys making it inaccessible for
use on such objects.
Action: Leave it as Object.keys.
Feel free to make your own, but the more properties you add to the Object prototype, the higher chance you'll collide. These collisions will most likely break any third party javascript library, and any code that relies on a for...in loop.
Object.prototype.keys = function () {
return Object.keys(this);
};
for (var key in {}) {
// now i'm getting 'keys' in here, wtf?
}
var something = {
keys: 'foo'
};
something.keys(); // error

Categories

Resources