Related
UPDATE: Recently a brilliant article from Mozilla came up. Read it if you're curious.
As you may know they are planning to include new Symbol primitive type in ECMAScript 6 (not to mention some other crazy stuff). I always thought that the :symbol notion in Ruby is needless; we could easily use plain strings instead, like we do in JavaScript. And now they decide to complicate things in JS with that.
I don't understand the motivation. Could someone explain to me whether we really need symbols in JavaScript?
The original motivation for introducing symbols to Javascript was to enable private properties.
Unfortunately, they ended up being severely downgraded. They are no longer private, since you can find them via reflection, for example, using Object.getOwnPropertySymbols or proxies.
They are now known as unique symbols and their only intended use is to avoid name clashes between properties. For example, ECMAScript itself can now introduce extension hooks via certain methods that you can put on objects (e.g. to define their iteration protocol) without risking them to clash with user names.
Whether that is strong enough a motivation to add symbols to the language is debatable.
Symbols do not guarantee true privacy but can be used to separate public and internal properties of objects. Let's take an example where we can use Symbol for having private properties.
Let's take an example where a property of an object is not private.
var Pet = (function() {
function Pet(type) {
this.type = type;
}
Pet.prototype.getType = function() {
return this.type;
}
return Pet;
}());
var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Modified outside
console.log(a.getType());//Output: null
Above, the Pet class property type is not private. To make it private we have to create a closure. The below example illustrates how we can make type private using a closure.
var Pet = (function() {
function Pet(type) {
this.getType = function(){
return type;
};
}
return Pet;
}());
var b = new Pet('dog');
console.log(b.getType());//dog
b.type = null;
//Stays private
console.log(b.getType());//dog
Disadvantage of above approach: We are introducing an extra closure for each Pet instance created, which can harm performance.
Now we introduce Symbol. This can help us make a property private without using extra unnecessary closures. Code example below:
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());//Output: dog
a.type = null;
//Stays private
console.log(a.getType());//Output: dog
This post is about the Symbol(), supplied with actual examples I could find/make and facts & definitions I could find.
TLDR;
The Symbol() is the data type, introduced with the release of ECMAScript 6 (ES6).
There're two curious facts about the Symbol.
the first data type and only data type in JavaScript which has got no literal
any variable, defined with Symbol(), gets unique content, but it's not really private.
any data has its own Symbol, and for the same data the Symbols would be the same. More info in the following paragraph, otherwise it's not a TLRD; :)
How do I initialise the symbol?
1. To get a unique identifier with a debuggable value
You can do it either this way:
var mySymbol1 = Symbol();
Or this way:
var mySymbol2 = Symbol("some text here");
The "some text here" string can't be extracted from the symbol, it's just a description for debugging purposes. It doesn't change the behaviour of symbol in any way. Although, you could console.log it (which is fair, since the value is for debugging, so as not to mistake that log with some other log entry):
console.log(mySymbol2);
// Symbol(some text here)
2. To obtain a symbol for some string data
In this case the value of symbol is actually taken into account and this way two symbols may be non-unique.
var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!
Let's call those symbols "second-type" symbols. They do not intersect with the "first-type" symbols (i.e. the ones defined with Symbol(data)) in any way.
The next two paragraphs pertain only the first-type symbol.
How do I benefit from using Symbol instead of the older data types?
Let's first consider an object, a standard data type. We could define some key-values pairs there and have an access to the values by specifying the key.
var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan
What if we have two persons with the name Peter?
Doing this:
var persons = {"peter":"first", "peter":"pan"};
wouldn't make much sense.
So, appears to be a problem of two absolutely different persons having a same name. Let's then refer out new Symbol(). It's like a person in real life - any person is unique, but their names can be equal. Let's define two "persons".
var a = Symbol("peter");
var b = Symbol("peter");
Now we have got two different persons with the same name. Are our persons different indeed? They are; you can check this:
console.log(a == b);
// false
How do we benefit there?
We can make two entries in your object for the different persons and they can't be mistaken in any way.
var firstPerson = Symbol("peter");
var secondPerson = Symbol("peter");
var persons = {[firstPerson]:"first", [secondPerson]:"pan"};
Note:
It's worth to notice, though, that stringifying the object with JSON.stringify will drop all the pairs initialised with a Symbol as a key.
Executing Object.keys won't either return such Symbol()->value pairs.
Using this initialisation, it's absolutely impossible to mistake the entries for the first and second persons. Calling console.log for them will correctly output their second names.
console.log(persons[a]);
// first
console.log(persons[b]);
// pan
When used in object, how it is different compared to defining non-enumerable property?
Indeed, there already existed a way to define a property to be hidden from Object.keys and enumeration. Here it is:
var anObject = {};
var fruit = "apple";
Object.defineProperty( anObject, fruit, {
enumerable: false,
value: "green"
});
What difference does Symbol() bring there? The difference is that you can still get the property defined with Object.defineProperty in the usual way:
console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //green
console.log(anObject.apple); //green
And if defined with Symbol as in previous paragraph:
fruit = Symbol("apple");
You will have an ability to receive its value only if knowing its variable, i.e.
console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined
Moreover, defining another property under the key "apple" will make the object drop the older one (and if hard-coded, it could throw an error). So, no more apples! That's a pity. Referring the previous paragraph, the Symbols are unique and defining a key as Symbol() will make it unique.
Type conversion and checking
Unlike other data types, it's impossible to convert the Symbol() to any other data type.
It's possible to "make" a symbol based on primitive data type by calling Symbol(data).
In terms of checking the type, nothing changes.
function isSymbol ( variable ) {
return typeof someSymbol === "symbol";
}
var a_Symbol = Symbol("hey!");
var totally_Not_A_Symbol = "hey";
console.log(isSymbol(a_Symbol)); //true
console.log(isSymbol(totally_Not_A_Symbol)); //false
Symbols are a new, special kind of object that can be used as a unique property name in objects. Using symbols instead of a strings allows different modules to create properties that don’t conflict with one another. Symbols can also be made effectively private, so that their properties can’t be accessed by anyone who doesn’t already have direct access to the symbol.
Symbols are a new primitive, just like the number, string, and boolean primitives. Unlike the other primitives, symbols do not have a literal syntax (e.g. how string has '') — the only way to create them is with the Symbol constructor in the following way:
let symbol = Symbol();
In reality, symbols are just a slightly different way to attach properties to an object — you could easily provide the well-known symbols as standard methods, just like Object.prototype.hasOwnProperty, which appears in everything that inherits from Object.
Here are some of the benefits of the Symbol primitive type.
Symbols have debuggability built in
Symbols can be given a description, which is really just used for debugging to make life a little easier when logging them to a console.
Symbols can be used as object keys
This is where symbols get really interesting. They are heavily intertwined with objects. Symbols can be assigned as keys to objects, meaning you can assign an unlimited number of unique symbols to an object and be guaranteed that these will never conflict with string keys, or other unique symbols.
Symbols can be used as unique values
Let’s assume you have a logging library, which includes multiple log levels such as logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARN and so on. In ES5 code you’d like make these strings (so logger.levels.DEBUG === 'debug'), or numbers (logger.levels.DEBUG === 10). Both of these aren’t ideal as those values aren’t unique values, but symbols are! So logger.levels simply becomes:
log.levels = {
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');
Read more in this great article.
Here is how I see it. Symbols provide 'an extra level of privacy', by preventing the keys/properties of an object from being exposed through some popular methods such as Object.keys() and JSON.stringify().
var age = Symbol(); // declared in another module perhaps?
class Person {
constructor(n,a){
this.name = n;
this[age] = a;
}
introduce(){
console.log(`My name is ${this.name}. I am ${this[age]-10}.`);
}
}
var j = new Person('Jane',45);
j.introduce(); // My name is Jane. I am 35.
console.log(JSON.stringify(j)); // {"name":"Jane"}
console.log(Object.keys(j)); // ["name"]
console.log(j[age]); // 45 (well…only if you know the age in the first place…)
Although given an object per se, such properties can still be exposed through reflection, proxy, Object.getOwnPropertySymbols() etc., there is no natural means to access them through a few direct methods, which may be sufficient sometimes from an OOP perspective.
A JS symbol is a new primitive data type. They are tokens that serve as unique IDs. A symbol can be created using the Symbol constructor. Take for instance this snippet from MDN:
// The symbol constructor takes one optional argument,
// the descriptions which is used for debugging only.
// Here are two symbols with the same description
let Sym1 = Symbol("Sym");
let Sym2 = Symbol("Sym");
console.log(Sym1 == Sym2); // returns "false"
// Symbols are guaranteed to be unique.
// Even if we create many symbols with the same description,
// they are different values.
It is often handy to use symbols as unique object property keys, for example:
let obj = {};
let prop = Symbol();
obj[prop] = 123; // the symbol prop is assigned 123
obj.prop = 456; // the string prop is assigned 456
console.log(obj.prop, obj[prop]); // logs 456, 123
Symbols have two main use cases:
“Hidden” object properties. If we want to add a property into an object that “belongs” to another script or a library, we can create a
symbol and use it as a property key. A symbolic property does not
appear in for..in, so it won’t be accidentally processed together
with other properties. Also it won’t be accessed directly, because
another script does not have our symbol. So the property will be
protected from accidental use or overwrite.
So we can “covertly” hide something into objects that we need, but
others should not see, using symbolic properties.
There are many system symbols used by JavaScript which are accessible as Symbol.*. We can use them to alter some built-in
behaviors. For instance, ......
Symbol.iterator for iterables, Symbol.toPrimitive to setup
object-to-primitive conversion and so on.
Source
Don't do it
This question has some comments with a low opinion of the very notion of reconstructing the objects. The commenters either couldn't or wouldn't explain why they thought it was a bad idea, but since asking I have come the to same conclusion. Here's why.
If you think about MVVM, the purpose of having a model and a view-model is to separate behaviour from data. This is kind of funny, because the point of object-orientation is to combine them. But in a distributed world, the data has to be shipped around. If your code and data are all munged together then you have to either invent MVVM or keep de- and re-constructing objects.
The code to de- and re-construct objects is a testing and maintenance time-sink you don't need, and introduces two failure modes. Don't do it. Have a method-less class to hold the state and a stateless class that operates on the method-less class. This is the essence of MVVM, and really nothing more than application of Memento pattern.
Memento (283)
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.
Design Patterns, Gamma et al, 1995
Original question
The data of my view models is passed back and forth between client JS and server Web APIs as JSON.
It is well understood that JSON.stringify(object) serialises only members that have a non-null value that is not a Function. Thus, JSON.parse(JSON.stringify(someObject)) will remove all the methods from the object.
My current implementation has each graph node implemented as a Typescript class with Serialise and Deserialise methods. JQuery.ajax calls a Web API and implicitly parses the resultant JSON into a DAG of object definitions, each of which has a Type property indicating which type of class it was prior to serialisation. I have a map of constructors indexed by name and the appropriate constructor is retrieved and the data passed as the constructor parameter.
Depending on type there may be children; if so things proceed recursively down the graph.
I have been wondering whether, rather than copy all the property values, I couldn't just assign an appropriate prototype. Bring the mountain to Mahomed, you might say. This would eliminate quite a bit of clutter in my codebase.
As I write it occurs to me that I could use $.extend, but I'm progressively weeding jQuery out of my codebase so this would be a retrograde step.
Is there any known peril in my proposition of diddling the prototype?
Does anyone have a better idea? Other than $.extend, I mean. Something TypeScripty, by preference.
It has been observed in comments that assigning the prototype means the constructor is never called. This is irrelevant. The object state is already set up, all that is required is to make the methods available.
I recently built object with methods which content could be serialized and then reconstructed.
I simply added an argument which could take a JSON object and assign it to itself.
Example using plain object:
function myObject() {
this.valueA = 1;
this.valueB = 2;
this.valueC = 3;
this.add = function() {
return this.valueA + this.valueB + this.valueC;
};
}
var o = new myObject();
console.log(o.add());
console.log(JSON.stringify(o));
If you serialized this you would get:
{"valueA":1,"valueB":2,"valueC":3}
Now, to reconstruct this you can add a Object.assign() to the object like this taking the argument and merge it with self:
function myObject(json) {
this.valueA = 0;
this.valueB = 0;
this.valueC = 0;
this.add = function() {
return this.value1 + this.value2 + this.value3;
};
Object.assign(this, json); // will merge argument with itself
}
If we now pass the parsed JSON object as argument it will merge itself with the object recreating what you had:
var json = JSON.parse('{"valueA":1,"valueB":2,"valueC":3}')
function myObject(json) {
this.valueA = 0;
this.valueB = 0;
this.valueC = 0;
this.add = function() {
return this.valueA + this.valueB + this.valueC;
};
Object.assign(this, json); // will merge argument with itself
}
var o = new myObject(json); // reconstruct using original data
console.log(o.add());
If you now have children via array you simply repeat the process recursively down the chain.
(A bonus is that you can also pass options this way).
I am serializing and storing an object that was created from a WinJS.Class like this:
var myClass = WinJS.Class.define(...);
var myObject = new myClass();
var serialized = JSON.stringify(myObject);
//store the object
And later I'm pulling the object out of storage and I want to deserialize it and cast it as a myClass. Is that possible with WinJS out of the box or do I need to create a constructor for my class that is capable of taking an object that can turn it into a new object?
I haven't broken into TypeScript yet, and I think that would help out in this situation, but until then I'm wondering how to do it with plain JavaScript/WinJS.
There are a few ways to handle this, and none are particularly special to WinJS. Simply put: JSON serialization only serializes and deserializes the obje values, not its methods, prototype, or other type information.
Option 1: Copy values to new instance of your class
This is usually best accomplished by having your constructor take the deserialized object as a parameter and copying the data to the new instance.
There are a variety of variations of this. Using the object constructor is generally the best for performance, as this typically enables the JS engine to apply the greater number of optimizations to the object.
WinJS.UI.setOptions can be helpful here, or you can just copy the data using a simple loop like this:
var keys = Object.keys(source);
for (var i = 0, len = keys.length; i < len; i++) {
var key = keys[i];
destination[key] = source[key];
}
Option 2: Setting __proto__
Warning: This can have significantly adverse performance effects, so it's not appropriate in some situations. But occasionally it can be handy.
Object.setPrototypeOf(myObject, myClass.prototype);
Note that setPrototypeOf is relatively new. It's there on Win8.1 for web apps (which I'm guessing this is about) and in IE 11, but not available in Safari, for example. On older browsers/ Safari, assigning to proto is the equivalent (but if available, setPrototypeOf is better).
This will attach methods from myClass to the object, but in addition to the negative performance effects, also does not run your constructor on the object - so it still may not be in exactly the same state as the object you originally serialized.
Other helpful thing: JSON "revivers"
JSON.parse takes an optional second parameter, called a "reviver". This lets you provide a function that gets the opportunity to transform each node of the JSON being deserialized. This can be useful for rehydrating serialized dates into JavaScript Date objects, for example. It also gets the opportunity to transform the top-most object, which could be useful in some cases to turn the deserialized object into the "class" you want.
Javascript is a dynamic language so I think you dont need to cast the deserialized object, just treat it as myClass type and that's it. Hope it helps you.
You should consider using the 'Options' constructor pattern, where the option value is the deserialized object:
// MovieModel Constructor
// ----------------------
function MovieModel(options) {
this._titleValue = options.title || "Sample Title";
}
Where the movie methods closure is something like this:
// MovieModel Methods
// ------------------
var movieModelMethods = {
title: {
get: function () {
return this._titleValue;
},
set: function (val) {
this._titleValue = val;
this.dispatchEvent("title");
}
}
};
Since WinJS class define can only specify one constructor function (as far as I understand it), you may use the static members to define a factory function that will take the serialized data as a parameter. This factory methdod will actually create a new instance and will set the values one by one and return the new object.
It as some advantages like the fact that you can actually manage the data structure changes over the time you enhance the app...
The drawback is that you cannot write new MySuperClass() all the time...
...
// let's suppose we already called JSON.parse(data);
create: function(serializedData) {
var newObj = new MySuperClass();
newObj.name = serializedData.name || "";
newObj.color = serializedData.color || "";
return newObj;
}
Then you will call somewhere else in the app :
var myInstance = MySuperClass.create(serializedDataFromfile);
You should just be able to call JSON.parse after pulling it out of local storage:
var myObject2;
myObject2 = JSON.parse(localStorage["mySeriazliedObject"];
I've been looking into HTML 5's new local storage and it seems pretty cool so far. I've decided to use JSON to serialize my objects into strings and to parse them back into objects, which all sounds very nice. However, it's easier said than done. I've discovered that you can't just JSON.stringify() an object and expect it to pack nicely for you, but I can't figure out what I have to do instead.
That's not all, though: my object contains two arrays, each of which holds another type of object and one of which is multidimensional. Here's what my rather complex and inter-dependent object architecture looks like:
function Vector2(x, y) {
this.x = x;
this.y = y;
}
function Bar(ID, position) {
this.id = id;
this.position = position;
}
function Goo(state, position) {
this.on = state;
this.position = position;
}
function Foo(name, size) {
this.name = name;
this.size = size;
this.bars = new Array(width)
this.goos = new Array(10);
this.Initialize();
}
Foo.prototype.Initialize() {
for(var x = 0;x<this.size.x;x++) {
this.bars[x] = new Array(this.size.y);
for(var y=0;y<this.size.y;y++) {
this.bars[x][y] = new Bar(x + y, new Vector2(x, y));
}
}
for(var i = 0;i<this.goos.length;i++) {
this.goos[i] = new Goo(on, new Vector2(i, i/2 + 1));
}
}
Each of those objects has plenty of additional functions as well, each added using the same prototype method that I used to add the method to Foo. Like I said, complex. My question is, how do I serialize all this? Do I really need to tack on toJSON() functions to every object?
Finally, once I've packed all this and saved it to localstorage, I know how to retrieve it, but I'm pretty much clueless on how to unpack it with JSON. That's another matter for another time, though, and I suspect it might be a bit easier to figure out on my own once I learn how to pack everything up.
Note: I wouldn't normally such a potentially broad question, but I couldn't really find anything here on SO or with my (admittedly weak) Google-fu that really addresses the issue, and I don't know how to break this question down any further.
Usually, you don't just serialize complex data structures in javascript because the normal serialization doesn't handle multiple difference things all have references to the same object, can't handle circular references, etc...
What I would recommend instead is that you figure out what the real state of your application is. Not the whole instantiated object structure, but what is the minimum amount of information that is actually needed to reconstruct the state of your data. Then, once you've figure that out (it should only be data, no actual objects), then you can create functions or methods to get that data from your data structures or create a new data structure from the data.
In looking at your code, the actual state of a Foo object is a two dimensional array of Bar objects and a one dimensional array of Goo objects and a name and a size. A Bar just has an x, y and id. A Goo just has a state and an x and a y. That would be pretty easy state to write a Foo method to generate and a Foo method to accept that state from saved storage.
Tacking on toJSON and fromJSON functions is probably the right way to do it. You should only be saving the actual unique data for any given object as JSON, it would not be a good idea to serialize the entire instantiated object for several big reasons, #1 being that it's just unnecessary. Also, think of the case where you add or modify functions for one of your objects in the near future: clients who had stored their own serialized objects, will never get your updates. Simply storing the unique instance data, and having functions to convert that data back to a real object (using your most recent definition) is the way to go.
You can pack functions to strings:
var a = function() {return "a"};
a.toString()
// And also:
function b() {return "b"};
b.toString();
So from there on you could hack the JSON source (by Douglas Crockford) and include support for functions. Or something.
I hope I'm not too late here but I just encountered the same problem.
I wanted to persist a promise object (returned from a JQuery AJAX call) to local storage.
Luckily I stumbled upon a little Javascript library called Stash (http://rezitech.github.io/stash/). It makes both the serialization of complex Javascript objects to Strings and the parsing of a String to a Javascript object possible.
The best thing, you don't have to explicitly perform the serialization/parsing because the data types are automatically recognized and transformed when you want to persist/retrieve an object to/from local storage.
Here is a quick test which yielded a positive result
// the api endpoint
var url = "www.api-host.com/api/bla/blub";
// create json call returning promise object
var promise = $.getJSON(url);
// persist promise object in local storage using dash.js
var key = url;
stash.set(key, promise);
// retrieve promise object from local storage
var stashGet = stash.get(key);
// display in console
console.log(stashGet);
Regards, Matthias
This is a follow-up question to Javascript Serialization of Typed Objects. That solution works OK for objects whose type is known, but now I have an object of a type that is unknown by the code that will be performing the de-serialization. There's a base class "Sprite" that has a number of properties that need to be serialized. Any number of derived classes (such as "Player" and "Platform" etc) may derive from class Sprite and add their own properties. I also have a "MapLayer" object that contains a collection of Sprite-derived objects. How do I de-serialize the layer and all of its sprites such that each sprite will be of the correct derived type when de-serialization is complete. Do I need to use eval("new " + derivedTypeName + parameterList)? Is there a better way?
More details:
The Sprite base class is hard-coded, but all the derived classes are generated code. I can make the code generator generate deserialize functions for every derived class, but how do I call them appropriately from the generic base class deserialization function? There's only one MapLayer class, and somehow it has to potentially call the deserialize function on all the classes derived from Sprite.
In order to call the derived object's constructor function, you'll first need to know which constructor you want to call. You don't give details about how you're currently encoding that type information in your serialized payload, so let's say you've got something like the following:
var MyDerivedType = function () {...};
MyDerivedType.prototype.__derivedTypeName = 'MyDerivedType';
MyDerivedType.deserialize = function ( input ) {
var obj = JSON.parse( input );
return new MyDerivedType( obj );
};
If you're not assigning your derived types to the global scope then you'll need to be able to address them at the time of deserialization. Here's an example of storing them on the Sprite constructor itself:
Sprite.derivedTypes = Sprite.derivedTypes || {};
Sprite.derivedTypes['MyDerivedType'] = MyDerivedType;
Then you can avoid using eval and call the appropriate deserializer like this:
Sprite.deserialize = function(input) {
// json parse the data string to pull out our derived type
var o = JSON.parse(input);
// delegate to the derived type's deserialize method
return Sprite.derivedTypes[o.__derivedTypeName].deserialize(input);
};
There is nothing built-in to JavaScript/browser js environments which provides deserialization to "classes" in the sense that you're familiar with in .net.