Javascript function, objects - javascript

I got an object defined global way:
window.myobj = {};
Then new elements are being added and finally it is being saved via ajax to database by SaveData(obj) function. The problem is, that I am making it alot of times in short period, and it seems that what is being passed to function is only a pointer to one single object, so when another event occures, it changes inside a save function even when it was changed outside. In PHP, you have to use &$obj for it... so how do I do it properly, so the obj passed to the function would not be only a pointer?
Edit:
The problem appeared in here:
Lets say that event = {time:123,type:blah}
function SaveData(event){
obj = jQuery.extend({}, event); // doesn't seem to replicate event
$.post('save',obj,function(res){
if(res) console.log(obj);
});
if(obj.type == 'blah'){
newobj = jQuery.extend({}, obj);
newobj.type = 'newtype';
newobj.time = 234;
SaveData(newobj);
}else if(obj.type == 'newtype'){
newobj = jQuery.extend({}, obj);
newobj.type = 'lasttype';
newobj.time = 345;
SaveData(newobj);
}
}
This returns 3 objects that are the same:
{time:345,type:lasttype}

Assuming your Object myobj does not contain any functions, but only values and further objects, one very easy way to clone the Object (and thus get rid of the problem with pointers) would be:
var clonedObj = JSON.parse(JSON.stringify(window.myobj));
Now you can pass the clonedObj to your SaveData Function.

If you're using jQuery, the answer is as simple as;
var myCopy = $.extend(true, {}, window.myobj);
If not, this function will create a deep copy the object - including functions and nested objects;
function clone(original) {
if (original == null || typeof(original) !== 'object')
return original;
var theClone = original.constructor.call(this);
for (var property in original) {
theClone[property] = clone(original[property]);
}
return theClone;
}

Related

Assign value in javascript

I have two objects : $scope.objectA and $scope.objectB.
I assign value from $scope.objectA to $scope.objectB like this:
$scope.objectB.value1 = $scope.objectA.value1;
then I make value1 of $scope.objectB to null;
$scope.objectB.value1 = null;
My question is why when I assign $scope.objectB.value1 to null, $scope.objectA.value1 is null too. How can I keep value of $scope.objectA.value1 while changing value of $scope.objectB.value1?
Make copy of object B and assign it to the object A. Use angular.copy function. It will creates a deep copy of source.
For more information Visit Angular copy doc
$scope.objectA.value1 = angular.copy($scope.objectB.value1);
Because this is how it works. You make these two variables "bound" together.
If you want to keep value of objectA, then use
$scope.objectB.value1 = angular.copy($scope.objectA.value1);
I think this can happen only if ObjectA and ObjectB refer to the same object on the heap, i.e. it ObjectA and ObjectB are the same objects
$scope['object1'] = {};
$scope['object1']['val'] = {};
$scope['object2'] = {};
$timeout(() => {
this.$scope['object2']['val'] = this.$scope['object1']['val'];
$timeout(() => {
this.$scope['object2']['val'] = null;
console.log(this.$scope['object1']['val']); // Object {}
})
});
-
$scope['object1'] = {};
$scope['object1']['val'] = {};
$scope['object2'] = {};
$timeout(() => {
this.$scope['object2']['val'] = this.$scope['object1']['val'];
$timeout(() => {
this.$scope['object2']['val'] = null;
console.log(this.$scope['object1']['val']); // null
})
});
The reason is when you assign object to a variable, the assignment will be by reference, so the old and new will be a reference to the original object
So when you edit an object, you're actually editing the original object.
The solution
Angular: use object = angular.copy(myObject1)
jQuery: object = $.extend({}, myObject1);

Merge objects dynamically - Object Property Name Issue

I'm trying to merge objects together, or add new objects to existing objects.
I've already got it working as for merge using jQuery, but it's the name of the property that wont work dynamically.
The parameter filters is an object, such as {test:123}.
When invoking filter({test:123});, I want the filter function to dynamically add objects to a global object. (and of course can't use push() since its not an array)
this.filter = function(filters) {
for (var key in filters) {
$.extend( settings.filter, {key:filters[key]} );
}
};
The problem is that "key" turns into "key" as the name of the property. When it should be "test" as the property name; I can not get the property name to be created dynamically.
The goal would be to allow the user to fire the function like this:
filter({test:123,test2:1321,test55:4})
and dynamically add objects to the global settings object without the user meddling with the object itself.
Your code does not work because key is not being interpreted as a variable when being directly set in the object.
$.extend( settings.filter, {key:filters[key]} );
Considering:
var keyName = "Test";
var badObj = { keyName: "Hello World" };
You would get undefined when calling newObj.Test because it is actually newObj.keyName.
In order to use a variable to create a property name, you need to use different syntax.
var keyName = "Test";
var newObj = {};
newObj[keyName] = "Hello World";
You could then reference and use newObj.Test to get "Hello World"
To fix the method you provided, you can adjust it to:
this.filter = function(filters) {
for (var key in filters) {
if (filters.hasOwnProperty(key)) {
var newObj = {};
newObj[key] = filters[key];
$.extend(settings.filter, newObj);
}
}
};
Keep in mind you can simplify this and just use the extend method. This would be better, unless you are looking to do your own filtering as the method name suggests.
this.filter = function(filters) {
$.extend(settings.filter, filters);
};
Demos
You should create temp obj before extend :
this.filter = function(filters) {
for (var key in filters) {
var obj = {};
obj[key] = filters[key];
$.extend( settings.filter, obj );
}
};

Using JSON.stringify on custom class

I'm trying to store an object in redis, which is an instance of a class, and thus has functions, here's an example:
function myClass(){
this._attr = "foo";
this.getAttr = function(){
return this._attr;
}
}
Is there a way to store this object in redis, along with the functions? I tried JSON.stringify() but only the properties are preserved. How can I store the function definitions and be able to perform something like the following:
var myObj = new myClass();
var stringObj = JSON.stringify(myObj);
// store in redis and retreive as stringObj again
var parsedObj = JSON.parse(stringObj);
console.log(myObj.getAttr()); //prints foo
console.log(parsedObj.getAttr()); // prints "Object has no method 'getAttr'"
How can I get foo when calling parsedObj.getAttr()?
Thank you in advance!
EDIT
Got a suggestion to modify the MyClass.prototype and store the values, but what about something like this (functions other than setter/getter):
function myClass(){
this._attr = "foo";
this._accessCounts = 0;
this.getAttr = function(){
this._accessCounts++;
return this._attr;
}
this.getCount = function(){
return this._accessCounts;
}
}
I'm trying to illustrate a function that calculates something like a count or an average whenever it is called, apart from doing other stuff.
First, you are not defining a class.
It's just an object, with a property whose value is a function (All its member functions defined in constructor will be copied when create a new instance, that's why I say it's not a class.)
Which will be stripped off when using JSON.stringify.
Consider you are using node.js which is using V8, the best way is to define a real class, and play a little magic with __proto__. Which will work fine no matter how many property you used in your class (as long as every property is using primitive data types.)
Here is an example:
function MyClass(){
this._attr = "foo";
}
MyClass.prototype = {
getAttr: function(){
return this._attr;
}
};
var myClass = new MyClass();
var json = JSON.stringify(myClass);
var newMyClass = JSON.parse(json);
newMyClass.__proto__ = MyClass.prototype;
console.log(newMyClass instanceof MyClass, newMyClass.getAttr());
which will output:
true "foo"
No, JSON does not store functions (which would be quite inefficient, too). Instead, use a serialisation method and a deserialisation constructor. Example:
function MyClass(){
this._attr = "foo";
this.getAttr = function(){
return this._attr;
}
}
MyClass.prototype.toJSON() {
return {attr: this.getAttr()}; // everything that needs to get stored
};
MyClass.fromJSON = function(obj) {
if (typeof obj == "string") obj = JSON.parse(obj);
var instance = new MyClass;
instance._attr = obj.attr;
return instance;
};
Scanales, I had the same issue and tried a technique similar to Bergi's recommendation of creating new serialization/deserialization methods...but found it didn't work for me because I have objects nested in objects (several deep). If that's your case then here's how I solved it. I wrote a base class (clsPersistableObject) from which all objects that I wanted to persist inherited from. The base class has a method called deserialize, which is passed the JSON string. This method sets the properties one by one (but does not wipe out the exist methods) and then recursively defer to the child object to do the same (as many times as necessary).
deserialize: function (vstrString) {
//.parse: convert JSON string to object state
//Use JSON to quickly parse into temp object (does a deep restore of all properties)
var tmpObject = JSON.parse(vstrString);
//objZoo2.animal.move();
//Note: can't just do something like this:
// CopyProperties(tmpObject, this);
//because it will blindly replace the deep objects
//completely...inadvertently wiping out methods on it. Instead:
//1) set the properties manually/one-by-one.
//2) on objects, defer to the deserialize on the child object (if it inherits clsPersistableObject)
//2b) if it doesn't inherit it, it's an intrinsic type, etc...just do a JSON parse.
//loop through all properties
var objProperty;
for (objProperty in tmpObject) {
//get property name and value
var strPropertyName = objProperty;
var strPropertyValue = tmpObject[objProperty]; //note: doing this .toString() will cause
if (objProperty !== undefined) {
//check type of property
if (typeof strPropertyValue == "object") {
//object property: call it recursively (and return that value)
var strPropertyValue_AsString = JSON.stringify(strPropertyValue);
//see if has a deserialize (i.e. inherited from clsPeristableObject)
if ("deserialize" in this[objProperty]) {
//yes: call it
this[objProperty]["deserialize"](strPropertyValue_AsString);
}
else {
//no: call normal JSON to deserialize this object and all below it
this[objProperty] = JSON.parse(strPropertyValue_AsString);
} //end else on if ("deserialize" in this[objProperty])
}
else {
//normal property: set it on "this"
this[objProperty] = tmpObject[objProperty];
} //end else on if (typeof strPropertyValue == "object")
} //end if (objProperty !== undefined)
}
}
it looks like you attempt to stringify a closed function. you can use ()=>{} to solve the scope problem.
function myClass(){
this._attr = "foo";
this._accessCounts = 0;
this.getAttr = ()=>{
this._accessCounts++;
return this._attr;
}
this.getCount = ()=>{
return this._accessCounts;
}
}
What you get back grom JSON.stringify() is a String. A string has no methods.
You need to eval first that string and then you'll be able to get the original object
and its methods.
var myObj = new myClass();
var stringObj = JSON.stringify(myObj);
---- EDIT -----
//Sorry use this:
var getBackObj = JSON.parse(stringObj);
//Not this
var getBackObj = eval(stringObj);
console.log(getBackObj.getAttr()); // this should work now

How to stringify inherited objects to JSON?

json2.js seems to ignore members of the parent object when using JSON.stringify(). Example:
require('./json2.js');
function WorldObject(type) {
this.position = 4;
}
function Actor(val) {
this.someVal = 50;
}
Actor.prototype = new WorldObject();
var a = new Actor(2);
console.log(a.position);
console.log(JSON.stringify(a));
The output is:
4
{"someVal":50}
I would expect this output:
4
{"position":0, "someVal":50}
Well that's just the way it is, JSON.stringify does not preserve any of the not-owned properties of the object. You can have a look at an interesting discussion about other drawbacks and possible workarounds here.
Also note that the author has not only documented the problems, but also written a library called HydrateJS that might help you.
The problem is a little bit deeper than it seems at the first sight. Even if a would really stringify to {"position":0, "someVal":50}, then parsing it later would create an object that has the desired properties, but is neither an instance of Actor, nor has it a prototype link to the WorldObject (after all, the parse method doesn't have this info, so it can't possibly restore it that way).
To preserve the prototype chain, clever tricks are necessary (like those used in HydrateJS). If this is not what you are aiming for, maybe you just need to "flatten" the object before stringifying it. To do that, you could e.g. iterate all the properties of the object, regardless of whether they are own or not and re-assign them (this will ensure they get defined on the object itself instead of just inherited from the prototype).
function flatten(obj) {
var result = Object.create(obj);
for(var key in result) {
result[key] = result[key];
}
return result;
}
The way the function is written it doesn't mutate the original object. So using
console.log(JSON.stringify(flatten(a)));
you'll get the output you want and a will stay the same.
Another option would be to define a toJSON method in the object prototype you want to serialize:
function Test(){}
Test.prototype = {
someProperty: "some value",
toJSON: function() {
var tmp = {};
for(var key in this) {
if(typeof this[key] !== 'function')
tmp[key] = this[key];
}
return tmp;
}
};
var t = new Test;
JSON.stringify(t); // returns "{"someProperty" : "some value"}"
This works since JSON.stringify searches for a toJSON method in the object it receives, before trying the native serialization.
Check this fiddle: http://jsfiddle.net/AEGYG/
You can flat-stringify the object using this function:
function flatStringify(x) {
for(var i in x) {
if(!x.hasOwnProperty(i)) {
// weird as it might seem, this actually does the trick! - adds parent property to self
x[i] = x[i];
}
}
return JSON.stringify(x);
}
Here is a recursive version of the snippet #TomasVana included in his answer, in case there is inheritance in multiple levels of your object tree:
var flatten = function(obj) {
if (obj === null) {
return null;
}
if (Array.isArray(obj)) {
var newObj = [];
for (var i = 0; i < obj.length; i++) {
if (typeof obj[i] === 'object') {
newObj.push(flatten(obj[i]));
}
else {
newObj.push(obj[i]);
}
}
return newObj;
}
var result = Object.create(obj);
for(var key in result) {
if (typeof result[key] === 'object') {
result[key] = flatten(result[key]);
}
else {
result[key] = result[key];
}
}
return result;
}
And it keeps arrays as arrays. Call it the same way:
console.log(JSON.stringify(flatten(visualDataViews)));
While the flatten approach in general works, the snippets in other answers posted so far don't work for properties that are not modifiable, for example if the prototype has been frozen. To handle this case, you would need to create a new object and assign the properties to this new object. Since you're just stringifying the resulting object, object identity and other JavaScript internals probably don't matter, so it's perfectly fine to return a new object. This approach is also arguably more readable than reassigning an object's properties to itself, since it doesn't look like a no-op:
function flatten(obj) {
var ret = {};
for (var i in obj) {
ret[i] = obj[i];
}
return ret;
}
JSON.stringify takes three options
JSON.stringify(value[, replacer[, space]])
So, make use of the replacer, which is a function, that is called recursively for every key-value-pair.
Next Problem, to get really everything, you need to follow the prototpes and you must use getOwnPropertyNames to get all property names (more than you can catch with keysor for…in):
var getAllPropertyNames = () => {
const seen = new WeakSet();
return (obj) => {
let props = [];
do {
if (seen.has(obj)) return [];
seen.add(obj);
Object.getOwnPropertyNames(obj).forEach((prop) => {
if (props.indexOf(prop) === -1) props.push(prop);
});
} while ((obj = Object.getPrototypeOf(obj)));
return props;
};
};
var flatten = () => {
const seen = new WeakSet();
const getPropertyNames = getAllPropertyNames();
return (key, value) => {
if (value !== null && typeof value === "object") {
if (seen.has(value)) return;
seen.add(value);
let result = {};
getPropertyNames(value).forEach((k) => (result[k] = value[k]));
return result;
}
return value;
};
};
Then flatten the object to JSON:
JSON.stringify(myValue, flatten());
Notes:
I had a case where value was null, but typeof value was "object"
Circular references must bee detected, so it needs seen

How to write a javascript clone function that can copy added object methods?

I have a javascript object cloning question. I'd like to be able to clone object methods that have been altered from those defined by the object prototype, or added to the object after instantiation. Is this possible?
The setting here is a javascript 'class' defined by me, so I'm fine with writing a clone method specific to my object class. I just can't figure out how to copy methods.
Example:
function myObject( name, att, dif ) {
/* 'privileged' methods */
this.attribute = function(newAtt) { // just a getter-setter for the 'private' att member
if(newAtt) { att = newAtt; }
return att;
}
// 'public' members
this.printName = name;
}
myObject.prototype.genericMethod = function() {
// does what is usually needed for myObjects
}
/* Create an instance of myObject */
var object153 = new myObject( '153rd Object', 'ABC', 2 );
// object153 needs to vary from most instances of myObject:
object153.genericMethod = function() {
// new code here specific to myObject instance object153
}
/* These instances become a collection of objects which I will use subsets of later. */
/* Now I need to clone a subset of myObjects, including object153 */
var copyOfObject153 = object153.clone();
// I want copyOfObject153 to have a genericMethod method, and I want it to be the one
// defined to be specific to object153 above. How do I do that in my clone() method?
// The method really needs to still be called 'genericMethod', too.
In your clone function, test each method on the object to see if it is equal to the same method on the object's constructor's prototype.
if (obj[method] != obj.constructor.prototype[method])
clone[method] = obj[method];
It sounds like you just want a shallow copy. However beware of that objects are shared among instances since we're not deep copying.
function clone(obj) {
var newObj = new obj.constructor();
for (var prop in obj) {
newObj[prop] = obj[prop];
}
return newObj;
}
var cloned = clone(object153);
A different syntax would be
myObj.prototype.clone = function() {
var newObj = new this.constructor();
for (var prop in this) {
newObj[prop] = this[prop];
}
return newObj;
}
var cloned = object153.clone();
Try it out and see if it works for you, it's still hard to tell what you're doing. If it doesn't, explain why, then I can better understand the problem.

Categories

Resources