JSON.stringify and Object.keys produce different results on same object - javascript

I have an issue that isn't making any sense to me. I was wondering if any of you could help.
I have a data source object which I use to access REST data through. All the complex asynchronous stuff works fine, but I've come completely unstuck on what should be the very simple task of passing options into the configuration of the data sources.
At the moment, I have this function:
object.addSourceOption = function( model, optKey, optVal ){
if(!_.has(config.sources, model) ){ return this; }
else{
var options = config.sources[model]["options"];
options[optKey] = optVal;
console.log(options[optKey]);
//options = JSON.parse( JSON.stringify( ) );
console.log( "Source Option: " + optKey + ": " + optVal
+" added for model: " + model );
var debugString = JSON.stringify(options);
console.log(debugString);
console.log( Object.keys(options));
}
return this;
};
This function is being called and it's being called with good values as far as I can see. Here's an example of some debug output (in this case, the key value is "post_get" and the value is a function, which is printed):
function (element){
}
restData2.js:189 Source Option: post_get: function (element){
} added for model: Contacts
restData2.js:191 {}
restData2.js:192 ["post_get"]
I don't understand why JSON.stringify and Objects.keys produce different results. I don't understand which to trust to debug the code I'm using, or what could possibly be happening behind the scenes to make the two functions disagree about the code I've written.

Congratulations! You've found one of the subtler parts of JSON.stringify(...) :)
Here's a helpful part from MDN:
If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). JSON.stringify can also just return undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).
While JSON is convenient because it looks like Javascript, JSON is really a subset of Javascript. Moreover, not all JS objects can be represented in JSON. The full details are available at http://json.org/.
But yeah, in your example, post_get is not showing up in the JSON because properties of type function are not legal in JSON.
Hope this helps!
Aside:
Also, keep in mind that sometimes a JS object can have properties that aren't returned by Object.keys(...) either, because Object.keys(...) only returns the properties that were not inherited from any prototypes (base objects) that the object extends.

the key value is "post_get" and the value is a function
JSON.stringify() won't include properties that refer to functions. Object.keys() will. It's as simple as that.

Related

How to preserve hasOwnProperty on JSON.parsed responseText?

I am a very new developer working on a very simple application as part of my training process - so be gentle.
I have built a function in javascript that accepts arbitrary objects from elsewhere and builds a legal POST request string.
Code:
function postify(oPost){
var out = '';
for (key in oPost){
if (oPost.hasOwnProperty(key) && key >0){
if(oPost[key].value != 'Submit'){
out += '&' + oPost[key].name + '=' + oPost[key].value;
}
}
}
return out;
}
There are many like it, but this one is mine. I elected to use hasOwnProperty as a conditional, as the total list of inherited properties could be really quite long.
One of the objects I would like to pass to this function is a JSON parsed responseText object, which is retrieved like so.
function postData(str){
var http = new XMLHttpRequest();
http.open('POST', 'test.php',false);
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
http.setRequestHeader("Content-length", str.length);
http.setRequestHeader("Connection", "close");
http.send(str);
var response = JSON.parse(http.responseText);
responseHandle(response);
}
So, the problem - both of these functions do exactly what they are supposed to do, until my responseHandle function routes the response object to the postify function. Manual checking indicates that all expected properties are in place, but postify() won't concatenate a string because those properties seem to have been inherited.
I am fully aware that I could trivially brute force assign all necessary properties - the handler function would do what it needed to either way. I am also aware that my synchronous XMLHttpRequest is deprecated - right this second, it's what I need, and works fine.
So, then, some questions - is there a way to pass my JSON.parsed object such that hasOwnProperty() == true is maintained? Is there a different property or technique I could or should be using in postify() to look for deliberately set key value pairs? Should I just rig the POST to transmit ALL of the inherited properties of the object I am POSTING to PHP?
The problem isn't with hasOwnProperty, it's with key > 0. Unless oPost is an array, the keys will be strings. When you compare a string with a number, the string is converted to a number. But if the string isn't numeric, the conversion will return NaN, and comparing this with 0 is false.
Your function shouldn't have worked for any object, it doesn't matter if it came from JSON.parse(). When JSON.parse returns an object, all the properties are "own".
The fix is to change
if (oPost.hasOwnProperty(key) && key >0){
to
if (oPost.hasOwnProperty(key)){
Objects restored from JSON serialization have no identity beyond "being a plain object", so all you're getting back is a plain, prototype-less object.
However, instead of solving that issue, let's solve the real problem, the one you're trying to use .hasOwnProperty for, by using some modern JS (but not so modern as to use ES6/ES2015 syntax) instead:
function postify(inputObject) {
var keys = Object.keys(inputObject);
return keys.filter(function(key) {
return inputObject[key].value !== 'Submit';
}).map(function(key) {
var e = inputObject[key];
return '&' + e.name + '=' + e.value;
}).join('');
}
on the first line, we get the object's keys using the build in JavaScript Object.keys function. Then we filter the keys, discarding any key for which oPost[key].value is not the string 'Submit'. Then we build a mapping of ["remaining keys", ...] => ["&thing=whatever", "&thing2=moo", ...], and then we join those things without any glue.
Done, no var out required even!

Javascript class - how to make properties required and optional

I'm building a event tracking api and need to provide a JS class with a list of properties of which some of them will be required and some optional. Also, no new properties should be added.
Once the client instantiates and passes the object based on the class, I will be serializing the object and sending it as query string. I already have this part but not sure how to restrict the client from creating new properties.
How do I go about designing the class in JS?
There are a number of ways of doing the things you're wanting. Required/Optionals are pretty easy with utilities like underscorejs/jquery extend, along with some of underscore's utility methods (map, reduce,etc.).
To prevent an object from actually having additional properties though is a bit trickier. You could run an array of known required/optional properties as a check and do a delete on any property values that don't match your criteria.
Another option is to use something like seal. You read up more about this on mozilla's website here.
So I guess what I'd do is get the required properties working first by doing a sanity check against their existence. Your optionals could be provided and wrapped/unwrapped by using extend.
Finally (and maybe an even better route) would be to force the user to call getter/setter methods like setMyProperty( 'value' ) in order to populate any required/optional property value. This way, you aren't going to have to write a big hairy solution using the tools above.
JS is just funny like that. Neither solution is perfect, but they are both possible solutions. I'm sure there are others too. This is just a part of JS that can be a bit of a pain in the arsonal. Good luck.
>>> Edit <<<
This may not be perfect, but this is what I've done so far for the optional/required properties. I am obviously assuming they would just be passing in a set of properties in the constructor.
var _Required = {
requiredProperty1: null,
requiredProperty2: null,
requiredProperty3: null
};
var _Optionals = {
optionalProperty1: null,
optionalProperty2: null,
optionalProperty3: null
};
var EventTrackerAPI = function( settings ) {
var requiredProp, optionalProp;
this.required = {};
this.optional = {};
for( requiredProp in _Required ) {
if( !settings.hasOwnProperty( requiredProp ) ) {
throw new Error( 'FAILED to create an instance of EventTrackerAPI - Required Property (' + requiredProp + ') Missing!' )
}
this.required[requiredProp] = settings[requiredProp];
}
for( optionalProp in _Optionals ) {
if( settings.hasOwnProperty( optionalProp ) ) {
this.optional[optionalProp] = settings.hasOwnProperty( optionalProp );
} else {
this.optional[optionalProp] = null;
}
}
};
Anyways, I'd probably do something like the above. It isn't perfect yet (since I threw it together in about 10 minutes), but it should be a good start. I would also just not allow access to the private storage either.
When I was ready to actually extract the properties (like when you're ready to serialize), I'd do something similar as is being done in the constructor - just manually go through and grab the things you actually want - everything else would just be discarded this way. As a matter of fact, it may make sense to strip out the optional/required logic and make them methods on the object's prototype (EventTrackerAPI.prototype.extractRequired = function( required ) {.... } for example...)
Why not usign just class with constructor - you will have required properties then

trying to work dynamically with object properties in javascript

I'm trying to sort out if this is plausible but have gotten syntax errors at best. So I am wondering if it is at all possible.
What I have is an object (example only)
var myObj = {
something1_max:50,
something1_enabled:false,
something1_locked:true,
something2_max:100,
something2_enabled:false,
something2_locked:true,
something3_max:10,
something3_enabled:true,
something3_locked:true
}
and what I want to do through a function is do something like again for example to sum things up..
function displayDetails(theScope, obj)
{
console.log(obj.[theScope]_max);
}
(function(){displayDetails('something3', myObj);})()
so when displayDetails() is called whatever the scope I can see in this example the max for that scope. In the console log for the example I would hope to see 10
Properties of JavaScript objects can always be accessed as a string using the bracket syntax, ie object['property']. This, of course, means you can build that string dynamically:
console.log(obj[theScope + '_max']);
Put the property name string in brackets.
console.log(obj[theScope + '_max']);

Javascript arrays and Meteor session

I have made an interesting observation. When trying to update an array that is stored in the Meteor session storage, the following code will not propagate the changes:
var tags = Session.get("Tags");
tags.push("a");
Session.set("Tags", tags);
But if I change the first line to use Session.get("Tags").slice(), everything depending on the session will update accordingly. I guess this is due to the fact that Meteor tests some references for equality and therefore does not update anything.
Is there a better way to manage lists stored in the meteor session store?
If I now try to remove an element from the collection (using array.remove() from here), the behavior turns out to be a bit ... of ... I am doing this inside a Meteor template event, the code looks like this:
"click .taglist li" : function(e) {
var tags = Session.get("Tags").slice();
var index = cardTags.indexOf(this);
Meteor._debug(Session.get("Tags").slice().indexOf("a"));
Meteor._debug("Removing tag \"" + this + "\", index: " + index, ", typeof(this) = " + typeof(this).toString());
tags.remove(index);
Session.set("Tags", tags);
}
This outputs:
1
Removing tag "a", index: -1, typeof(this) = string
So somehow, the cardTags.indexOf(this); statement seems to return -1 for almost any case. I guess I am doing something fundamentally wrong, as I am quite now to javascript, but somehow I can not figure out whats going on here.
Why will those two calls to indexOf() behave different?
I believe this is the same as this situation in Backbone.js. In order for the change event to be triggered, Meteor needs to have a new reference for the array, not just an updated copy of the old one.
In brief, in order to have the 'correct' behaviour, you'll need to clone the array, make the changes you want, and then do Session.set('foo', myCopiedArray).
In short: Use var index = cardTags.indexOf(this.toString()); instead.
Long version:
When using strings in JavaScript, those are strings, whereas typeof 'test' returns string.
Let's take a look at the following code in order to get find out another way to represent strings in JavaScript:
var func = function () {
return this;
};
console.log(func.call('test'));
The console (at least FireBug) won't show us "test", but instead it shows String {0="t", 1="e", 2="s", 3="t" }. typeof would return "object".
The content of the this statement seems to need to be an object. In order to convert a string into a "String" object we can do console.log(new String('test'));, which is the same as the previously logged value.
To convert a string object into a string (data type), just use its prototype toString.

Passing unknown amounts of variables using through a string string and eval and multiple functions and all sorts

In short, I want to use an object literal to allow me to pass a unknown amount of variables in any order to a function. Whilst this is not big deal in theory, in my code, this object literal is passed to a second function called on_change.
on_change works by comparing an element's innerHTML to a string; If it is the same, it sets a timeout to call the function again. If the element's innerHTML is different from the string, then the third parameter is executed, this will either be a function or a string. either way it will execute. I have tested this function plenty and used it for a while now.
However, I cannot seem to get the object literal to flow through the function calls...
var params = { xpos:'false'};
on_change('window_3_cont_buffer','','
if(Window_manager.windows[3].window_cont_buffer.getElementsByTagName(\'content\')[0].getElementsByTagName(\'p\')[0].innerHTML == \'ERROR\'){
alert(Window_manager.windows[3].window_cont_buffer.getElementsByTagName(\'content\')[0].getElementsByTagName(\'p\')[1].innerHTML);
return false;
} else {
Window_manager.windows[3].load_xml(\'location/view.php?location_ID=3\', \'\', ' + params + ' ); }
');
I call this as part of the form submission. After this line, I then call a function to load some content via ajax, which works fine and will trigger the code from the on_change function.
I have tested the load_xml function, it is able to call alert(param.xpos) and get the correct response. I can even added in a check for being undefined so that rest of the times I call load_xml I don't get swamped with alerts.
The load_xml function first sets up the on_change function, then calls the function to load the content to a hidden div. Once the AJAX request has updated that DIV, the on_change function should now call the parse_xml function. This pulls out the information from the xml file. However... The idea of this object literal param is that it can tell this parse_xml function to ignore certain things.
on_change("window_" + this.id + "_cont_buffer", "", "Window_manager.windows[" + this.id + "].parse_xml('" + param + "')");
This is part of load_xml, it works perfectly fine, even with the param bit in there. except, parse_xml does not seem to be able to use that parameter.
I have been able to get it to a point where parse_xml can alert(param) and give [object object] which I would of thought meant that the object literal had been passed through, but when I try and call alert(param.xpos) I get undefined.
I know this is a pig of a problem, and I could get around it by just having the function take a zillion boolean parameters, but its just not a very nice solution.
In effect, what you have is this:
var params = {param: "value"};
foo("bar('one', 'two', 'three');");
...where foo uses eval on that string, something like:
function foo(codestring) {
eval(codestring);
}
...and you're looking for how to embed params in that.
You could do this by serializing the object literal as a string so that when you combine it with the other string, and the total string is evaluated, it gets evaluated. Browsers are slowly getting JSON serialization built in, but for now you want to use jQuery, Prototype, or (if you just want this part) json2.js from Crockford, which offers JSON.stringify for turning objects that can be turned into JSON strings, into JSON strings. So:
var params = {param: "value"};
foo("bar(" + JSON.stringify(params) + ");");
But what you really want to do is refactor so that all of that logic is expressed as code, not code within a string. Then you could pass the literal directly, plus a whole raft of other benefits, including modularization, debugging, etc.
var params = {param: "value"};
function callBar() {
bar(params);
}
foo(callBar);
...changing foo so that it calls a function rather than evaling a string. (eval is to be avoided whenever possible, and to paraphrase the Dalai Lama, it's [almost] always possible.) My sample foo changes to:
function foo(func) {
func();
}
If foo needs to include additional information for bar (and if callBar is set up to handle those extra arguments), it can use Function#call or Function#apply to do that. (Those links are to MDC, but don't worry, they're not Firefox-specific, they've been in the ECMA spec for years and are nearly universally supported.)
You can't put an object inside a string. You would have to serialise the object, add it into the string, then parse it back into a structured object on the other side. The simplest way to do that would be to use JSON (via JSON.stringify or a library fallback for older browsers that don't have it), since JSON evaluates as simple JavaScript.
Note that you wouldn't get the exact same object back, but a new one with the same attributes and properties, and it only works for simple types, so you can't include a function in the object or anything.
However, in any case, passing JavaScript code around in strings is an anti-pattern to be strenuously avoided. Instead use inline functions, and you don't have to worry about what you can and can't put in a string, and you can get rid of all that unreadable wrapping and \-escaping:
var params = {xpos: 'false'};
on_change('window_3_cont_buffer', '', function() {
var w= Window_manager.windows[3];
var ps= w.window_cont_buffer.getElementsByTagName('content')[0].getElementsByTagName('p');
if (ps[0].innerHTML==='ERROR') {
alert(ps[1].innerHTML);
return false;
} else {
w.load_xml('location/view.php?location_ID=3', '', params);
}
});
Some general techniques which may be helpful for you:
// Example of calling function objects given as arguments:
function on_change(foo, callback1, callback2) {
if (foo)
callback1();
else
callback2.call(available_as_this);
}
on_change(foo, function() { your code }, function() { another code });
// Example of function taking arbitrary number of arguments:
function any_params() {
console.log('I got ' + arguments.length + 'arguments!');
console.log('First argument: ' + arguments[0]);
}
any_params('one', 'two', 'three', [], null, {});
See arguments variable and call().
I want to use object literal to allow me to pass a random amount of variables in any order to a function.
Why oh why don't you just create an object which contains the parameters and functions, and pass that around? The receiving function can just test to see if a property of the object is set before trying to use it.

Categories