Best approach for creating an object from deserializing - javascript

Best approach for creating an object from deserializing.
I'm looking for good approach when create object from serialized data. Let's assume that there is an object defined like that:
function A()
{}
A.prototype.a = "";
and serialized data: "a".
So which approach will be better and why:
1. Create static method deserialize:
A.deserialize = function( data )
{
var a = new A();
a.a = data;
return a;
}
and will be called like that:
var a = A.deserialize("a");
2. Create method in prototype
A.prototype.deserialize = function ( data )
{
this.a = data;
}
and it will be called like that
var a = new A();
a.deserialize( "a" );
3. Process data in contstructor
function A(data)
{
this.a = data;
}
Consider that data can be different types for example - string, json or ArrayBuffer.
I'm looking for a more generic solution. Is there any matter how I will create object?

You can use generic solution for (de)serializing other objects as JSON and make an utility function out of it. See more at How to serialize & deserialize Javascript objects?
If you prefer deserializing each object type by itself.
Static method approach
Pros:
Static method handles whole object creation and setting values cleanly, not creating any unnecessary temp objects and such. By far the most cleaner solution.
This method doesn't require object to be aware of serialization process, and it can be easily added to existing solution
Prototype method approach
Cons:
This variant pollutes prototype chain.
You have to create an object for sake of creating an object unless you want to use it to fill it up from inside. That can be problematic if there is for instance logic in constructor, that needs to be executed.
Process data in constructor
Cons:
Constructor needs to be overloaded. It can be difficult to recognize, if data passed to constructor are normal or serialized data. e.g. normally it takes some string and serialized data is string as well.

i wrote this jquery function, makes it very easy to serialize any form data.
$.fn.serializeObject = function () {
var result = {};
this.each(function () {
var this_id = (this.id.substring(this.id.length - 2) == '_s') ? this.id.substring(0, this.id.length - 2) : this.id.replace('_val', '');
if (this.type == 'radio') {
var this_id = this.name;
result[this_id.toLowerCase()] = $('input[name=' + this_id + ']:checked').val();
}
else if (this.type == 'checkbox') {
result[this_id.toLowerCase()] = $('#' + this_id).prop('checked');
}
else {
if (this_id.indexOf('___') > -1) {
this_id = this_id.substring(0, this_id.indexOf('___'));
}
result[this_id.toLowerCase()] = this.value;
}
});
return result;
};
you can easily call it by doing var form_vars = $('#div input, #div select, #div textarea').serializeForm()
you can add additional properties to the object by doing form_vars.property = 'value';, you can also even add js arrays and json objects to it. then you can use $.ajax to submit.

Related

Load object with logic

I believe that solution to my issue is relatively easy. I just don't see it.
I have an object:
function MyObject(){
this.attr = "anything";
}
MyObject.prototype.doSomething = function(){
// logic
}
I create the object using new MyObject(). I use it and when I want to quit what I do I simply store it into database (mongodb). In mongo it is stored in this way:
{ "attr" : "anything" }
When I load the object from database I only have the plain object literal without any logic. The methods are missing. I can see why ;) but I don't know how to add the logic to the object literal again...
Question
How can I decorate the retrieved object with it's original logic again? So that it look like this again:
{
"attr" : "anything",
"doSomething": doSomething()
}
How to do it simply?
Is there any other approach to this (except for storing the methods and all prototype hierarchy with it)?
Make a load function to load all the properties of the object you get back into your own object.
You can access your objects property names as an associative array index.
so myobj.attr is the same as myobj['attr'].
This helps with dynamically inserting data into your object whilst keeping full control of the data(my personal favorite) :-)
You can add some extra checks to prevent surplus data or do some extra things whatever you want. For example modifying timestamps.
function MyObject(data){
if(typeof data !== 'undefined') {
this.load(data);
}
else {
this.attr = "anything";
}
}
MyObject.prototype.doSomething = function(){
// logic
}
MyObject.prototype.load = function(data) {
for(var key in data) {
if(data.hasOwnProperty(key)) {
this[key] = data[key];
// Just sample validation check. wahtever you want.
if(key == 'timestamp') {
if(this[key] < new Date().getTime()-4000) {
this[key] = new Date().getTime();
}
}
}
}
}
You can use Object.create to create an object from the prototype without calling the constructor, and then use Object.assign to assign the properties from the object that you retrieved, to the newly created object:
var fullObject = Object.assign( Object.create( MyObject.prototype ), retrievedObject );
Example:
function MyObject(){
this.attr = "anything";
}
MyObject.prototype.doSomething = function(){
document.body.innerHTML = this.attr;
}
// Plain object retrieved from database
var retrievedObject = {
"attr" : "foobar"
};
// Object with proper prototype and properties
var myObject = Object.assign( Object.create( MyObject.prototype ), retrievedObject );
myObject.doSomething();
An extension to #paulpro's answer which I had to modify a little this piece of code Object.create( MyObject.prototype ). As I was using an inherited object this way it didn't instantiate private members properly. I simply replaced Object.create( MyObject.prototype ) with new MyObject(). That's it.
UPDATE 5/3/2016
The safest way is to use lodash's assing.
var objectWithLogic = _.assign(new MyObject(), data);

Can I smuggle custom constructors in the toJSON overriden method?

The JSON.stringify behavior can be altered by overriding .toJSON method:
var obj = {toJSON: function() {return [1,2,3];}};
var x = JSON.stringify(dd);
console.log(x); // "[1,2,3]"
JSON.parse(x); // [1,2,3]
I'd like to pass the javascript pseudo-class instances (objects inheriting from other objects). However it doesn't seem possible to add any function call in the data:
function Pseudoclass(x) {
this.x = x;
//More operations
}
If you return function, .stringify fails. Not talking about the fact that it doesn't seem very possible to pass the class properties:
//JSON.stringify(inst) will be undefined
Pseudoclass.prototype.toJSON = function() {
//If converted to string, the function loses variables from this scope
return function() {return new Pseudoclass();};
}
If you return string, it's encoded as string:
//JSON.stringify(inst) returns "\"Pseudoclass.fromJSON(6)\"" if x was 6
Pseudoclass.prototype.toJSON = function() {
return "Pseudoclass.fromJSON("+this.x+")";
}
So does anybody has any hacks in mind? I'd use this to pass prepared class instances to Worker - the only option there is figuring this JSON question or a custom format.
Don't forget that the constructor call may not be the only way. All I need is that the object inherits from my predefined object!

Convert arguments into new object parameter without cloning the object?

I want to create a new object with parameters from 'arguments', but I don't know
how to or even possible to convert it directly without cloning. Here's how it is possible using a clone:
function theClass(name, whatever) {
this.name = name;
this.whatever = whatever;
}
// I need to use the arguments passed from this function but without using clone
// as shown.
function doSomething()
{
function clone(args) {
theClass.apply(this, args);
}
clone.prototype = theClass.prototype;
return new clone(arguments);
}
// Usage expectation.
var myNewObject = doSomething("Honda", "motorbike");
console.log(myNewObject.name);
However, this suffers on performance because each time you call doSomething, you have to create a clone just to pass that arguments to be applied in it from theClass.
Now I want to pass that arguments without passing to a cloned object, but I don't know
how to convert it directly.
Any idea?
Note: As clarified by kaminari, the parameters passed are not strictly 'name' and 'whatever', but could be anything depends on the object I want to create. 'theClass' in the code is merely an example.
Thanks.
EDIT: In light of the intended use of these functions:
Probably your best option on maintaining your intended behavior is to implement your function in the following way:
function theClass(options){
this.name = options.name || ''; //or some other default value
this.whatever = options.whatever || '';
};
function doSomething(options){
options = options || {};
return new theClass(options);
};
With this implementation in mind, the code you supplied in "usage expectation" would look like this:
var myNewObject = doSomething({name: "honda", whatever: "motorbike"});
console.log(myNewObject.name);
In this manner, theClass can support as many or as few parameters as need be (only depends on what's supplied in the object and what you choose to extract from it) and similarly, the wrapper doSomething can be given as many or as few options as desired.
this suffers on performance because each time you call doSomething, you have to create a clone just to pass that arguments to be applied in it from theClass.
Simply define the clone function outside of doSomething, and it won't get recreated every time:
function theClass(name, whatever) {
this.name = name;
this.whatever = whatever;
}
function clone(args) {
theClass.apply(this, args);
}
clone.prototype = theClass.prototype;
function doSomething() {
return new clone(arguments);
}

Is encapsulation required for JavaScript objects?

I'm developing a node.js application. Looking for ways to create the datamodel.
The data being sent to/from the client is JSON. Since database is MongoDb, the data to/from the db is JSON.
I'm new to JS, I could find so many js libraries dedicated to creating encapsulated objects. Is it still required?
What are the possible consequence of just defining models as simple js objects, and use prototype based inheritance where necessary?
Any help is appreciated. Thanks.
What are the possible consequence of just defining models as simple js
objects, and use prototype based inheritance where necessary?
IMO, You will lose maintainability over the time as your application size increases or your team size increases as many developers start working on the same code.
In other words - Without proper encapsulation it is easy to modify objects one doesn't own - easy to meddle with the parts that you don't want to be touched.
If you are writing a library/framework of some sort where the just APIs are exposed to the user and you don't have proper encapsulation one could probably bring everything down by just one modification.
For example:
var myObj = {
mySecretPrivateCrucialFunction: function () {
// all the awesome crucial logic
},
//This is what you want other parts of the code ...
// ... to be interacting with in this object
myPublicMethod: function () {
// some logic
mySecretPrivateCrucialFunction();
// some thing else
}
}
I can do this.
myObj.mySecretPrivateCrucialFunction = function () {
alert('yay..i got you..');
};
But if you do this way - you don't give that chance.
var myObj = (function () {
var mySecretPrivateCrucialFunction = function () {
// all the awesome crucial logic
}
//This is what you want other parts of the code ...
// ... to be interacting with in this object
return {
myPublicMethod: function () {} /*some logic */
mySecretPrivateCrucialFunction(); /*some thing else */
}
})();
In case you want to make all your properties hidden/private and still want to get the JSON representation of the object - you can do something like this -
var myObj = (function () {
// your private properties
var prop1 = 1;
var prop2 = 2;
// your getters
var getProp1 = function () {
return prop1;
};
var getProp2 = function () {
return Prop2;
};
// your setters
var setProp1 = function (newValue) {
prop1 = newValue;
};
var setProp2 = function (newValue) {
prop2 = newValue;
};
// your JSON representation of the object
var toString = function () {
return JSON.stringify({
prop1: prop1,
prop2: prop2
});
};
// Object that gets exposed -
return {
"getProp1": getProp1,
"getProp2": getProp2,
"setProp1": setProp1,
"setProp2": setProp2,
"toString": toString
}
})();
There are two ways to approach this question as I see it:
I'm assuming that your data is being transfered as a JSON string.
"[{\"key\":\"val\"}]" is showing up in your responses, and you are then putting them through JSON.parse to turn them into viable arrays and objects.
So the first way would be to make "class-instances" (no new or inheritance necessary, just a constructor function which encapsulates data and exposes an interface, based on the data-type).
function makeTweet (data) {
var tweet = {
from_user : data.from_user || "anonymous",
/* ... */
},
toString = function () {},
public_interface : {
toString : toString,
/* getters, etc */
};
return public_interface;
}
I know you already know this stuff, but consider a situation where you've got two or three different data-types inside of the same process (like at the end of the line, when you're ready to print to the client), and you have a process which is reading and writing to public fields on every object. If different objects have different properties, things end poorly, or end in a sea of flakey if statements.
The other way to look at it might be an entity/service system
function handleTweets (tweetArr) {
var templateFormat = system.output_format,
string = "";
if (templateFormat === "HTML") {
string = HTMLtemplateTweets(tweetArr);
} else { /* ... */ }
}
function HTMLtemplateTweets (tweetArr) {}
function JSONtemplateTweets (tweetArr) {}
function XMLtemplateTweets (tweetArr) {}
...
With the point being that you would turn the JSON string into an array of data-only objects, and feed them down a line of type-specific library/system functions.
This would be more like a very simplified entity/system approach, rather than an OO (as classically accepted) approach.
Now, your data safety comes from making sure that your objects only ever go down one intended path, and thus transforms will be predictable for every object of that type.
If you want "inheritance" for performance/memory purposes in JS, then this might also be a direction to look.

Javascript apply methods from one object to another

I have been at this for hours and just can't get it quite right. I have an object with methods that works fine. I need to save it as a string using JSON.stringify and then bring it back as an object and still use the same methods.
function Workflow(){
this.setTitle = function(newtitle){this.title = newtitle; return this;};
this.getTitle = function(){return this.title;};
}
function testIn(){
var workflow = new Workflow().setTitle('Workflow Test');
Logger.log(workflow);//{title=Workflow Test}
Logger.log(workflow.getTitle()); //Workflow Test
var stringy = JSON.stringify(workflow);
var newWorkflow = Utilities.jsonParse(stringy);
Logger.log(newWorkflow); //{title=Workflow Test}
//Looks like the same properties as above
Logger.log(newWorkflow.getTitle());//Error can't find getTitle
}
I think I should prototype the new object but nothing seems to work.
Please help I have very little hair left.
You need to copy the method to the new object:
newWorkflow.getTitle = workflow.getTitle;
you are losing your functions when you stringify and parse.
if you have access to jquery, the $.extend is handy (if not, just copy&paste form jquery source)
here's a demo:
http://jsfiddle.net/VPfLc/
Serializing to JSON won't store executable code. It's being removed from your object when calling JSON.stringify. Your best bet is to make the object so it can be initialized when created.
function Workflow(){
this.initialize = function(properties) { this.title = properties.title; }
this.setTitle = function(newtitle){this.title = newtitle; return this;};
this.getTitle = function(){return this.title;};
}
function testIn(){
var workflow = new Workflow().setTitle('Workflow Test');
Logger.log(workflow);//{title=Workflow Test}
Logger.log(workflow.getTitle()); //Workflow Test
var stringy = JSON.stringify(workflow);
var newWorkflow = new Workflow().initialize(Utilities.jsonParse(stringy));
Logger.log(newWorkflow); //{title=Workflow Test}
//Looks like the same properties as above
Logger.log(newWorkflow.getTitle());//Error can't find getTitle
}
All you have to do is use call.
Workflow.call(newWorkflow);
EDIT:
If your actual Workflow() implementation sets any attributes to default values during its initilization then calling on your new json object will also reset those. Which is what I'm assuming is going on, without being able to look at your actual implementation code.
If that is the case, then you have two options.
1) Rather than blindly initilize your object (and assuming its empty), conditionally initilize your variables.
function Workflow(){
this.setTitle = function(newtitle){this.title = newtitle; return this;};
this.getTitle = function(){return this.title;};
this.array = this.array || [];
}
for new empty objects. this.array will be null, and it'll be set to a new array. calling Workflow on a existing object that already has that property, it'll leave it alone.
2) Extract your methods into an Extension Module
function Workflow(){
this.array = this.array || [];
// Other work
// Lastly include my method extensions.
WorkflowMethodExtensions.call(this);
}
function WorkflowMethodExtensions(){
this.setTitle = function(newtitle){this.title = newtitle; return this;};
this.getTitle = function(){return this.title;};
}
Then use:
WorkflowMethodExtensions.call(newWorkflow);
to extend an existing object with those methods defined in the existion

Categories