Is it possible to access the value of a Symbol in JavaScript? - javascript

I have been just introduced to the concept of Symbols in JavaScript. I have been informed that they can be used to create unique identifiers to avoid potential clashes.
For example...
let user = {
name : "John",
Symbol("id"): 123, // Symbol("id") is a unique identifier
Symbol("id"): 123 // and the Symbol("id") here is also different and unique
};
... I understand the above code. However, what is the actual identifier value of each "Symbol("id")"? How do I find out?
Any pointers appreciated.

No, you cannot see the "raw" value of the symbol in the JS environment, because it is implemented using the native C ++ code of the JS engine itself, and this implementation does not provide an opportunity to display it anywhere in the console or on a page.
You can think of symbols as big numbers and every time you create a symbol, a new random number gets generated (uuid). You can use that symbol (the random big number) as a key in objects.
Regarding this definition, you can look at a possible implementation of the symbol in ES5:
var Symbol;
if (!Symbol) {
Symbol = (function(Object){
// (C) WebReflection Mit Style License
var ObjectPrototype = Object.prototype,
defineProperty = Object.defineProperty,
prefix = '__simbol' + Math.random() + '__',
id = 0;
//... some other code
You can see that Math.random() is used here, so Symbol there will have long big number as a his main property (unique with high possibility).
Another way to explain what a symbol is is that a Symbol is just a piece of memory in which you can store some data. Each symbol will point to a different memory location. In the context of this definition, you can see the source code in C++ of the JS engine itself, for example V8 that is used in Chromium. If you know C++, you can try to find an implementation of Symbol() constructor there, but it won't be easy.
Therefore, we can say that a Symbol is a kind of unique memory area that a certain string describes. The memory area itself it's already term from a low-level programming, so you can think of it as something like 1010010011...

const id1 = Symbol("id");
const id2 = Symbol("id");
const user = {
name: "John",
[id1]: 123, // "[id1]" is a unique identifier
[id2]: 456, // and the value of "[id2]" here is also different
};
console.log('id1:', user[id1], id1.description);
console.log('id2:', user[id2], id2.description);

I wasn't able to get your question properly, I tried to help you hope this will work
let user = { // belongs to another code
name: "Alex"
};
let id = Symbol("id");
user[id] = 200;
alert( user[id] ); // we can access the data using the symbol as the key

From mdn:
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.
console.log(Symbol('foo') === Symbol('foo'))
From an article in this answer:
ES6 symbols are similar to the more traditional symbols in languages
like Lisp and Ruby, but not so closely integrated into the language.
In Lisp, all identifiers are symbols. In JS, identifiers and most
property keys are still considered strings. Symbols are just an extra
option.
As mdn docs explains, you could access the Description that you passed but not the value of the Symbol:
Most values in JavaScript support implicit conversion to a string. For
instance, we can alert almost any value, and it will work. Symbols are
special. They don’t auto-convert.
For example,
let Sym = Symbol("Sym");
alert(Sym); // TypeError: Cannot convert a Symbol value to a string
That’s a "language guard" against messing up, because strings and
symbols are fundamentally different and should not occasionally
convert one into another.
If we really want to show a symbol, we need to call .toString() on it,
for example,
let Sym = Symbol("Sym");
alert(Sym.toString()); // Symbol(Sym), now it works
Or we can use get symbol.description property to get
the description on it, for example,
let _Sym = Symbol("Sym");
alert(_Sym.description); // Sym

Related

How to set a Javascript Symbol as an Object Key?

I have a unique case where I need to use a Javacript symbol as an object's key. This is necessary because in order to conform to Sequelize's documentation, there are instances where we need to have something that looks like this:
const where = {
cost: {
[Op.gt]: 1000,
[Op.lt]: 2000
}
}
Both [Op.gt] and [Op.lt] are Javascript symbols that assist with querying. The block of code will query where a property called cost is greater than 1000 but less than 2000. But when I try to programmatically set the key/value pairs like:
where['cost'][[Op.gt]] = 1000;
I receive the following error:
Cannot convert a Symbol value to a string
This is a dynamic object, so I cannot hard code the symbols into the where query since the next user may not need to query by these parameters. How do I go about this? Thanks!
Remove 1 bracket around your symbol and you will be fine:
where['cost'][Op.gt] = 1000;
obj[Op.gt] means you're accessing an object property with the Op.gt name. obj[[Op.gt]] means you're accessing an object property with the name equal to an array [Op.gt] stringified. Which is similar to below:
const arr = [Op.gt];
const propertyName = arr.toString(); // => throw error "Cannot convert a Symbol value to a string"
where['cost'][propertyName];

Node.JS behaves strange

I have a variable called uids
var uids = [];
Then I write some value to it property
uids[16778923] = "3fd6335d-b0e4-4d77-b304-d30c651ed509"
But before it
if (!uids[user.id]) {
uids[user.id] = generateKey(user);
}
This thing behaves ok. If I try to get the value of it property
uids[currentUser.id]
It will give me a value of this property. If I try to call some methods like
Object.keys(uids);
It will give me, what I expected. And here the mystery comes...
uids;
RAM rest in piece. See the node eating ram
I am very confused now. What's wrong?
This is because you are creating a huge array and node will reserve memory for it - who knows what comes. I'd say that's a scenario where you would use a Map (or a plain object, but Map feels better here.
var uids = new Map();
var key = 456464564564654;
if (! uids.has(key)) {
uids.set(key, generateKey(user))
}
You are creating an empty array (length is zero), then you assign some value to an arbitrary index. This will make the array grow as big as the index and assign the value to that index. Look at this example using node.js REPL:
> var a = []
undefined
> a[5] = "something"
'something'
> a
[ , , , , , 'something' ]
> a.length
6
Instead of creating an array, you could create a Map() or an common javascript object (singleton). Javascript objects behave like Maps but only Strings can be used as keys. If you assign a Number to be key, javascript will convert it to String automatically.
Personally, I would go with objects because they perform better. Instantiating an object takes longer than instantiating a Map (and it doesn't seem like you need to create several groups of "uids"), but once done, adding new keys and retrieving values from any key in faster when using common objects. At least that's how things go in my node.js v6.7.0 on ubuntu 14.04 but you could try for yourself. And it would also make the least alteration to your code.
var uids = {} // common/ordinary empty javascript object instead of array.
if (!uids[user.id]) { // getting value from one key works the same.
uids[user.id] = generateKey(user) // assignment works the same.
}
////
uids[16778923] = "3fd6335d-b0e4-4d77-b304-d30c651ed509" // key will be "16778923".
uids[16778923] // getting value for key "16778923" can be done using 16778923 instead of "16778923".
////
uids[currentUser.id] // still returning values like this.
Object.keys(uids) // still returning an array of keys like this. but they are all Strings.

Efficient memoization of object arguments

Summary: Is there a faster way to hash objects than JSON.stringify?
Details: I have a Ruby and JavaScript library (NeatJSON) that provides pretty-printing of JavaScript values. I recently fixed a problem where deeply-nested objects caused O(n!) performance (n being the nesting level) using memoization based on the object being serialized and the indentation amount.
In Ruby, the fix was really easy, because you can index hashes by arrays of unique sets of objects:
build = ->(object,indent) do
memoizer[[object,indent]] ||= <all the rest of the code>
end
In JavaScript, however, I can't index an object by another object (in a unique way). Following the lead of several articles I found online, I decide to fix the problem generically, using JSON.stringify on the full set of arguments to the function to create a unique key for memoization:
function memoize(f){
var memo = {};
var slice = Array.prototype.slice;
return function(){
var args = slice.call(arguments);
var mkey = JSON.stringify(args);
if (!(mkey in memo)) memo[mkey] = f.apply(this,args);
return memo[mkey];
}
}
function rawBuild(o,indent){ .. }
var build = memoize(rawBuild);
This works, but (a) it's a little slower than I'd like, and (b) it seems wildly inefficient (and inelegant) to perform (naive) serialization of every object and value that I'm about to serialize smartly. The act of serializing a large object with many values is going to store a string and formatting result for EVERY unique value (not just leaf values) in the entire object.
Is there a modern JavaScript trick that would let me uniquely identify a value? For example, some way of accessing an internal ID, or otherwise associating complex objects with unique integers that takes O(1) time to find the identifier for a value?
If you are looking to memoise your objects by identity (not by content), then you'll want to use a WeakMap which is designed for exactly this purpose. They don't work for primitive values though, so you'll need a different solution for such arguments.
Using #Bergi's suggestion of a WeakMap I found out about Map, which allows using any value type as the key (not just objects). Because I needed a compound key—uniquely memoizing the combination of the value passed in and the indentation string—I created a hierarchical memoization structure:
function memoizedBuild(){
var memo = new Map;
return function(value,indent){
var byIndent=memo.get(value);
if (!byIndent) memo.set(value,byIndent={});
if (!byIndent[indent]) byIndent[indent] = rawBuild(value,indent);
return byIndent[indent];
}
}
This proved to be about 4× faster than the memoization code I had been using when serializing a large 270kB JSON object.
Note that in the above code I'm able to use !byIndent[indent] only because I know that rawBuild will never return a falsey value (null, undefined, false, NaN, 0, ""). The safer code line would look something like:
if (!(indent in byIndent)) byIndent[indent] = rawBuild(value,indent);
If you just need to memoise objects then it makes sense to assign some unique ID to your objects .
var gID = 0;
function createNode() {
var obj = ...
obj.id = (++gID).toString();
}
and use those obj.id's as keys in your memo collection.
That would be fastest and least greedy solution.
Update:
If you want that id property to do not clash with existing properties
then you can create non-enumerable properties using standard ES5.1 Object.createProperty() (with some unique name) or to use ES6 symbols:
var gID = 0;
var gUidSym = Symbol("uid");
function getUidOf(obj) {
return obj[gUidSym]
|| (obj[gUidSym] = (++gID).toString());
}

Retrieving JS' Object value without knowing it's name

This is a fairly common question here in SO, and I've looked into quite a few of them before deciding to ask this question.
I have a function, hereby called CheckObjectConsistency which receives a single parameter, an object of the following syntax:
objEntry:
{
objCheck: anotherObject,
properties: [
{
//PropertyValue: (integer,string,double,whatever), //this won't work.
PropertyName: string,
ifDefined: function,
ifUndefined: function
}
,...
]
}
What this function does is... considering the given parameter is correctly designed, it gets the objCheck contained within it (var chk = objEntry.objCheck;), It then procedes to check if it contains the properties contained in this collection.
Like this
for(x=0;x<=properties.length;x++){
if(objCheck.hasOwnProperty(properties[x].PropertyName)){
properties[x].ifDefined();
}
else{
properties[x].ifUndefined();
}
What I want is... I want to bring it to yet another level of dynamicity: Given the propositions that IfDefined and IfUndefined are functions to be called, respectively, if the currently-pointed PropertyName exists, and otherwise, I want to call these functions while providing them, as parameters, the very objCheck.PropertyName's value, so that it can be treated before returning to the user.
I'll give a usage example:
I will feed this function an object I received from an external provider (say, a foreign JSON-returning-WebService) from which I know a few properties that may or may not be defined.
For example, this object can be either:
var userData1 = {
userID : 1
userName: "JoffreyBaratheon",
cargo: "King",
age: 12,
motherID : 2,
//fatherID: 5,--Not defined
Status: Alive
}
or
var userData2 = {
userID :
userName: "Gendry",
cargo: "Forger Apprentice",
//age: 35, -- Not Defined
//motherID: 4,-- Not Defined
fatherID: 3,
Status: Alive
}
My function will receive:
var objEntry=
{
objCheck: userData1,
properties: [
{
PropertyName: "age",
ifDefined: function(val){alert("He/she has an age defined, it's "+val+" !");},
ifUndefined: function(){alert("He/she does not have an age defined, so we're assuming 20.");},
},
{
PropertyName: "fatherID",
ifDefined: function(val){alert("He/she has a known father, his ID is "+val+" !");},
ifUndefined: function(){alert("Oh, phooey, we don't (blink!blink!) know who his father is!");},
}
]
}
CheckObjectConsistency(objEntry); // Will alert twice, saying that Joffrey's age is 12, and that his father is supposedly unknown.
ifDefined will only actually work if, instead of properties[x].ifDefined();, I somehow provide it with properties[x].ifDefined(PropertyValue);. And here, at last, lies my question.
Being inside the consistency-checking-function, I only know a given property's name if it's provided. Being inside it, I can't simply call it's value, since there is no such function as properties[x].ifUndefined(properties[x].GetValueFromProperty(properties[x].PropertyName)) ,... is there?
I'm sorry. Not being a native english speaker (I'm brazilian), I can't properly express my doubts in a short way, so I prefer to take my time writing a long text, in an (hopefully not wasted) attempt to make it clearer.
If, even so, my doubt is unclear, please let me know.
I think you're looking for the bracket notation here. It allows you to provide an arbitrary value as key to access the object. Also, you know its name. You have your properties object right?
objEntry.properties.forEach(function(property){
// Check if objCheck has a property with name given by PropertyName
if(!objEntry.objCheck.hasOwnProperty(property.PropertyName)){
// If it doesn't, call isUndefined
property.isUndefined();
} else {
// If it does, call isDefined and passing it the value
// Note the bracket notation, allowing us to provide an arbitrary key
// provided by a variable value to access objCheck which in this case is
// the value of PropertyName
property.isDefined(objEntry.objCheck[property.PropertyName]);
}
});
Oh yeah, forEach is a method of arrays which allows you to loop over them. You can still do the same with regular loops though.

Is there a name for a language feature that allows assignment/creation?

This is a bit hard for me to articulate, but in PHP you can say something like:
$myArray['someindex'] = "my string";
and if there is no index named that, it will create/assign the value, and if there IS an index, it will overwrite the existing value.
Compare this to Javascript where today I had to do checks like so:
if (!myObject[key]) myObject[key] = "value";
I know this may be a bit of a picky point, but is there a name for the ability of PHP (and many other languages) to do these checks on their own as opposed to the more verbose (read: PITA) method of Javascript?
EDIT
I confused myself in asking this. Let's say you want to add to this structure:
myobject = {
holidays : {easter : {date : 4/20/2010,
religion : Christianity}
holi : {date : 3/10/2010,
religion : hindu}
}
I had a problem today where I received tabular data and I wanted to put it into a tree sort of like this by building an object.
When I started my loops, I had trouble making NEW indices like myobject['holidays'][thisVariable][date] = 4/20/2010 if the tree hadn't been mostly built to that point.
I'll grab a code sample from my other computer if this isn't clear, sorry for the poor thinking.
You are mistaken. To assign a value to an object's key in javascript, you don't need to perform that check. The value will be assigned whether there is already a value for that key or not.
Think about it. How could you ever get values into an object or hash if you had to have a value there first?
I would guess 'auto-vivification' http://en.wikipedia.org/wiki/Autovivification from Perl might be relevant, but it works different than you described. The wiki page has a good summary. Other languages like Ruby support a "default action" hook for hash keys that have not been assigned, which can be used for auto-vivification as well.
For instance, in Ruby:
>> h = Hash.new {|h,k| h[k] = {}}
=> {}
>> h["hello"]["world"] = 20
=> 20
>> h["hello"]["world"]
=> 20
Javascript does exactly the same thing as PHP here: myObject[key] = "value" will overwrite the existing value if one exists. Can you tell us why you think otherwise?

Categories

Resources