How to reset na object property to the default one? - javascript

I am using jQuery and I am still pretty new to JavaScript. I am implementing an object as the following:
MyObject = {
properties : [{}],
resetProperties: function resetProperties() { this.properties = [{}] }
};
As you can see in the above code I can reset the properties by running MyObject.resetProperties() but, in order to do that, I state two times the [{}] variable. How should I accomplish the same thing without repeating that code?
Update
I tried to do the following:
MyObject = {
properties : this.propertiesDefault,
resetProperties : function resetProperties() { this.properties = [{}] },
propertiesDefault: [{}]
};
but I get "TypeError: invalid 'in' operand MyObject.properties" and I am not sure that is the right way to proceed.

It seems to me that it would be impossible to avoid having your default / reset properties as a separate object to the one that will be modified.
I would recommend having a default value, and cloning it in your initialisation and reset function. Since you tagged your question with jQuery, I assume you are happy to clone the object with that:
MyObject = {
defaultProperties : [{}],
properties : jQuery.extend(true, {}, this.defaultProperties),
resetProperties: function() {
this.properties = jQuery.extend(true, {}, this.defaultProperties);
}
};
See this Stack Overflow question for more information on cloning objects:
What is the most efficient way to deep clone an object in JavaScript?
This is the documentation for jQuery.extend:
http://docs.jquery.com/Utilities/jQuery.extend

From what I know this isn't possible. You're going to have to hard-code the property reset. I tried setting a variable cache outside the object, but when I reset the property it unfortunately maintains its value.
var obj = {
p: [ {} ],
r: function() { this.p = this.cache; }
};
obj.cache = obj.p; // attempt to set to original
obj.p[0].m = 5; // modify
obj.r(); // reset
--------
>>> obj.p[0].m; // 5
We can assume the the cache property is being modified in the same way as p is. Therefore, we can't reset like that.

Depends on what you want. Since you're new to javascript, you may be unfamiliar with using functions to create custom objects, which is the general javascript "OOP" kinda way to do it.
function MyObjectClass() {
this.properties = null;
this.resetProperties();
}
MyObjectClass.prototype.resetProperties = function () { this.properties = [{}] };
var MyObject= new MyObjectClass();
But we don't really know that function MyObject needs to fulfill. There may be a requirement that it NEEDs to be a plain old javascript object. Or maybe not, and you're done.
Of course, you can always directly:
MyObject = {
properties : null,
resetProperties: function () { this.properties = [{}];}
};
MyObject.resetProperties();

Related

How to serialize a javascript object into json file that include all Class methods? [duplicate]

I am new to "object-oriented" JavaScript. Currently, I have an object that I need to pass across pages. My object is defined as follows:
function MyObject() { this.init(); }
MyObject.prototype = {
property1: "",
property2: "",
init: function () {
this.property1 = "First";
this.property2 = "Second";
},
test: function() {
alert("Executing test!");
}
}
On Page 1 of my application, I am creating an instance of MyObject. I am then serializing the object and storing it in local storage. I am doing this as shown here:
var mo = new MyObject();
mo.test(); // This works
window.localStorage.setItem("myObject", JSON.stringify(mo));
Now, on Page 2, I need get that object and work with it. To retrieve it, I am using the following:
var mo = window.localStorage.getItem("myObject");
mo = JSON.parse(mo);
alert(mo.property1); // This shows "First" as expected.
mo.test(); // This does not work. In fact, I get a "TypeError" that says "undefined method" in the consol window.
Based on the outputs, it looks like when I serialized the object, somehow the functions get dropped. I can still see the properties. But I can't interact with any of my functions. What am I doing wrong?
JSON doesn't serialize functions.
Take a look at the second paragraph here.
If you need to preserve such values, you can transform values as they are serialized, or prior to deserialization, to enable JSON to represent additional data types.
In other words, if you really want to JSONify the functions, you can convert them to strings before serializing:
mo.init = ''+mo.init;
mo.test = ''+mo.test;
And after deserializing, convert them back to functions.
mo.init = eval(mo.init);
mo.test = eval(mo.test);
However, there should be no reason to do that. Instead, you can have your MyObject constructor accept a simple object (as would result from parsing the JSON string) and copy the object's properties to itself.
Functions can not be serialized into a JSON object.
So I suggest you create a separate object (or property within the object) for the actual properties and just serialize this part.
Afterwards you can instantiate your object with all its functions and reapply all properties to regain access to your working object.
Following your example, this may look like this:
function MyObject() { this.init(); }
MyObject.prototype = {
data: {
property1: "",
property2: ""
},
init: function () {
this.property1 = "First";
this.property2 = "Second";
},
test: function() {
alert("Executing test!");
},
save: function( id ) {
window.localStorage.setItem( id, JSON.stringify(this.data));
},
load: function( id ) {
this.data = JSON.parse( window.getItem( id ) );
}
}
To avoid changing the structure, I prefer to use Object.assign method on object retrieval. This method merge second parameter object in the first one. To get object methods, we just need an empty new object which is used as the target parameter.
var mo = window.localStorage.getItem("myObject");
// this object has properties only
mo = JSON.parse(mo);
// this object will have properties and functions
var completeObject = Object.assign(new MyObject(), mo);
Note that the first parameter of Object.assign is modified AND returned by the function.
it looks like when I serialized the object, somehow the functions get dropped... What am I doing wrong?
Yes, functions will get dropped when using JSON.stringify() and JSON.parse(), and there is nothing wrong in your code.
To retain functions during serialization and deserialization, I've made an npm module named esserializer to solve this problem -- the JavaScript class instance values would be saved during serialization on Page 1, in plain JSON format, together with its class name information:
var ESSerializer = require('esserializer');
function MyObject() { this.init(); }
MyObject.prototype = {
property1: "",
property2: "",
init: function () {
this.property1 = "First";
this.property2 = "Second";
},
test: function() {
alert("Executing test!");
}
}
MyObject.prototype.constructor=MyObject; // This line of code is necessary, as the prototype of MyObject has been overridden above.
var mo = new MyObject();
mo.test(); // This works
window.localStorage.setItem("myObject", ESSerializer.serialize(mo));
Later on, during the deserialization stage on Page 2, esserializer can recursively deserialize object instance, with all types/functions information retained:
var mo = window.localStorage.getItem("myObject");
mo = ESSerializer.deserialize(mo, [MyObject]);
alert(mo.property1); // This shows "First" as expected.
mo.test(); // This works too.
That's because JSON.stringify() doesn't serialize functions i think.
You're right, functions get dropped. This page might help:
http://www.json.org/js.html
"Values that do not have a representation in JSON (such as functions and undefined) are excluded."

Disable property mutation in JS

I was creating a component and was trying to break my implementation. The idea is to not allow user to manipulate the exposed properties.
The implementation was like this:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data; },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
As you see, setting the property is handled but user is still able to mutate it using .push. This is because I'm returning a reference.
I have handled this case like:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data.slice(); },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
As you see, I'm returning a new array to solve this. Not sure how it will affect performance wise.
Question: Is there an alternate way to not allow user to mutate properties that are of type object?
I have tried using writable: false, but it gives me error when I use it with get.
Note: I want this array to mutable within class but not from outside.
Your problem here is that you are effectively blocking attempts to modify MyClass. However, other objects members of MyClass are still JavaScript objects. That way you're doing it (returning a new Array for every call to get) is one of the best ways, though of course, depending of how frequently you call get or the length of the array might have performance drawbacks.
Of course, if you could use ES6, you could extend the native Array to create a ReadOnlyArray class. You can actually do this in ES5, too, but you lose the ability to use square brackets to retrieve the value from a specific index in the array.
Another option, if you can avoid Internet Explorer, is to use Proxies (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy).
With a proxy, you can trap calls to get properties of an object, and decide what to return or to do.
In the example below, we create a Proxy for an array. As you see in the handler, we define a get function. This function will be called whenever the value of a property of the target object is accessed. This includes accessing indexes or methods, as calling a method is basically retrieving the value of a property (the function) and then calling it.
As you see, if the property is an integer number, we return that position in the array. If the property is 'length' then we return the length of the array. In any other case, we return a void function.
The advantage of this is that the proxyArray still behaves like an array. You can use square brackets to get to its indexes and use the property length. But if you try to do something like proxyArray.push(23) nothing happens.
Of course, in a final solution, you might want decide what to do based on which
method is being called. You might want methods like map, filter and so on to work.
And finally, the last advantage of this approach is that you keep a reference to the original array, so you can still modify it and its values are accessible through the proxy.
var handler = {
get: function(target, property, receiver) {
var regexp = /[\d]+/;
if (regexp.exec(property)) { // indexes:
return target[property];
}
if (property === 'length') {
return target.length;
}
if (typeof (target[property]) === 'function') {
// return a function that does nothing:
return function() {};
}
}
};
// this is the original array that we keep private
var array = [1, 2, 3];
// this is the 'visible' array:
var proxyArray = new Proxy(array, handler);
console.log(proxyArray[1]);
console.log(proxyArray.length);
console.log(proxyArray.push(32)); // does nothing
console.log(proxyArray[3]); // undefined
// but if we modify the old array:
array.push(23);
console.log(array);
// the proxy is modified
console.log(proxyArray[3]); // 32
Of course, the poblem is that proxyArray is not really an array, so, depending on how you plan to use it, this might be a problem.
What you want isn't really doable in JavaScript, as far as I'm aware. The best you can hope for is to hide the data from the user as best you can. The best way to do that would be with a WeakMap
let privateData = new WeakMap();
class MyClass {
constructor() {
privateData.set(this, {
data: []
});
}
addEntry(entry) {
privateData.get(this).data.push(entry);
}
getData() {
return privateData.get(this).data.concat();
}
}
So long as you never export privateData don't export from the module, or wrap within an IIFE etc.) then your MyClass instances will be able to access the data but external forces can't (other than through methods you create)
var myInstance = new MyClass();
myInstance.getData(); // -> []
myInstance.getData().push(1);
myInstance.getData(); // -> []
myInstance.addEntry(100);
myInstance.getData(); // -> [100]

jquery using objects as filters

Is there any way to have DOM elements selectable through objects?
For example I want to be able to associate objects to DOM elements like so:
var obj = { a: 1, b:2 };
$('a').click(function() { this.selectThing = obj });
And later on...
$.something(obj);
Or even better:
$('a|selectThing?=', obj);
Something like that. You can see that I want to associate an object to a DOM element in such a way that I can grab the element with the object.
I know this can be done with the filter() method, my question is if there's a more elegant way that doesn't use filter() to do this.
EDIT:
To clarify, I want to be able to use an object kind of like a selector, so I can do something similar to this $(obj) obviously that won't work, but you get the idea (I hope)
EDIT #2:
I want to be able to do something like this:
var obj = { prop: 'prop' };
$('a').bindTo(obj);
$.retreive(obj) // should equal $('a')
I don't want it to alter obj in any way though (obj should still be {prop: 'prop'} only).
demo
var $div1 = $('.box1');
var $div2 = $('.box2');
var obj = { a: $div1, b: $div2 };
obj.a.css({background:'red'});
Or the short way: var obj = { a: $('.box1'), b: $('.box2') };
demo jsBin 2
var obj = $('.box1, .box2'); // store objects
obj.css({background:'red'}); // access collection
You're looking for $.data. This method associates any JavaScript object or primitive with a DOM element. Under the hood, it's not adding the data as an expando to the DOM element or anything--instead, jQuery maintains its own object cache of DOM elements and data hashes. But that's under the hood; the point is, I think it's exactly what you're looking for.
$('#example').data('foo', { bar: 'quux' }); // returns the jquery object containing '#example', like most jQuery methods
Then, later:
console.log($('#example').data('foo')); // returns {bar: 'quux'}
I dont think this is easily achievable. Let me clarify:
To achieve what you want you would require a hashmap that allows objects in the position of keys. JavaScript does not (yet) support objects as keys in hashmaps though. So, for example, the following does not work:
var key = {value: 'key'};
var data {value: 'data'};
var map = {};
map[key] = data;
There are other solutions to achieve this in current javascript implementations, eg. a double lookup:
var key = {value: 'key'};
var data {value: 'data'};
var map = { keys: [], data: [], get: function (key) {
var k = this.keys.indexOf(key);
if (k >= 0) {
return this.data[k];
} else return undefined;
}, set: function (key, val) {
var k = this.keys.indexOf(key);
if (k < 0) {
k = this.keys.push(k) - 1;
}
this.data[k] = val;
} };
map.set(key, data);
map.get(key).value;
This implementation however is of a terrible performance. There is a proposal for a so called WeakMap in JavaScript Harmony. Firefox I believe is currently the only browser implementing them, though. Since the feature required is not widely available and workarounds are of poor performance I would recommend trying to figure out a different way of achieving what you are trying to.
Extend jQuery with three methods:
jQuery.bindObj(data)
jQuery.unbindObj(data)
$.retrieve(data)
Your code looks like:
$('a').bindObj({blorg: 'shmorg'});
console.log($.retrieve({blorg: 'shmorg'})); // logs live result of $('a');
Full source: http://jsfiddle.net/nUUSV/6/.
The trick to this solution is storing the selectors/identifiers based to the jQuery constructor in one array, and the objects bound to those selectors/identifiers in another array, then using $.inArray to get the index of the object upon retrieval and using that index to grab the bound jQuery collection.
As I understand, you're looking for some sugar way to run multiple named searches on the DOM and have results filtered in a namespace object.
If so, I guess the following jquery extension might be helpfull to you:
$.fn.seek = function (selectors) {
var container = this,
found = {};
$.each(selectors, function (name) {
if ($.isPlainObject(selectors[name])) {
found[name] = $(container).seek(selectors[name]);
}
else if ($.type(selectors[name]) === 'string') {
found[name] = $(container).find(selectors[name]);
}
});
return found;
}
And here's the example of how the above extension might be applicable to your cases:
var res = $('body').seek({
links: 'a',
headers: 'h1,h2,h3,h4,h5,h6'
});
$(res.links).css({ color: 'green' });
$(res.headers).css({ color: 'red' });
I hope this helps you.
Not sure if this is what you are looking for. Perhaps you could write a custom selector based on the jquery selector, which handles objects with a selector-property the way you like. A selectable object would look like
var objSelector = {'selector' : '#select-me', 'a' : 'somestring', 'b' : 1243};
So you are free to use it like any other object, but you have to add the selector property. Than you add your custom selector:
$$ = (function($) {
return function(el, tag) {
if (typeof el === 'object' && el.selector !== undefined) {
return $(el.selector);
}
return $(el);
}
}($));
Now you can do things like
$$(objSelector).css({'border':'1px solid red'});
See an implementation on http://jsfiddle.net/JXcnJ/
If I understood correctly then, I think you need to define a property and say enumerable as false. See below,
Note: Below is just an example to demonstrate and not exactly meant to do such stuff,
DEMO
$(function() {
$.fn.bindTo = function(o) {
var _that = this;
Object.defineProperty(o, 'myFx', {
value: function() { return $(_that); },
writable: true,
enumerable: false,
configurable: true
});
}
$.retrieve = function(obj) {
return obj.myFx();
}
var obj = {
prop: 'prop'
};
$('#test').bindTo(obj);
$($.retrieve(obj)).html('Test');
//below is for proof
for (i in obj) {
alert(obj[i]);
}
});
Reference: http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/

What does this JavaScript mean?

I am working on a legacy application and all JS seems mystrious to me.
Here is the few mysterious lines which are loaded before all and I don't have any idea what they are doing.
var i2b2 = {sdx:{TypeControllers:{},Master:{_sysData:{}}},events:{},hive:{cfg:{},helpers:{},base_classes:{}},h:{}};
if (undefined==i2b2.hive) { i2b2.hive = {}; }
i2b2.hive.tempCellsList = [
{ code: "PM",
forceLoading: true
},
{ code: "ONT" },
{ code: "CRC" },
{ code: "WORK"},
{ code: "SHRINE"},
{ code: "PLUGINMGR",
forceLoading: true,
forceConfigMsg: { params: [] }
}
];
There are many more var and if statements but they are doing same thing with different variables.
Please help me to solve this mystery.
The first line initialises i2b2 using nested object literals.
var obj = {}; is a shorter way of writing var obj = new Object();
A simple object literal will be
var simpleObject = {
property1: "Hello",
property2: "MmmMMm",
property3: ["mmm", 2, 3, 6, "kkk"],
method1: function() {
alert("my method")
}
};
A nested one will be
var rectangle = {
upperLeft: {
x: 2,
y: 2
},
lowerRight: {
x: 4,
y: 4
}
};
Yours is a classic.
var i2b2 = {
sdx: {
TypeControllers: {},
Master: {
_sysData: {}
}
},
events: {},
hive: {
cfg: {},
helpers: {},
base_classes: {}
},
h: {}
};
The second line should be IMHO
i2b2.hive = i2b2.hive || {};
This just says that if hive is undefined create a new object.
The last lines create a property tempCellsList to the object hive. ( Please note that hive in turn is a property of i2b2 )
Lastly a new array of objects are added to the property tempCellsList
This javascript code creates a variable called ib2b that has a number of properties: sdx, events, hive, etc. Those properties hold more composite objects, which are constructed below.
The idea is that this global object can be referenced from other javascript code and it stores global configuration for the client-side application.
I'm not quite sure, what exactly you don't understand. There are two "strange" points about the code above, which I'll try to explain, but if that's not enough you will need to describe better what you don't understand:
The code checks is i2b2.hive is is undefined and set it as an empty object, if it is. Since the property is obviously set in the previous line, my guess is that this code is generated dynamically and some of the logic (such as this check) is defined in the JavaScript code even if it could (should?) be the the server side code.
undefined==i2b2.hive is a bad/wrong way to test "undefinedness", because undefined is not a reserved word in JavaScript.This just works, because undefined is just a variable that - by chance - happens to be undefined. Instead one should use if (typeof i2b2.hive == "undefined") ... or just if (i2b2.hive) ....
It seems like it's setting a few parameters in that i2b2 object. It doesn't "do" anything in itself, but it seems to set a few basic configuration settings for further execution. Try and look for similar occurrences in the code further below.
E.g it sets i2b2.hive.tempCellList[5].forceLoading to true. Later on the application probably has if-conditions, such as
for(var i in i2b2.hive.tempCellList)
{
if(i2b2.hive.tempCellList[i].forceLoading === true)
{
// do something...
}
}

Stuck on a simple object literal scope / referencing issue

Here's a sample of my structure:
this.is.a.really.long.namespace = {
inputs : {},
buttons : {},
panels : {},
fn : {
abc : function() {},
def : function() {}
}
};
Now, as you can see I'm storing my inputs, buttons, panels, and functions in their own respective object literals. The issue lies within fn.abc, fn.def, or any other function inside of page.fn. I want to be able to access my inputs, buttons, and panels from within the functions inside of fn.
Obviously, I know I can type this.is.a.really.long.namespace.inputs, but as you can see, that's pretty long, and I don't want to have to type it out for every single instance where I need to reference an object within the page.
Is there not a way I can directly reference inputs, buttons, and panels from within fn?
I was thinking I could do:
fn : {
that : this.is.a.really.long.namespace,
abc : function() {},
def : function() {}
}
which would allow me to use that.inputs inside of fn.abc, but is there a concern with that method? Any sort of overhead I need to be aware of? Or is there a better way to accomplish this?
There is nothing wrong with this. In fact you are likely to reduce overhead for the following reasons:
Less run-time resolution of object heirarchies
Fewer characters = shorter script
A more fleshed out construct is the commonly used "module pattern."
Javascript is fairly inefficient internally (e.g. it doesn't have real indexed arrays) so anything you can do to reduce run-time lookups is usually good. Creating a pointer to a long heirarchy will be much faster than using the full heirarchy every time. This will probably only matter much in long loops, but since it's also easier to read, it's just a bonus of being a bit faster - there is no downside.
(edit)
to do this with straight objects you could do something like this, using jQuery to
simplify adding properties:
this.is.a.long.namespace = {};
$.extend(this.is.a.long.namespace,
{ that: this.is.a.long.namespace,
... // other properties
});
Generally speaking though if you're building functional objects the module pattern is better, because it's more flexible and lets you use scope to create private variable/objects.
this.is.a.long.namespace = (function()
{
var that = {},
somePrivateVariable;
function privateFunction() {
...
}
that.inputs = {};
...
// assign everything to "that"
return that;
}());
If you build the object in such a way that every level contains a parent parameter pointing to the level above it, then you can access the data that way. Take a look at this similar question.
Ok ... Here's what I ended up doing:
this.is.a.really.long.namespace = {
inputs : { firstName : undefined },
buttons : { submit : undefined },
fn : {
root : undefined,
abc : function() { console.log(this.root.inputs.firstName); },
def : function() { console.log(this.root.buttons.submit); }
},
init : function() {
var self = this,
fn = self.fn,
inputs = self.inputs,
buttons = self.button;
fn.root = this; // this is the key
inputs.firstName = $("#first-name");
buttons.submit = $("#submit-button");
fn.abc();
fn.def();
}
};
I would consider something like this:
var namespace = this.is.a.really.long.namespace
this.is.a.really.long.namespace = {
root : namespace,
inputs : {},
buttons : {},
panels : {},
fn : {
abc : function() {},
def : function() {}
}
};
from there you should have no problems referencing your namespace anywhere within the object.

Categories

Resources