What is the use of Symbol in javascript ECMAScript 6? [duplicate] - javascript

This question already has answers here:
What is the motivation for bringing Symbols to ES6?
(7 answers)
What is the point of the 'Symbol' type in ECMA-262-v6?
(2 answers)
Closed 4 years ago.
What is the use of Symbol in javascript (ECMASCRIPT6)?
Why does the example below return false?
const symbol1 = Symbol();
console.log(Symbol('foo') === Symbol('foo'));
// expected output: false

Symbol is used to create a totally unique, one-of-a-kind identifier. It's use is precisely for the example you list.
Even if you call Symbol with the same string, the instances will be different. This allows different libraries (which may be used at the same time) to define keys which may be used at the same time.
For example, imagine two libraries using a common name to define something on window or global (or for illustration, the fake global door):
const door = {};
// from library 1
door.cake = () => console.log('chocolate');
// from library 2
door.cake = () => console.log('vanilla');
// your code
door.cake();
In this example, the first libraries code is lost because it was unintentionally given the same name as the first.
Now, if they both use Symbol, then even if they are named the same, you can still access both (assuming they export Symbol somehow):
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]();
Both are still accessible.
That is a bit of an oversimplification, but it illustrated the point.
In a more practical usage, these are used for things such as importing modules. The modules may end up with the same name, but that's okay because they'll have unique symbols associated with them, which makes them uniquely accessible as long as your have the Symbol objects.
As for when to use them yourself... it's probably going to be pretty rare. You'll mainly want to use them any time you have a way to provide the Symbol but need other things to remain unique. I've only used these directly in a few narrow circumstances where the created element may end up the same.
For example, if you were making an object using names as the key, you might have duplicate names. Without symbols, the objects would override one other. With symbols, they'll all remain.
const people = {};
people[Symbol('bob')] = { name: 'Bob Smith' };
people[Symbol('bob')] = { name: 'Bob Jones' };

From the documentation:
Every symbol value returned from Symbol() is unique.
That means === comparisons will fail because they're not identical.
If you want a unique identifier of some sort that can be given a descriptive, if otherwise irrelevant name, then Symbol might be useful.

The Idea of the symbol is to introduce private properties into Javascript. But, its actual purpose is for name collision.
Unfortunately, however, they ended up being severely downgraded, and not private after all, because you can find them via reflection. Specifically, via the Object.getOwnPropertySymbols method and through proxies.
Every symbol value returned from Symbol() is unique. A symbol value may be used as an identifier for object properties; this is the data type's only purpose. (as per mozilla)
var Pet = (function() {
var typeSymbol = Symbol('type');
function Pet(type) {
this[typeSymbol] = type;
}
Pet.prototype.getType = function(){
return this[typeSymbol];
}
return Pet;
}());
var a = new Pet('dog');
console.log(a.getType()); // prints dog
a[Object.getOwnPropertySymbols(a)[0]] = "cat"
console.log(a.getType()); //prints cat

Related

How to make sure `has` method of `Map` or `Set` javascript collections compares objects by properties values? [duplicate]

This question already has answers here:
Define a custom hash() method for use with ES6 maps
(2 answers)
Closed 4 years ago.
I couldn't find more information on this but as far as I understand has method from Map and Set collections determines whether a given map or set has an object by checking for reference equality. But I'd like to check whether the objects are equal by comparing their properties.
For example:
let map = new Map();
let obj = {name: "Jack"};
map.set(obj, "myObj");
let result = map.has({name: "Jack"}); //false
result is false but the properties of the objects are identical, so in such case I'd like has to return true.
In Java I'd have to override equals and hashCode methods inside my class to achieve such functionality. Is there something similar I can do in Javascript without overriding has itself?
but the properties of the objects are identical
Really? As this answer outlines, it is usually very hard to define the identity of an object, as it is built of much more than sinple key-value pairs. Therefore it is impossible to derive a unique hash for it to be used in a hashtable, thats why javascript uses the object reference in the Hashtables. It also makes sense as you could get collisions otherwise. Additionally, if you change an object that is included in a hashtable so that it equals to another one, what should happen then?
Now in a lot of cases, you probably know how the object is built and how you can get its identity. Imagine a player that has a unique username, e.g.:
function Player(username, password) {
this.username = username;
this.password = password
}
Now when we want to build a Set to check if an username is already existing, it makes sense to just use the Players username instead of the player itself:
const a = new Player("a", "super secret");
const b = new Player("b", "****");
new Set([a.username, b.username]);
Or you could define a way to build up a unique key from the players properties:
Player.prototype.identity = function() {
return this.username + "°" + this.password;
};
So one can do:
const a = new Player("this is", "equal");
const b = new Player("this is", "equal");
console.log(
a === b, // false
a.identity() === b.identity() // true
);
const players = new Set([a.identity()]);
players.has(b.identity()); // true

Sanitizing `eval` to prevent it from changing any values

This is front-end only, and not back-end. I also acknowledge that this is a bad idea. At this point I'm just curious.
I have a table of records. I would like the user to be able to enter a JavaScript conditional statement, which is then applied to the table to filter the records.
For example, to filter out records with a name that's less than 6 characters, I might enter:
record.name.length < 6
Without using an external library, the easiest way I've found to do this is with eval. However, in using eval, I of course introduce the risk of the user breaking the code (not a huge concern since this is front-end only, but still a user experience issue).
I would like to sanitize the user input so that it cannot change any values. So far, I believe I only need to do these two things to make eval "safe":
Turn any single equals signs = into double or triple equals signs
Remove or escape parentheses ( )
With these two items taken care of, is there anything else I need to do to prevent the user input from changing values?
One way of doing this which is safer than eval is using the Function constructor. As far as I know, this answer is totally safe, but it's quite possible there's some caveat I don't know or have forgotten, so everyone feel free to reply if I'm wrong.
The Function constructor allows you to construct a function from its string and a list of argument names. For example, the function
function(x, y) {
return x + y;
}
could be written as
new Function('x', 'y', 'return x + y;')
or simply
Function('x', 'y', 'return x + y;')
Note that although the function body has access to variables declared in the function definition, it cannot access variables from the local scope where the Function constructor was called; in this respect it is safer than eval.
The exception is global variables; these are accessible to the function body. Perhaps you want some of them to be accessible; for many of them, you probably don't. However, there is a way round this: declare the names of globals as arguments to the function, then call the function overriding them with fake values. For example, note that this expression returns the global Object:
(function() { return Object; })()
but this one returns 'not Object':
(function(Object) { return Object; })('not Object')
So, to create a function which does not have access to any of the globals, all you have to do is call the Function constructor on the javascript string, with arguments named after all the globals, then call the function with some innocuous value for all the globals.
Of course, there are variables (such as record) which you do want the javascript code to be able to access. The argument-name arguments to Function can be used for this too. I'll assume you have an object called myArguments which contains them, for example:
var myArguments = {
record: record
};
(Incidentally, don't call it arguments because that's a reserved word.) Now we need the list of names of arguments to the function. There are two kinds: arguments from myArguments, and globals we want to overwrite. Conveniently, in client-side javascript, all global variables are properties in a single object, window. I believe it's sufficient to use its own properties, without prototype properties.
var myArgumentNames = Object.keys(myArguments);
var globalNames = Object.keys(window);
var allArgumentNames = myArgumentNames.concat(globalNames);
Next we want the values of the arguments:
var myArgumentValues = myArgumentNames.map(function(key) {
return myArguments[key];
};
We don't need to do the values part for the globals; if we don't they'll just all be set to undefined. (Oh, and don't do Object.keys(myArguments).map(...), because there's a (small) chance that the array will come out in the wrong order, because Object.keys doesn't make any guarantees about the order of its return value. You have to use the same array, myArgumentNames.) Then call the Function constructor. Because of the large number of arguments to Function it's not practical to list them all explicitly, but we can get round this using the apply method on functions:
var myFn = Function.apply(null, allArgumentNames.concat([jsString]))
and now we just call this function with the argument list we've generated, again using the apply method. For this part, bear in mind that the jsString may contain references to this; we want to make sure this doesn't help the user to do something malicious. The value of this inside the script is the first argument to apply. Actually that's not quite true - if jsString doesn't use strict mode, then trying to set this to undefined or null will fail, and this will be the global object. You can get round this by forcing the script into strict mode (using '"use strict";\n' + jsString), or alternatively just set this to an empty object. Like this:
myFn.apply({}, myArgumentValues)
I am sharing my implementation (based on #David's answer).
Some of the keys of the Window object might break the Function.apply. This is why I've filtered the ones that break. Explanations in the code below as a comment.
// Why is windowKeys not inside function scope? No need. It won't
// be changing on each call. Creating array with +270 items for each eval
// might effect performance.
const windowKeys = Object.keys(window).filter((key) => {
// Why is window filtered?
// There are some cases that parameters given here might break the Function.apply.
// Eg. window keys as numbers: '0', (if there is iframe in the page)
// the ones that starts with numbers '0asdf',
// the ones that has dash and special characters etc.
try {
Function.apply(null, [key, "return;"]);
return true;
} catch (e) {
return false;
}
});
/**
* evaluates
* #param {string} code
* #param {object} context
* #returns
*/
const safeEval = (code, context) => {
const keys = Object.keys(context);
const allParams = keys.concat(windowKeys, [`"use strict"; return ${code}`]);
try {
const fn = Function.apply(null, allParams);
const params = keys.map((key) => context[key]);
return fn(...params);
} catch (e) {
console.log(e);
}
};
// simple expression evaluation
const res = safeEval("a + b", { a: 1, b: 2 });
console.log(res);
// try to access window
const res1 = safeEval("{a, b, window, document, this: this}", { a: 1, b: 2 });
console.log(res1);
Idk. if this approach can be exploited, if it does. I think another approach can be running eval on cross-domain iframe and get the result with window messages.

ES6 safe to use static class variable as key for Map?

In babel-preset-stage-0, we can declare static class variable as follow:
class Constants {
static COUNTRY = Object.freeze({
NAME: 'Germany',
DESCRIPTION: 'placeholder',
})
}
Is it safe to use Constants.COUNTRY as the key for a ES6 Map or Set?
eg.
const map = new Map();
map.add(Constants.COUNTRY, something);
Is it guaranteed that map.get(Constants.COUNTRY) will always return something?
Is the performance as good as using strings as key? Is it safe to use Constants.COUNTRY as eventKey (bootstrap component attribute) for NavItem too?
Is it also more appropriate to declare it as a variable instead of a class?
i.e.
const Constants = Object.freeze({
COUNTRY: Object.freeze({
NAME: 'Germany',
DESCRIPTION: 'placeholder',
})
})
Is it guaranteed that map.get(Constants.COUNTRY) will always return something?
For map.get(Constants.COUNTRY) to always return your original value, a couple things have to be true.
You have to make sure that Constants.COUNTRY can never be assigned a different value either because the .COUNTRY property was reassigned or because the Constants object was replaced with something else that had a different .COUNTRY property value.
You have to make sure that nobody can ever remove that key from the map object.
If you can assure these two things, then yes map.get(Constants.COUNTRY) will always return your desired value. But, if either of those are not necessarily true, then you are not assured of always getting your value from the map.
You can assure that Constants.COUNTRY cannot be changed by freezing the Constants object or by setting that property to be configurable to it cannot be removed or written to. To assure that the Constants object cannot be replaced, it would be best for it to be const as in your second code block.
I don't know of a way to assure that nobody can call map.delete(Constants.COUNTRY) except by keeping the map object private so foreign code cannot get to it.
If you had any reason to want to prevent enumeration of the keys in the map (to make it harder for someone to discover a key perhaps), then you could use a WeakMap instead of a Map.
Is the performance as good as using strings as key?
You'd have to test a specific Javascript implementation to be sure about performance. There is no required implementation reason that one or the other should be faster - it will just depend upon the internals of the implementation.
I created a jsPerf test case to compare string lookups to object lookups. Feedback is welcome on improving how this is tested/measured, but using the current scheme where I create 10,000 string keys and 10,000 object keys in a map and then compare accessing 1000 of each, I find varying results.
Chrome is ~20% slower to access the object keys.
Firefox is ~20% slower to access the string keys.
Edge is ~27% slower to access the string keys.
Is it also more appropriate to declare it as a variable instead of a class?
As discussed, your second const form has the advantage that Constants cannot be reassigned.
You can use WeakMap to COUNTRY as key, and something as value. A variable declared with const cannot be deleted. Along with you use of Object.freeze(), wm.get(COUNTRY) should always return something
const wm = new WeakMap;
const COUNTRY = Object.freeze({
NAME: 'Germany',
DESCRIPTION: 'placeholder',
});
wm.set(COUNTRY, "something");
// error when "use strict"
delete wm;
delete COUNTRY;
COUNTRY.NAME = 123;
console.log(
wm.get(COUNTRY)
);
console.log(
COUNTRY
);
console.log(
wm
);
If requirement is for a variable that cannot be deleted or changed you can use const see Is it possible to delete a variable declared using const? and JSON
"use strict";
// `Constants` cannot be changed or deleted
const Constants = `{
"NAME": "Germany",
"DESCRIPTION": "placeholder"
}`;
console.log(
JSON.parse(Constants)
);
// delete Constants;
/*
Error: {
"message": "Uncaught SyntaxError: Delete of an unqualified identifier in strict mode."
}
*/

Symbol in Javascript

I saw following code in a project. can anyone explain what is going on here? What will be value of Attributes? What is happening this[Attributes] = attrs line?
const Attributes = Symbol('User#attrs');
class User {
constructor (attrs) {
this[Attributes] = attrs;
}
}
Symbol creates an un-collidable key for any object:
const first = Symbol('debug-name');
const second = Symbol('debug-name');
first !== second // true;
const anObj = {};
anObj[first] = 123;
anObj[second] = 456;
console.log(anObj) // {Symbol('debug-name'): 123, Symbol('debug-name'): 456}
Note that even though the first and second variables have the same debugging string they create different keys in anObj. Anyone who has access to first can add that key to any object and it will not collide with any other key in that object.
This can be used instead of magic strings to manage protocols:
// ES5
someObject.MY_LIB_attributes = [1, 2, 3];
// Only safe if no other library uses the key
// "MY_LIB_attributes"
// ES2015+
export const Attributes = Symbol('myLib#attributes');
import { Attributes } from 'my-lib';
someObj[Attributes] = [1, 2, 3];
// Safe as long as no other library uses
// *this* Symbol instance for some other purpose.
Edit
Since you've now clarified the question to be only about the line of code this[Attributes] = attrs, see the second part of my answer for discussion of that.
Original Answer
This is a couple of the new ES6 Javascript features.
const Attributes = Symbol('User#attrs'); creates a new Symbol object. The Symbol function and object is described here. It creates a unique identifier object that can then be used for many other uses, one of which is as a property name. There are many other references on the new Symbol feature so I won't repeat all of that here.
The class definition is the ES6 method for declaring prototyped classes. Again, there are many other references on this new syntax so there is no point in repeating all that here. There's an example below of what the equivalent ES5 code is.
This line this[Attributes] = attrs; uses the Symbol generated above to set a property on the newly created object.
The class definition is equivalent to the regular constructor
declaration like this:
function User(attrs) {
this[Attributes] = attrs;
}
Discussion of this[Attributes] = attrs
Attributes is a symbol which can be used as a property name on an object. It's a way of generating a unique key that can be used as a property name. So, this[Attributes] = attrs is setting a property on the newly constructed object and it is using the Attributes symbol as the property name. This Attributes symbol is a unique value that will not match any known string (in fact it won't even match other Symbol objects) so it's a way of making a unique property name.
It is unclear why the code does this:
this[Attributes] = attrs;
instead of just something like this:
this.attrs = attrs;
We would have to see a bit more context for how that is being used and why a plain string property could not also be used in place of the Symbol as you haven't provided enough context for us to know.
One possible use is for privacy. If Attributes is not public, then this is a way of creating a property on the object that the outside world doesn't know how to access because you have to have the current value of Attributes in order to access that properly. As you've shown the code with User and Attributes in the same scope that does not seem like it is private, but perhaps only User is exported to a public scope.
Another possible use is for uniqueness. If the User object may have lots of other properties added to it by other code, then Attributes creates a unique property name that cannot collide with other property names. This seems less likely in this case, but it is one possible use of Symbols.

Accessing value of key inside object

There's a handful of questions extremely similar to this, I know, but I can't seem to get anything to work.
For sake of brevity, I'll summarize this. I have this constructor function with strings assigned as values of the keys (assignment).
var Quote = function() {
this.quote1 = 'You can discover more about a person in an hour of play than in a year of conversation.';
this.quote2 = 'Nothing is at last sacred but the integrity of your own mind.';
this.quote3 = 'We have to fight them daily, like fleas, those many small worries about the morrow, for they sap our energies.';
this.quote4 = 'Ethics are so annoying. I avoid them on principle.';
this.quote5 = "Never trust anything that can think for itself if you can't see where it keeps its brain.";
};
module.exports = Quote;
Using AJAX I am printing the value of the keys inside the constructor to a static page. However... I am only successful in printing "quote1", "quote2", etc... (Just the name of the keys).
This is what my function for accessing the constructor looks like. My question is: How can I access the string values assigned to the keys inside the constructor? Is that possible?
Thank you in advance.
module.exports = function(object) {
var propArray = Object.keys(object);
var randomProp = propArray[Math.floor(Math.random() * propArray.length)];
return {quote: randomProp};
};
Inside your function, you should be able to access the quote by key using this:
object[randomProp]
In JavaScript, object properties can be accessed using square bracket notation. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects.
As you might have expected, there are several things wrong with your code :)
I've rewritten it with pretty thorough comments.
<script>
// create your 'Quote' class, which can be instantiated as many times as necessary
var Quote = function() {
this.quote1 = 'You can discover more about a person in an hour of play than in a year of conversation.';
this.quote2 = 'Nothing is at last sacred but the integrity of your own mind.';
this.quote3 = 'We have to fight them daily, like fleas, those many small worries about the morrow, for they sap our energies.';
this.quote4 = 'Ethics are so annoying. I avoid them on principle.';
this.quote5 = "Never trust anything that can think for itself if you can't see where it keeps its brain.";
};
// now let's set up the 'exports' method on the function's prototype
// this will allow any new instance of the Quote function to call or override this method
// without affecting the master 'Quote' class
Quote.prototype.exports = function() {
// you don't need to explicitly pass an object to this function
// just have it reference itself using the 'this' keyword
var propArray = Object.keys(this);
var randomProp = propArray[Math.floor(Math.random() * propArray.length)];
// objects in JavaScript can be handled like associative arrays
// simply access the key of 'this' with the value provided by 'randomProp'
return {quote: this[randomProp]};
};
// create a new instance of 'Quote'
// this guy can be used and manipulated independent of the master 'Quote' class
var module = new Quote();
// let's store the value returned by 'exports' in randomQuote
// note the () at the end of module.exports -- this tells JavaScript to treat the object as a function
var randomQuote = module.exports();
// write your quote to the document -- or do whatever else you want to with it at this point
document.write(randomQuote.quote);
</script>
Instead of return { quote: randomProp } you should use return object[randomProp].
You can access object properties via dot notation or square bracket notation.
Here's an example from jsbin
https://jsbin.com/yopeli/edit?js,console

Categories

Resources