How to preserve hasOwnProperty on JSON.parsed responseText? - javascript

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!

Related

What data structure in JS would allows to choose one value from defined values?

I want to create status object. Which can have values like pending, session, rejected, cancelled...
I want to access it like status.isSession();, status.isPending()... etc`
but i want to assign it like this status.toPending(); , status.toSession();
This way there is no room for spelling errors.
I think this is dynamic programming.
You can achieve this in JavaScript, but it will work only with double equals.
You can create an object with constructor like this :
function Status(value){
this.session = true;
this.value = value;
}
Now if you create an instance of this object :
const status = new Status('session');
Sure enough status.session will return true and status.value will return 'session.'
Now you can override toString() method of this object so it returns status.value
Status.prototype.toString = function(){
return this.value;
}
The reason for this is that, when you try to compare an Object vs Primitive, the Engine will try to put them in 'value context' and coerce your object to String by calling .toString() operation.
Sure enough this will produce :
const status = new Status('session');
console.log(status == 'session');
A true in your console.
But this is highly discouraged because is a hack and you might get weird bugs, so I suggest to stay away.

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

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.

How to set a localStorage item as function?

I would like to set a localStorage item as a function, but when I do it gets saved as a string.
localStorage.getData = function (key="def") {
let data = null;
if (this[key] && !_this.isDataAvailableInList(this[key])) {
data = this[key].data;
}
return data;
};
The above function get stored as string and not function.
Note: Kindly do not provide alternatives or other suggestions, I am well aware of alternatives and am curious to know how this works. I have not provided the entire context so lets just assume I am in need of setting a localStorage item as a function
Unfortunately local storage is only about strings. Even arrays or other data are always converted to strings.
For most dead data without cycles you can use of course JSON.stringify and JSON.parse, but in other cases or for code or other Javascript entities unsupported by JSON (like Infinity or undefined) you need to serialize and deserialize yourself.
Javascript provides eval, so you can store the function f converting it first to a string with f+"" and get back the function with eval(s).
This won't work with closures however and moreover you cannot even know if a function is indeed a closure or not in Javascript (so you cannot even raise an error if the impossible operation of serializing a closure is attempted).
Convert the function into a string via .toString(). When you read it, convert it back to a function via new Function(myString).
Example:
No pamameters:
(new Function('var a = 0; return a + 1;'))() === 1
With parameters:
(new Function(['param0'],'var a = param0; return a + 1;'))(100) === 101
Note: The scope of the original function will be lost.

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