How do I get the result of class getters into JSON? [duplicate] - node.js

Take this object:
x = {
"key1": "xxx",
"key2": function(){return this.key1}
}
If I do this:
y = JSON.parse( JSON.stringify(x) );
Then y will return { "key1": "xxx" }. Is there anything one could do to transfer functions via stringify? Creating an object with attached functions is possible with the "ye goode olde eval()", but whats with packing it?

json-stringify-function is a similar post to this one.
A snippet discovered via that post may be useful to anyone stumbling across this answer. It works by making use of the replacer parameter in JSON.stringify and the reviver parameter in JSON.parse.
More specifically, when a value happens to be of type function, .toString() is called on it via the replacer. When it comes time to parse, eval() is performed via the reviver when a function is present in string form.
var JSONfn;
if (!JSONfn) {
JSONfn = {};
}
(function () {
JSONfn.stringify = function(obj) {
return JSON.stringify(obj,function(key, value){
return (typeof value === 'function' ) ? value.toString() : value;
});
}
JSONfn.parse = function(str) {
return JSON.parse(str,function(key, value){
if(typeof value != 'string') return value;
return ( value.substring(0,8) == 'function') ? eval('('+value+')') : value;
});
}
}());
Code Snippet taken from Vadim Kiryukhin's JSONfn.js or see documentation at Home Page

I've had a similar requirement lately. To be clear, the output looks like JSON but in fact is just javascript.
JSON.stringify works well in most cases, but "fails" with functions.
I got it working with a few tricks:
make use of replacer (2nd parameter of JSON.stringify())
use func.toString() to get the JS code for a function
remember which functions have been stringified and replace them directly in the result
And here's how it looks like:
// our source data
const source = {
"aaa": 123,
"bbb": function (c) {
// do something
return c + 1;
}
};
// keep a list of serialized functions
const functions = [];
// json replacer - returns a placeholder for functions
const jsonReplacer = function (key, val) {
if (typeof val === 'function') {
functions.push(val.toString());
return "{func_" + (functions.length - 1) + "}";
}
return val;
};
// regex replacer - replaces placeholders with functions
const funcReplacer = function (match, id) {
return functions[id];
};
const result = JSON
.stringify(source, jsonReplacer) // generate json with placeholders
.replace(/"\{func_(\d+)\}"/g, funcReplacer); // replace placeholders with functions
// show the result
document.body.innerText = result;
body { white-space: pre-wrap; font-family: monospace; }
Important: Be careful about the placeholder format - make sure it's not too generic. If you change it, also change the regex as applicable.

Technically this is not JSON, I can also hardly imagine why would you want to do this, but try the following hack:
x.key2 = x.key2.toString();
JSON.stringify(x) //"{"key1":"xxx","key2":"function (){return this.key1}"}"
Of course the first line can be automated by iterating recursively over the object. Reverse operation is harder - function is only a string, eval will work, but you have to guess whether a given key contains a stringified function code or not.

You can't pack functions since the data they close over is not visible to any serializer.
Even Mozilla's uneval cannot pack closures properly.
Your best bet, is to use a reviver and a replacer.
https://yuilibrary.com/yui/docs/json/json-freeze-thaw.html
The reviver function passed to JSON.parse is applied to all key:value pairs in the raw parsed object from the deepest keys to the highest level. In our case, this means that the name and discovered properties will be passed through the reviver, and then the object containing those keys will be passed through.

This is what I did https://gist.github.com/Lepozepo/3275d686bc56e4fb5d11d27ef330a8ed
function stringifyWithFunctions(object) {
return JSON.stringify(object, (key, val) => {
if (typeof val === 'function') {
return `(${val})`; // make it a string, surround it by parenthesis to ensure we can revive it as an anonymous function
}
return val;
});
};
function parseWithFunctions(obj) {
return JSON.parse(obj, (k, v) => {
if (typeof v === 'string' && v.indexOf('function') >= 0) {
return eval(v);
}
return v;
});
};

The naughty but effective way would be to simply:
Function.prototype.toJSON = function() { return this.toString(); }
Though your real problem (aside from modifying the prototype of Function) would be deserialization without the use of eval.

I have come up with this solution which will take care of conversion of functions (no eval). All you have to do is put this code before you use JSON methods. Usage is exactly the same but right now it takes only one param value to convert to a JSON string, so if you pass remaning replacer and space params, they will be ignored.
void function () {
window.JSON = Object.create(JSON)
JSON.stringify = function (obj) {
return JSON.__proto__.stringify(obj, function (key, value) {
if (typeof value === 'function') {
return value.toString()
}
return value
})
}
JSON.parse = function (obj) {
return JSON.__proto__.parse(obj, function (key, value) {
if (typeof value === 'string' && value.slice(0, 8) == 'function') {
return Function('return ' + value)()
}
return value
})
}
}()
// YOUR CODE GOES BELOW HERE
x = {
"key1": "xxx",
"key2": function(){return this.key1}
}
const y = JSON.parse(JSON.stringify(x))
console.log(y.key2())

It is entirely possible to create functions from string without eval()
var obj = {a:function(a,b){
return a+b;
}};
var serialized = JSON.stringify(obj, function(k,v){
//special treatment for function types
if(typeof v === "function")
return v.toString();//we save the function as string
return v;
});
/*output:
"{"a":"function (a,b){\n return a+b;\n }"}"
*/
now some magic to turn string into function with this function
var compileFunction = function(str){
//find parameters
var pstart = str.indexOf('('), pend = str.indexOf(')');
var params = str.substring(pstart+1, pend);
params = params.trim();
//find function body
var bstart = str.indexOf('{'), bend = str.lastIndexOf('}');
var str = str.substring(bstart+1, bend);
return Function(params, str);
}
now use JSON.parse with reviver
var revivedObj = JSON.parse(serialized, function(k,v){
// there is probably a better way to determ if a value is a function string
if(typeof v === "string" && v.indexOf("function") !== -1)
return compileFunction(v);
return v;
});
//output:
revivedObj.a
function anonymous(a,b
/**/) {
return a+b;
}
revivedObj.a(1,2)
3

To my knowledge, there are no serialization libraries that persist functions - in any language. Serialization is what one does to preserve data. Compilation is what one does to preserve functions.

It seems that people landing here are dealing with structures that would be valid JSON if not for the fact that they contain functions. So how do we handle stringifying these structures?
I ran into the problem while writing a script to modify RequireJS configurations. This is how I did it. First, there's a bit of code earlier that makes sure that the placeholder used internally (">>>F<<<") does not show up as a value in the RequireJS configuration. Very unlikely to happen but better safe than sorry. The input configuration is read as a JavaScript Object, which may contain arrays, atomic values, other Objects and functions. It would be straightforwardly stringifiable as JSON if functions were not present. This configuration is the config object in the code that follows:
// Holds functions we encounter.
var functions = [];
var placeholder = ">>>F<<<";
// This handler just records a function object in `functions` and returns the
// placeholder as the value to insert into the JSON structure.
function handler(key, value) {
if (value instanceof Function) {
functions.push(value);
return placeholder;
}
return value;
}
// We stringify, using our custom handler.
var pre = JSON.stringify(config, handler, 4);
// Then we replace the placeholders in order they were encountered, with
// the functions we've recorded.
var post = pre.replace(new RegExp('"' + placeholder + '"', 'g'),
functions.shift.bind(functions));
The post variable contains the final value. This code relies on the fact that the order in which handler is called is the same as the order of the various pieces of data in the final JSON. I've checked the ECMAScript 5th edition, which defines the stringification algorithm and cannot find a case where there would be an ordering problem. If this algorithm were to change in a future edition the fix would be to use unique placholders for function and use these to refer back to the functions which would be stored in an associative array mapping unique placeholders to functions.

Related

How to remove same named objects from a JSON structure, but with a non uniform pattern in structure?

I have a json structure, that is a bit odd, this is returned from a remote device, and I have to accept it as is. For example...
{"_":"e6a7f749","4321013c":{"_":"5d839a60"},"67ea44a0":{"_":"ec7500f9"},"6bea5f08":{"_":"ecdaead4"},"1e92311e":{"_":"5348dab3"}}
I need to remove the '_' key-value pairs, but retain everything else, such that...
{"4321013c":,"67ea44a0":,"6bea5f08":,"1e92311e":,}
As you can see, this leaves just some 'keys', from the original structure. I then want to convert the 'keys' to a simple array of values. Such...
["4321013c","67ea44a0","6bea5f08","1e92311e"]
All this done in node.js (JavaScript) by the way.
Here is my solution. I am not sure if I fully understood the question though.
But here I traverse the object, an gather up all the keys and values into another, flattened object. Then finally, I run Object.keys on that and remove any "_" values.
const obj = {
"_":"e6a7f749",
"4321013c":{"_":"5d839a60"},
"67ea44a0":{"_":"ec7500f9"},
"6bea5f08":{"_":"ecdaead4"},
"1e92311e":{"_":"5348dab3"}
};
const collectValues = (root) => {
return Object.keys(root).reduce((map, key) => {
const value = root[key];
if (typeof value === 'string') {
map = {
...map,
[key]: true,
[value]: true
};
} else {
const nestedValues = collectValues(root[key]);
map = {
...map,
...nestedValues,
[key]: true
};
}
return map;
}, {});
}
const uniqueArray = Object.keys(collectValues(obj)).filter(v => v !== '_');
console.log(uniqueArray);

Getting an error while saving JSON in to mongodb [duplicate]

How do I display the content of a JavaScript object in a string format like when we alert a variable?
The same formatted way I want to display an object.
Use native JSON.stringify method.
Works with nested objects and all major browsers support this method.
str = JSON.stringify(obj);
str = JSON.stringify(obj, null, 4); // (Optional) beautiful indented output.
console.log(str); // Logs output to dev tools console.
alert(str); // Displays output using window.alert()
Link to Mozilla API Reference and other examples.
obj = JSON.parse(str); // Reverses above operation (Just in case if needed.)
Use a custom JSON.stringify replacer if you
encounter this Javascript error
"Uncaught TypeError: Converting circular structure to JSON"
If you want to print the object for debugging purposes, use the code:
var obj = {
prop1: 'prop1Value',
prop2: 'prop2Value',
child: {
childProp1: 'childProp1Value',
},
}
console.log(obj)
will display:
Note: you must only log the object. For example, this won't work:
console.log('My object : ' + obj)
Note ': You can also use a comma in the log method, then the first line of the output will be the string and after that, the object will be rendered:
console.log('My object: ', obj);
var output = '';
for (var property in object) {
output += property + ': ' + object[property]+'; ';
}
alert(output);
console.dir(object):
Displays an interactive listing of the properties of a specified JavaScript object. This listing lets you use disclosure triangles to examine the contents of child objects.
Note that the console.dir() feature is non-standard. See MDN Web Docs
Try this:
console.log(JSON.stringify(obj))
This will print the stringify version of object. So instead of [object] as an output you will get the content of object.
Well, Firefox (thanks to #Bojangles for detailed information) has Object.toSource() method which prints objects as JSON and function(){}.
That's enough for most debugging purposes, I guess.
If you want to use alert, to print your object, you can do this:
alert("myObject is " + myObject.toSource());
It should print each property and its corresponding value in string format.
If you would like to see data in tabular format you can use:
console.table(obj);
Table can be sorted if you click on the table column.
You can also select what columns to show:
console.table(obj, ['firstName', 'lastName']);
You can find more information about console.table here
Function:
var print = function(o){
var str='';
for(var p in o){
if(typeof o[p] == 'string'){
str+= p + ': ' + o[p]+'; </br>';
}else{
str+= p + ': { </br>' + print(o[p]) + '}';
}
}
return str;
}
Usage:
var myObject = {
name: 'Wilson Page',
contact: {
email: 'wilson#hotmail.com',
tel: '123456789'
}
}
$('body').append( print(myObject) );
Example:
http://jsfiddle.net/WilsonPage/6eqMn/
In NodeJS you can print an object by using util.inspect(obj). Be sure to state the depth or you'll only have a shallow print of the object.
Simply use
JSON.stringify(obj)
Example
var args_string = JSON.stringify(obj);
console.log(args_string);
Or
alert(args_string);
Also, note in javascript functions are considered as objects.
As an extra note :
Actually you can assign new property like this and access it console.log or display it in alert
foo.moo = "stackoverflow";
console.log(foo.moo);
alert(foo.moo);
To print the full object with Node.js with colors as a bonus:
console.dir(object, {depth: null, colors: true})
Colors are of course optional, 'depth: null' will print the full object.
The options don't seem to be supported in browsers.
References:
https://developer.mozilla.org/en-US/docs/Web/API/Console/dir
https://nodejs.org/api/console.html#console_console_dir_obj_options
NB:
In these examples, yourObj defines the object you want to examine.
First off my least favorite yet most utilized way of displaying an object:
This is the defacto way of showing the contents of an object
console.log(yourObj)
will produce something like :
I think the best solution is to look through the Objects Keys, and then through the Objects Values if you really want to see what the object holds...
console.log(Object.keys(yourObj));
console.log(Object.values(yourObj));
It will output something like :
(pictured above: the keys/values stored in the object)
There is also this new option if you're using ECMAScript 2016 or newer:
Object.keys(yourObj).forEach(e => console.log(`key=${e} value=${yourObj[e]}`));
This will produce neat output :
The solution mentioned in a previous answer: console.log(yourObj) displays too many parameters and is not the most user friendly way to display the data you want. That is why I recommend logging keys and then values separately.
Next up :
console.table(yourObj)
Someone in an earlier comment suggested this one, however it never worked for me. If it does work for someone else on a different browser or something, then kudos! Ill still put the code here for reference!
Will output something like this to the console :
Here's a way to do it:
console.log("%o", obj);
Use this:
console.log('print object: ' + JSON.stringify(session));
As it was said before best and most simply way i found was
var getPrintObject=function(object)
{
return JSON.stringify(object);
}
(This has been added to my library at GitHub)
Reinventing the wheel here! None of these solutions worked for my situation. So, I quickly doctored up wilsonpage's answer. This one is not for printing to screen (via console, or textfield or whatever). It does work fine in those situations and works just fine as the OP requested, for alert. Many answers here do not address using alert as the OP requested. Anyhow, It is, however, formatted for data transport. This version seems to return a very similar result as toSource(). I've not tested against JSON.stringify, but I assume this is about the same thing. This version is more like a poly-fil so that you can use it in any environment. The result of this function is a valid Javascript object declaration.
I wouldn't doubt if something like this was already on SO somewhere, but it was just shorter to make it than to spend a while searching past answers. And since this question was my top hit on google when I started searching about this; I figured putting it here might help others.
Anyhow, the result from this function will be a string representation of your object, even if your object has embedded objects and arrays, and even if those objects or arrays have even further embedded objects and arrays. (I heard you like to drink? So, I pimped your car with a cooler. And then, I pimped your cooler with a cooler. So, your cooler can drink, while your being cool.)
Arrays are stored with [] instead of {} and thus dont have key/value pairs, just values. Like regular arrays. Therefore, they get created like arrays do.
Also, all string (including key names) are quoted, this is not necessary unless those strings have special characters (like a space or a slash). But, I didn't feel like detecting this just to remove some quotes that would otherwise still work fine.
This resulting string can then be used with eval or just dumping it into a var thru string manipulation. Thus, re-creating your object again, from text.
function ObjToSource(o){
if (!o) return 'null';
var k="",na=typeof(o.length)=="undefined"?1:0,str="";
for(var p in o){
if (na) k = "'"+p+ "':";
if (typeof o[p] == "string") str += k + "'" + o[p]+"',";
else if (typeof o[p] == "object") str += k + ObjToSource(o[p])+",";
else str += k + o[p] + ",";
}
if (na) return "{"+str.slice(0,-1)+"}";
else return "["+str.slice(0,-1)+"]";
}
Let me know if I messed it all up, works fine in my testing. Also, the only way I could think of to detect type array was to check for the presence of length. Because Javascript really stores arrays as objects, I cant actually check for type array (there is no such type!). If anyone else knows a better way, I would love to hear it. Because, if your object also has a property named length then this function will mistakenly treat it as an array.
EDIT: Added check for null valued objects. Thanks Brock Adams
EDIT: Below is the fixed function to be able to print infinitely recursive objects. This does not print the same as toSource from FF because toSource will print the infinite recursion one time, where as, this function will kill it immediately. This function runs slower than the one above, so I'm adding it here instead of editing the above function, as its only needed if you plan to pass objects that link back to themselves, somewhere.
const ObjToSource=(o)=> {
if (!o) return null;
let str="",na=0,k,p;
if (typeof(o) == "object") {
if (!ObjToSource.check) ObjToSource.check = new Array();
for (k=ObjToSource.check.length;na<k;na++) if (ObjToSource.check[na]==o) return '{}';
ObjToSource.check.push(o);
}
k="",na=typeof(o.length)=="undefined"?1:0;
for(p in o){
if (na) k = "'"+p+"':";
if (typeof o[p] == "string") str += k+"'"+o[p]+"',";
else if (typeof o[p] == "object") str += k+ObjToSource(o[p])+",";
else str += k+o[p]+",";
}
if (typeof(o) == "object") ObjToSource.check.pop();
if (na) return "{"+str.slice(0,-1)+"}";
else return "["+str.slice(0,-1)+"]";
}
Test:
var test1 = new Object();
test1.foo = 1;
test1.bar = 2;
var testobject = new Object();
testobject.run = 1;
testobject.fast = null;
testobject.loop = testobject;
testobject.dup = test1;
console.log(ObjToSource(testobject));
console.log(testobject.toSource());
Result:
{'run':1,'fast':null,'loop':{},'dup':{'foo':1,'bar':2}}
({run:1, fast:null, loop:{run:1, fast:null, loop:{}, dup:{foo:1, bar:2}}, dup:{foo:1, bar:2}})
NOTE: Trying to print document.body is a terrible example. For one, FF just prints an empty object string when using toSource. And when using the function above, FF crashes on SecurityError: The operation is insecure.. And Chrome will crash on Uncaught RangeError: Maximum call stack size exceeded. Clearly, document.body was not meant to be converted to string. Because its either too large, or against security policy to access certain properties. Unless, I messed something up here, do tell!
If you would like to print the object of its full length, can use
console.log(require('util').inspect(obj, {showHidden: false, depth: null})
If you want to print the object by converting it to the string then
console.log(JSON.stringify(obj));
I needed a way to recursively print the object, which pagewil's answer provided (Thanks!). I updated it a little bit to include a way to print up to a certain level, and to add spacing so that it is properly indented based on the current level that we are in so that it is more readable.
// Recursive print of object
var print = function( o, maxLevel, level ) {
if ( typeof level == "undefined" ) {
level = 0;
}
if ( typeof level == "undefined" ) {
maxLevel = 0;
}
var str = '';
// Remove this if you don't want the pre tag, but make sure to remove
// the close pre tag on the bottom as well
if ( level == 0 ) {
str = '<pre>';
}
var levelStr = '';
for ( var x = 0; x < level; x++ ) {
levelStr += ' ';
}
if ( maxLevel != 0 && level >= maxLevel ) {
str += levelStr + '...</br>';
return str;
}
for ( var p in o ) {
if ( typeof o[p] == 'string' ) {
str += levelStr +
p + ': ' + o[p] + ' </br>';
} else {
str += levelStr +
p + ': { </br>' + print( o[p], maxLevel, level + 1 ) + levelStr + '}</br>';
}
}
// Remove this if you don't want the pre tag, but make sure to remove
// the open pre tag on the top as well
if ( level == 0 ) {
str += '</pre>';
}
return str;
};
Usage:
var pagewilsObject = {
name: 'Wilson Page',
contact: {
email: 'wilson#hotmail.com',
tel: '123456789'
}
}
// Recursive of whole object
$('body').append( print(pagewilsObject) );
// Recursive of myObject up to 1 level, will only show name
// and that there is a contact object
$('body').append( print(pagewilsObject, 1) );
You can also use ES6 template literal concept to display the content of a JavaScript object in a string format.
alert(`${JSON.stringify(obj)}`);
const obj = {
"name" : "John Doe",
"habbits": "Nothing",
};
alert(`${JSON.stringify(obj)}`);
I always use console.log("object will be: ", obj, obj1).
this way I don't need to do the workaround with stringify with JSON.
All the properties of the object will be expanded nicely.
Another way of displaying objects within the console is with JSON.stringify. Checkout the below example:
var gandalf = {
"real name": "Gandalf",
"age (est)": 11000,
"race": "Maia",
"haveRetirementPlan": true,
"aliases": [
"Greyhame",
"Stormcrow",
"Mithrandir",
"Gandalf the Grey",
"Gandalf the White"
]
};
//to console log object, we cannot use console.log("Object gandalf: " + gandalf);
console.log("Object gandalf: ");
//this will show object gandalf ONLY in Google Chrome NOT in IE
console.log(gandalf);
//this will show object gandalf IN ALL BROWSERS!
console.log(JSON.stringify(gandalf));
//this will show object gandalf IN ALL BROWSERS! with beautiful indent
console.log(JSON.stringify(gandalf, null, 4));
Javascript Function
<script type="text/javascript">
function print_r(theObj){
if(theObj.constructor == Array || theObj.constructor == Object){
document.write("<ul>")
for(var p in theObj){
if(theObj[p].constructor == Array || theObj[p].constructor == Object){
document.write("<li>["+p+"] => "+typeof(theObj)+"</li>");
document.write("<ul>")
print_r(theObj[p]);
document.write("</ul>")
} else {
document.write("<li>["+p+"] => "+theObj[p]+"</li>");
}
}
document.write("</ul>")
}
}
</script>
Printing Object
<script type="text/javascript">
print_r(JAVACRIPT_ARRAY_OR_OBJECT);
</script>
via print_r in Javascript
var list = function(object) {
for(var key in object) {
console.log(key);
}
}
where object is your object
or you can use this in chrome dev tools, "console" tab:
console.log(object);
Assume object obj = {0:'John', 1:'Foo', 2:'Bar'}
Print object's content
for (var i in obj){
console.log(obj[i], i);
}
Console output (Chrome DevTools) :
John 0
Foo 1
Bar 2
Hope that helps!
I prefer using console.table for getting clear object format, so imagine you have this object:
const obj = {name: 'Alireza', family: 'Dezfoolian', gender: 'male', netWorth: "$0"};
And you will you see a neat and readable table like this below:
Circular references solution
To make string without redundant information from object which contains duplicate references (references to same object in many places) including circular references, use JSON.stringify with replacer (presented in snippet) as follows
let s = JSON.stringify(obj, refReplacer(), 4);
function refReplacer() {
let m = new Map(), v= new Map(), init = null;
return function(field, value) {
let p= m.get(this) + (Array.isArray(this) ? `[${field}]` : '.' + field);
let isComplex= value===Object(value)
if (isComplex) m.set(value, p);
let pp = v.get(value)||'';
let path = p.replace(/undefined\.\.?/,'');
let val = pp ? `#REF:${pp[0]=='[' ? '$':'$.'}${pp}` : value;
!init ? (init=value) : (val===init ? val="#REF:$" : 0);
if(!pp && isComplex) v.set(value, path);
return val;
}
}
// ---------------
// TEST
// ---------------
// gen obj with duplicate references
let a = { a1: 1, a2: 2 };
let b = { b1: 3, b2: "4" };
let obj = { o1: { o2: a }, b, a }; // duplicate reference
a.a3 = [1,2,b]; // circular reference
b.b3 = a; // circular reference
let s = JSON.stringify(obj, refReplacer(), 4);
console.log(s);
alert(s);
This solution based on this (more info there) create JSONPath like path for each object value and if same object occurs twice (or more) it uses reference with this path to reference that object e.g. #REF:$.bar.arr[3].foo (where $ means main object) instead 'render' whole object (which is less redundant)
BONUS: inversion
function parseRefJSON(json) {
let objToPath = new Map();
let pathToObj = new Map();
let o = JSON.parse(json);
let traverse = (parent, field) => {
let obj = parent;
let path = '#REF:$';
if (field !== undefined) {
obj = parent[field];
path = objToPath.get(parent) + (Array.isArray(parent) ? `[${field}]` : `${field?'.'+field:''}`);
}
objToPath.set(obj, path);
pathToObj.set(path, obj);
let ref = pathToObj.get(obj);
if (ref) parent[field] = ref;
for (let f in obj) if (obj === Object(obj)) traverse(obj, f);
}
traverse(o);
return o;
}
// ------------
// TEST
// ------------
let s = `{
"o1": {
"o2": {
"a1": 1,
"a2": 2,
"a3": [
1,
2,
{
"b1": 3,
"b2": "4",
"b3": "#REF:$.o1.o2"
}
]
}
},
"b": "#REF:$.o1.o2.a3[2]",
"a": "#REF:$.o1.o2"
}`;
console.log('Open Chrome console to see nested fields');
let obj = parseRefJSON(s);
console.log(obj);
A little helper function I always use in my projects for simple, speedy debugging via the console.
Inspiration taken from Laravel.
/**
* #param variable mixed The var to log to the console
* #param varName string Optional, will appear as a label before the var
*/
function dd(variable, varName) {
var varNameOutput;
varName = varName || '';
varNameOutput = varName ? varName + ':' : '';
console.warn(varNameOutput, variable, ' (' + (typeof variable) + ')');
}
Usage
dd(123.55); outputs:
var obj = {field1: 'xyz', field2: 2016};
dd(obj, 'My Cool Obj');
The console.log() does a great job of debugging objects, but if you are looking to print the object to the page content, here's the simplest way that I've come up with to mimic the functionality of PHP's print_r(). A lot these other answers want to reinvent the wheel, but between JavaScript's JSON.stringify() and HTML's <pre> tag, you get exactly what you are looking for.
var obj = { name: 'The Name', contact: { email: 'thename#gmail.com', tel: '123456789' }};
$('body').append('<pre>'+JSON.stringify(obj, null, 4)+'</pre>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
i used pagewil's print method, and it worked very nicely.
here is my slightly extended version with (sloppy) indents and distinct prop/ob delimiters:
var print = function(obj, delp, delo, ind){
delp = delp!=null ? delp : "\t"; // property delimeter
delo = delo!=null ? delo : "\n"; // object delimeter
ind = ind!=null ? ind : " "; // indent; ind+ind geometric addition not great for deep objects
var str='';
for(var prop in obj){
if(typeof obj[prop] == 'string' || typeof obj[prop] == 'number'){
var q = typeof obj[prop] == 'string' ? "" : ""; // make this "'" to quote strings
str += ind + prop + ': ' + q + obj[prop] + q + '; ' + delp;
}else{
str += ind + prop + ': {'+ delp + print(obj[prop],delp,delo,ind+ind) + ind + '}' + delo;
}
}
return str;
};

Is it possible to log variable name in JavaScript?

Is it possible to log variable name (not value) in JavaScript?
var max_value = 4;
console.log(max_value); // should log "max_value" as a string
UPDATE: I need a testing function that should be able to log any variable name (passed as an argument) as a string, not just this one variable.
There is a solution that can help you. I grabbed this function from this stackoverflow answer, which is able to get the name of the function parameters:
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
if(result === null)
result = [];
return result;
}
then all you need to do now, is to use the name of your variable as a parameter of an anonymous function and pass all the function as argument of the getParamNames :
variablesNames = getParamNames(function (max_value, min_value) {});
This will return an array like this :
result => ["max_value", "min_value"];
Let's make it practical, first change the name of the getParamNames function to something easy and small like this :
function __ (func) {
// code here ...
}
second thing, instead of returning an array, just return the first element of the array, change this :
return result;
to this :
return result.shift();
now, you can get the name of your variable like this :
__(function( max_value ){});

Object.defineProperty on any property [duplicate]

I am aware of how to create getters and setters for properties whose names one already knows, by doing something like this:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
Now, my question is, is it possible to define sort of catch-all getters and setters like these? I.e., create getters and setters for any property name which isn't already defined.
The concept is possible in PHP using the __get() and __set() magic methods (see the PHP documentation for information on these), so I'm really asking is there a JavaScript equivalent to these?
Needless to say, I'd ideally like a solution that is cross-browser compatible.
This changed as of the ES2015 (aka "ES6") specification: JavaScript now has proxies. Proxies let you create objects that are true proxies for (facades on) other objects. Here's a simple example that turns any property values that are strings to all caps on retrieval, and returns "missing" instead of undefined for a property that doesn't exist:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let original = {
example: "value",
};
let proxy = new Proxy(original, {
get(target, name, receiver) {
if (Reflect.has(target, name)) {
let rv = Reflect.get(target, name, receiver);
if (typeof rv === "string") {
rv = rv.toUpperCase();
}
return rv;
}
return "missing";
}
});
console.log(`original.example = ${original.example}`); // "original.example = value"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = VALUE"
console.log(`proxy.unknown = ${proxy.unknown}`); // "proxy.unknown = missing"
original.example = "updated";
console.log(`original.example = ${original.example}`); // "original.example = updated"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = UPDATED"
Operations you don't override have their default behavior. In the above, all we override is get, but there's a whole list of operations you can hook into.
In the get handler function's arguments list:
target is the object being proxied (original, in our case).
name is (of course) the name of the property being retrieved, which is usually a string but could also be a Symbol.
receiver is the object that should be used as this in the getter function if the property is an accessor rather than a data property. In the normal case this is the proxy or something that inherits from it, but it can be anything since the trap may be triggered by Reflect.get.
This lets you create an object with the catch-all getter and setter feature you want:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let obj = new Proxy({}, {
get(target, name, receiver) {
if (!Reflect.has(target, name)) {
console.log("Getting non-existent property '" + name + "'");
return undefined;
}
return Reflect.get(target, name, receiver);
},
set(target, name, value, receiver) {
if (!Reflect.has(target, name)) {
console.log(`Setting non-existent property '${name}', initial value: ${value}`);
}
return Reflect.set(target, name, value, receiver);
}
});
console.log(`[before] obj.example = ${obj.example}`);
obj.example = "value";
console.log(`[after] obj.example = ${obj.example}`);
The output of the above is:
Getting non-existent property 'example'
[before] obj.example = undefined
Setting non-existent property 'example', initial value: value
[after] obj.example = value
Note how we get the "non-existent" message when we try to retrieve example when it doesn't yet exist, and again when we create it, but not after that.
Answer from 2011 (obsoleted by the above, still relevant to environments limited to ES5 features like Internet Explorer):
No, JavaScript doesn't have a catch-all property feature. The accessor syntax you're using is covered in Section 11.1.5 of the spec, and doesn't offer any wildcard or something like that.
You could, of course, implement a function to do it, but I'm guessing you probably don't want to use f = obj.prop("example"); rather than f = obj.example; and obj.prop("example", value); rather than obj.example = value; (which would be necessary for the function to handle unknown properties).
FWIW, the getter function (I didn't bother with setter logic) would look something like this:
MyObject.prototype.prop = function(propName) {
if (propName in this) {
// This object or its prototype already has this property,
// return the existing value.
return this[propName];
}
// ...Catch-all, deal with undefined property here...
};
But again, I can't imagine you'd really want to do that, because of how it changes how you use the object.
Preface:
T.J. Crowder's answer mentions a Proxy, which will be needed for a catch-all getter/setter for properties which don't exist, as the OP was asking for. Depending on what behavior is actually wanted with dynamic getters/setters, a Proxy may not actually be necessary though; or, potentially, you may want to use a combination of a Proxy with what I'll show you below.
(P.S. I have experimented with Proxy thoroughly in Firefox on Linux recently and have found it to be very capable, but also somewhat confusing/difficult to work with and get right. More importantly, I have also found it to be quite slow (at least in relation to how optimized JavaScript tends to be nowadays) - I'm talking in the realm of deca-multiples slower.)
To implement dynamically created getters and setters specifically, you can use Object.defineProperty() or Object.defineProperties(). This is also quite fast.
The gist is that you can define a getter and/or setter on an object like so:
let obj = {};
let val = 0;
Object.defineProperty(obj, 'prop', { //<- This object is called a "property descriptor".
//Alternatively, use: `get() {}`
get: function() {
return val;
},
//Alternatively, use: `set(newValue) {}`
set: function(newValue) {
val = newValue;
}
});
//Calls the getter function.
console.log(obj.prop);
let copy = obj.prop;
//Etc.
//Calls the setter function.
obj.prop = 10;
++obj.prop;
//Etc.
Several things to note here:
You cannot use the value property in the property descriptor (not shown above) simultaneously with get and/or set; from the docs:
Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both.
Thus, you'll note that I created a val property outside of the Object.defineProperty() call/property descriptor. This is standard behavior.
As per the error here, don't set writable to true in the property descriptor if you use get or set.
You might want to consider setting configurable and enumerable, however, depending on what you're after; from the docs:
configurable
true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.
Defaults to false.
enumerable
true if and only if this property shows up during enumeration of the properties on the corresponding object.
Defaults to false.
On this note, these may also be of interest:
Object.getOwnPropertyNames(obj): gets all properties of an object, even non-enumerable ones (AFAIK this is the only way to do so!).
Object.getOwnPropertyDescriptor(obj, prop): gets the property descriptor of an object, the object that was passed to Object.defineProperty() above.
obj.propertyIsEnumerable(prop);: for an individual property on a specific object instance, call this function on the object instance to determine whether the specific property is enumerable or not.
The following could be an original approach to this problem:
var obj = {
emptyValue: null,
get: function(prop){
if(typeof this[prop] == "undefined")
return this.emptyValue;
else
return this[prop];
},
set: function(prop,value){
this[prop] = value;
}
}
In order to use it the properties should be passed as strings.
So here is an example of how it works:
//To set a property
obj.set('myProperty','myValue');
//To get a property
var myVar = obj.get('myProperty');
Edit:
An improved, more object-oriented approach based on what I proposed is the following:
function MyObject() {
var emptyValue = null;
var obj = {};
this.get = function(prop){
return (typeof obj[prop] == "undefined") ? emptyValue : obj[prop];
};
this.set = function(prop,value){
obj[prop] = value;
};
}
var newObj = new MyObject();
newObj.set('myProperty','MyValue');
alert(newObj.get('myProperty'));
You can see it working here.
I was looking for something and I figured out on my own.
/*
This function takes an object and converts to a proxy object.
It also takes care of proxying nested objectsa and array.
*/
let getProxy = (original) => {
return new Proxy(original, {
get(target, name, receiver) {
let rv = Reflect.get(target, name, receiver);
return rv;
},
set(target, name, value, receiver) {
// Proxies new objects
if(typeof value === "object"){
value = getProxy(value);
}
return Reflect.set(target, name, value, receiver);
}
})
}
let first = {};
let proxy = getProxy(first);
/*
Here are the tests
*/
proxy.name={} // object
proxy.name.first={} // nested object
proxy.name.first.names=[] // nested array
proxy.name.first.names[0]={first:"vetri"} // nested array with an object
/*
Here are the serialised values
*/
console.log(JSON.stringify(first)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
console.log(JSON.stringify(proxy)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
var x={}
var propName = 'value'
var get = Function("return this['" + propName + "']")
var set = Function("newValue", "this['" + propName + "'] = newValue")
var handler = { 'get': get, 'set': set, enumerable: true, configurable: true }
Object.defineProperty(x, propName, handler)
this works for me

Mongodb $where query always true with nodejs

When I query my database with a function passed in the "$where" clause in nodejs, it always return me all documents in the db.
For example, if I do
var stream = timetables.find({$where: function() { return false; }}).stream();
it return me all the documents.
Instead, if I do
var stream = timetables.find({$where: 'function() { return false; }'}).stream();
the function is really executed, and this code doesn't return any document.
The problem is that if I convert in string my function the context's bindinds are removed, and I need them for more complex query. For example:
var n = 1;
var f = function() { return this.number == n; }
var stream = timetables.find({$where: f.toString()}).stream();
// error: n is not defined
Is this a normal behaviour? How can I solve my problem?
Please excuse me for my poor english!
First off, keep in mind that the $where operator should almost never be used for the reasons explained here (credit goes to #WiredPrairie).
Back to your issue, the approach you'd like to take won't work even in the mongodb shell (which explicitly allows naked js functions with the $where operator). The javascript code provided to the $where operator is executed on the mongo server and won't have access to the enclosing environment (the "context bindings").
> db.test.insert({a: 42})
> db.test.find({a: 42})
{ "_id" : ObjectId("5150433c73f604984a7dff91"), "a" : 42 }
> db.test.find({$where: function() { return this.a == 42 }}) // works
{ "_id" : ObjectId("5150433c73f604984a7dff91"), "a" : 42 }
> var local_var = 42
> db.test.find({$where: function() { return this.a == local_var }})
error: {
"$err" : "error on invocation of $where function:\nJS Error: ReferenceError: local_var is not defined nofile_b:1",
"code" : 10071
}
Moreover it looks like that the node.js native mongo driver behaves differently from the shell in that it doesn't automatically serialize a js function you provide in the query object and instead it likely drops the clause altogether. This will leave you with the equivalent of timetables.find({}) which will return all the documents in the collection.
This one is works for me , Just try to store a query as a string in one variable then concat your variable in query string,
var local_var = 42
var query = "{$where: function() { return this.a == "+local_var+"}}"
db.test.find(query)
Store your query into a varibale and use that variable at your find query. It works..... :D
The context will always be that of the mongo database, since the function is executed there. There is no way to share the context between the two instances. You have to rethink the way you query and come up with a different strategy.
You can use a wrapper to pass basic JSON objects, ie. (pardon coffee-script):
# That's the main wrapper.
wrap = (f, args...) ->
"function() { return (#{f}).apply(this, #{JSON.stringify(args)}) }"
# Example 1
where1 = (flag) ->
#myattr == 'foo' or flag
# Example 2 with different arguments
where2 = (foo, options = {}) ->
if foo == options.bar or #_id % 2 == 0
true
else
false
db.collection('coll1').count $where: wrap(where1, true), (err, count) ->
console.log err, count
db.collection('coll1').count $where: wrap(where2, true, bar: true), (err, count) ->
console.log err, count
Your functions are going to be passed as something like:
function () {
return (function (flag) {
return this.myattr === 'foo' || flag;
}).apply(this, [true])
}
...and example 2:
function () {
return (
function (foo, options) {
if (options == null) {
options = {};
}
if (foo === options.bar || this._id % 2 === 0) {
return true;
} else {
return false;
}
}
).apply(this, [ true, { "bar": true } ])
}
This is how it is supposed to be. The drivers don't translate the client code into the mongo function javascript code.
I'm assuming you are using Mongoose to query your database.
If you take a look at the actual Query object implementation, you'll find that only strings are valid arguments for the where prototype.
When using the where clause, you should use it along with the standard operators such as gt, lt that operates on in the path created by the where function.
Remember that Mongoose querying, as in Mongo, is by example, you may want to reconsider your query specification in a more descriptive fashion.

Resources