Related
I have a JS object I would like to save in Local Storage for future use, and I cannot parse it to a string.
Code:
JSON.stringify({
a: 5,
b: function (param) {
return param;
}
})
Result:
"{"a":5}"
How do I save it for future use, if not with JSON?
(And creating my own Lexer-Parser to interupt string function I dont think is an option)
I'd recommend this approach:
Store arguments and the body in your json:
{"function":{"arguments":"a,b,c","body":"return a*b+c;"}}
Now parse json and instantiate the function:
var f = new Function(function.arguments, function.body);
I think it's save
Usually a question like this indicates an X/Y problem: You need to do X, you think Y will help you do that, so you try to do Y, can't, and ask how to do Y. It would frequently be more useful to ask how to do X instead.
But answering the question asked: You could use replacer and reviver functions to convert the function to a string (during stringify) and back into a function (during parse) to store a string version of the function, but there are all sorts of issues with doing that, not least that the scope in which the function is defined may well matter to the function. (It doesn't matter to the function you've shown in the question, but I assume that's not really representative.) And converting a string from local storage into code you may run means that you are trusting that the local storage content hasn't been corrupted in a malicious way. Granted it's not likely unless the page is already vulnerable to XSS attacks, but it's an issue to keep in mind.
Here's an example, but I don't recommend it unless other options have been exhausted, not least because it uses eval, which (like its close cousin new Function)) can be a vector for malicious code:
// The object
var obj = {
a: 5,
b: function (param) {
return param;
}
};
// Convert to JSON using a replacer function to output
// the string version of a function with /Function(
// in front and )/ at the end.
var json = JSON.stringify(obj, function(key, value) {
if (typeof value === "function") {
return "/Function(" + value.toString() + ")/";
}
return value;
});
// Convert to an object using a reviver function that
// recognizes the /Function(...)/ value and converts it
// into a function via -shudder- `eval`.
var obj2 = JSON.parse(json, function(key, value) {
if (typeof value === "string" &&
value.startsWith("/Function(") &&
value.endsWith(")/")) {
value = value.substring(10, value.length - 2);
return (0, eval)("(" + value + ")");
}
return value;
});
document.body.innerHTML = obj2.b(42);
The construct (0, eval)("(" + value + ")"); ensures that eval runs at global scope rather than within the scope of the reviver function. Normally eval has a magic ability to use the scope you call it in, but that only works when you call it directly. Indirect eval as shown (or just var e = eval; e("(" + value + ")");) doesn't have that magic ability, it runs at global scope.
You can't store functions in JSON.
The value in JSON may contain only string, number, object, array, true, false or null:
Check out it on JSON site.
One simple way of doing this is
var dstr = JSON.stringify( { a: 5
, b: x => x
}
, (k,v) => typeof v === "function" ? "" + v : v
);
I've taken to storing the function name, along with the parameter values, in an array, with the first item in the array being the function name prepended with a $, to separate them from normal arrays.
{
"object": {
"your-function": ["$functionName", "param-1", "param-2"],
"color": ["$getColor", "brand", "brand-2"],
"normal-array": ["normal", "array"]
...
}
}
In the above example I have Sass and JS functions to retrieve color values from a global map/object. Parsing the function in this manner naturally requires custom code, but in terms of "storing" functions in JSON, I like this way of doing it.
I have created JSON.parseIt() and JSON.stringifyIt() functions based on the first answer without using eval
JSON.stringifyIt = (obj)=>{
return(
JSON.stringify(obj, function(key, value) {
if (typeof value === "function") {
return "/Function(" + value.toString() + ")/";
}
if(typeof value === "string"){
return "/String(" + value.toString() + ")/"
}
return value;
})
)
}
JSON.parseIt=(json)=>{
return(
JSON.parse(json, function(key, value) {
if (typeof value === "string" &&
value.startsWith("/Function(") &&
value.endsWith(")/")) {
value = value.substring(10, value.length - 2);
var string = value.slice(value.indexOf("(") + 1, value.indexOf(")"));
if(/\S+/g.test(string)){
return (new Function(string,value.slice(value.indexOf("{") + 1, value.lastIndexOf("}"))))
}else{
return (new Function(value.slice(value.indexOf("{") + 1, value.lastIndexOf("}"))));
}
}
if (typeof value === "string" &&
value.startsWith("/String(") &&
value.endsWith(")/")){
value = value.substring(8, value.length - 2);
}
return value;
})
)
}
// DEMO
var obj = {
string:"a string",
number:10,
func:()=>{
console.log("this is a string from a parsed json function");
},
secFunc:(none,ntwo)=>{console.log(none + ntwo)} ,
confuse:"/Function(hello)/"
}
const stringifiedObj = JSON.stringifyIt(obj);
console.log("the stringified object is: ",stringifiedObj);
const parsedObj = JSON.parseIt(stringifiedObj);
// console.log("the parsed object is: ",parsedObj);
console.log(parsedObj.string);
console.log(parsedObj.number);
console.log(parsedObj.confuse);
parsedObj.func();
parsedObj.secFunc(5,6);
The problems I fixed were
Removed eval.
there was a problem in the stringifying and parsing that if I give a string like
"/Function(hello)/" will be a function when parsed
Made it to two functions
Added parameter insertation
For someone that still need include, for whatever reason, the function definition in JSON, this code can help (but can be slow depending object size):
function Object2JsonWithFunctions(o, space = null) {
var functionList = {}
var fnSeq = 0;
var snrepl = function(k,v){
if(typeof v === 'function'){
fnSeq++;
var funcName = `___fun${fnSeq}___`;
var funcText = ''+v;
functionList[funcName] = funcText
return funcName;
}
return v;
}
var RawJson = JSON.stringify(o, snrepl, space);
for(func in functionList){
var PropValue = `"${func}"`;
RawJson = RawJson.replace(PropValue, functionList[func])
}
return RawJson;}
The code will do the normal convert to JSON.
For functions, the original stringify will return as "prop":"function()..." (function as a string)... The code above will create a placeholder (e.g: "prop":"fn1") and create a function list... After, will replace every placeholder to original function body...
let x = ‽; // "‽" Could be any type(String, Array, Object, ...) The goal is to make the statement true
`${x}` != '' + x // true
`${x}` !== '' + x // true
What should we assign to X to make the statements true?
I'm thinking it's not possible to make it true, but is there any exception?
You need a value that serializes to a different value every time it's converted to a string.
const x = {
[Symbol.toPrimitive]: (y => () => y++)(0),
};
console.log(`${x}` !== '' + x);
Felix's answer covers one absolutely valid approach, but I'll add another one here that doesn't require incrementing or random values.
`${x}`
converts x to a string using ToString, which in the case of x being an object, does ToPrimitive(argument, string).
'' + x
on the other hand uses ApplyStringOrNumericBinaryOperator, which does ToPrimitive(rval).
You can take advantage of that difference in the 2nd parameter of the function. For example:
x = {
[Symbol.toPrimitive](hint) {
if (hint === "string") {
return 'a';
} else {
return 'b';
}
},
};
and now in this case
`${x}` === 'a' // true
'' + x === 'b' // true
You can also see this without Symbol by directly using toString and valueOf:
x = {
toString(){ return 'a'; },
valueOf(){ return 'b'; },
};
which produces the same thing as the last example, because valueOf is the default unless a string value is explicitly hinted, as in the case for ${x}.
This is a JavaScript function that determines if arguments are strings
just curious if anyone has a way to simplify this function?
I can't help but think there is since there are so many similarities in the parameters
typeof x === "string"
that there is a way to simplify it. I asked my teachers and they told me they were unaware of any.
function isString(string1, string2, string3) {
if (typeof x === "string" && typeof y === "string" && typeof z === "string")
console.log("Yes!" + x + " " + y + " " + z)
else {
console.log("Nope!")
}
}
isString("String1", "String2", "String3")
I really look forward to reading your responses!
Thanks
-Joe
You might be looking for rest parameters or the arguments object that let you handle an arbitrary amount of arguments, together with looping over them (or using a helper method for that):
function areStrings(...args) {
if (args.every(x => typeof x === "string"))
return "Yes!" + args.join(" ");
else
return "Nope!";
}
console.log(areStrings("String1", "String2", "String3"));
console.log(areStrings(5, "someString"));
you can receive your params as an array o, and the use functional parameter to check:
function isString(...strings) {
if (strings.every(s => typeof s === 'string'))
console.log("They are string")
else
console.log("They are not string")
}
If you want to check if multiple variables are strings, you can use this function:
function isString(...args){
for(var i = 0; i<args.length; i++){
if(typeof args[i] !== 'string'){
return false;
}
}
return true;
}
You can pass as much parameters as you want and the result will be only true if all parameters are strings.
Example:
isString(4) returns false
isString("Hello World") returns true
isString("I am a string", 3, true, "Hello") returns false
isString("Hello World", "Welcome") returns true
Clear answer, simple effective. I come to propose another way of doing things. Arrow function + ternary. More verbose but condense.
(I didn't know every cool :))
var resultAmeliored = (...args) => 'Var: '+args.map(val => val+' is '+typeof val).join(', ');
var areStrings = (...args)=>args.every(s => typeof s === 'string')?true:false;
console.log(areStrings("String1", "String2", "String3"));
//true
console.log(resultAmeliored(5, "someString"));
//"Var: 5 is number, someString is string"
How can I check if a variable's type is of type Boolean?
I mean, there are some alternatives such as:
if(jQuery.type(new Boolean()) === jQuery.type(variable))
//Do something..
But that doesn't seem pretty to me.
Is there a cleaner way to achieve this?
That's what typeof is there for. The parentheses are optional since it is an operator.
if (typeof variable == "boolean") {
// variable is a boolean
}
With pure JavaScript, you can just simply use typeof and do something like typeof false or typeof true and it will return "boolean"...
But that's not the only way to do that, I'm creating functions below to show different ways you can check for Boolean in JavaScript, also different ways you can do it in some new frameworks, let's start with this one:
function isBoolean(val) {
return val === false || val === true;
}
Or one-line ES6 way ...
const isBoolean = val => 'boolean' === typeof val;
and call it like!
isBoolean(false); //return true
Also in Underscore source code they check it like this(with the _. at the start of the function name):
isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
};
Also in jQuery you can check it like this:
jQuery.type(true); //return "boolean"
In React, if using propTypes, you can check a value to be boolean like this:
MyComponent.propTypes = {
children: PropTypes.bool.isRequired
};
If using TypeScript, you can use type boolean also:
let isDone: boolean = false;
Also another way to do it, is like converting the value to boolean and see if it's exactly the same still, something like:
const isBoolean = val => !!val === val;
or like:
const isBoolean = val => Boolean(val) === val;
and call it!
isBoolean(false); //return true
It's not recommended using any framework for this as it's really a simple check in JavaScript.
If you just want to check for a primitive value
typeof variable === 'boolean'
If for some strange reason you have booleans created with the constructor, those aren't really booleans but objects containing a primitive boolean value, and one way to check for both primitive booleans and objects created with new Boolean is to do :
function checkBool(bool) {
return typeof bool === 'boolean' ||
(typeof bool === 'object' &&
bool !== null &&
typeof bool.valueOf() === 'boolean');
}
function checkBool(bool) {
return typeof bool === 'boolean' ||
(typeof bool === 'object' &&
bool !== null &&
typeof bool.valueOf() === 'boolean');
}
console.log( checkBool( 'string' )); // false, string
console.log( checkBool( {test: 'this'} )); // false, object
console.log( checkBool( null )); // false, null
console.log( checkBool( undefined )); // false, undefined
console.log( checkBool( new Boolean(true) )); // true
console.log( checkBool( new Boolean() )); // true
console.log( checkBool( true )); // true
console.log( checkBool( false )); // true
There are three "vanilla" ways to check this with or without jQuery.
First is to force boolean evaluation by coercion, then check if it's equal to the original value:
function isBoolean( n ) {
return !!n === n;
}
Doing a simple typeof check:
function isBoolean( n ) {
return typeof n === 'boolean';
}
Doing a completely overkill and unnecessary instantiation of a class wrapper on a primative:
function isBoolean( n ) {
return n instanceof Boolean;
}
The third will only return true if you create a new Boolean class and pass that in.
To elaborate on primitives coercion (as shown in #1), all primitives types can be checked in this way:
Boolean:
function isBoolean( n ) {
return !!n === n;
}
Number:
function isNumber( n ) {
return +n === n;
}
String:
function isString( n ) {
return ''+n === n;
}
You can use pure Javascript to achieve this:
var test = true;
if (typeof test === 'boolean')
console.log('test is a boolean!');
If you want your function can validate boolean objects too, the most efficient solution must be:
function isBoolean(val) {
return val === false || val === true || val instanceof Boolean;
}
I would go with Lodash: isBoolean checks whether the passed-in variable is either primitive boolean or Boolean wrapper object and so accounts for all cases.
BENCHMARKING:
All pretty similar...
const { performance } = require('perf_hooks');
const boolyah = true;
var t0 = 0;
var t1 = 0;
const loops = 1000000;
var results = { 1: 0, 2: 0, 3: 0, 4: 0 };
for (i = 0; i < loops; i++) {
t0 = performance.now();
boolyah === false || boolyah === true;
t1 = performance.now();
results['1'] += t1 - t0;
t0 = performance.now();
'boolean' === typeof boolyah;
t1 = performance.now();
results['2'] += t1 - t0;
t0 = performance.now();
!!boolyah === boolyah;
t1 = performance.now();
results['3'] += t1 - t0;
t0 = performance.now();
Boolean(boolyah) === boolyah;
t1 = performance.now();
results['4'] += t1 - t0;
}
console.log(results);
// RESULTS
// '0': 135.09559339284897,
// '1': 136.38034391403198,
// '2': 136.29421120882034,
// '3': 135.1228678226471,
// '4': 135.11531442403793
The most reliable way to check type of a variable in JavaScript is the following:
var toType = function(obj) {
return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
toType(new Boolean(true)) // returns "boolean"
toType(true); // returns "boolean"
The reason for this complication is that typeof true returns "boolean" while typeof new Boolean(true) returns "object".
the easiest way to check for true and false is :
(typeof value === "boolean") ,
but if value is an instance of the Boolean class, then it will return "object". so to handle that we must add another condition to check if :
(value instanceof Boolean)
the code snippet :
const value = false;
//const value = new Boolean(10);
//const value = new Boolean("hi");
if((typeof value === "boolean") || (value instanceof Boolean))
console.log("boolean");
else
console.log("not boolean");
You can create a function that checks the typeof for an argument.
function isBoolean(value) {
return typeof value === "boolean";
}
Sometimes we need a single way to check it. typeof not working for date etc. So I made it easy by
Date.prototype.getType() { return "date"; }
Also for Number, String, Boolean etc. we often need to check the type in a single way...
Creating functions like isBoolean which contains oneliner typeof v === "boolean" seems very unhandy in long term. i am suprised that almost everyone suggest to create your own function. It seems to be same cancer as extending native prototypes.
you need to recreate them in every project you are involved
other developers might have different habits,, or need to check source of your function to see which impementation of check you use, to know what are weak points of your check
you will be fruustrated when you will try to write one liner in console on site which doesn't belong to your project
just memoize typeof v === "boolean" and that's all.
Add a template to your IDE to be able to put it by some three letter shortcut and be happy.
if(['true', 'yes', '1'].includes(single_value)) {
return true;
}
else if(['false', 'no', '0'].includes(single_value)) {
return false;
}
if you have a string
One more decision with es2015 arrow function
const isBoolean = val => typeof val === 'boolean';
The most readable: val === false || val === true.
Also readable: typeof variable == typeof true.
The shortest, but not readable at all: !!val === val.
Explanation:
[!!] The double exclamation mark converts the value into a Boolean.
[===] The triple equals test for strict equality: both the type (Boolean) and the value have to be the same.
If the original value is not a Boolean one, it won't pass the triple equals test. If it is a Boolean variable, it will pass the triple equals test (with both type & value).
Tests:
!!5 === 5 // false
!!'test' === 'test' // false
let val = new Date(); !!val === val // false
!!true === true // true
!!false === false // true
Update: The previous solution is more specific, you can choose which value you want to consider as a boolean and you can add that in regex, If you need a more general solution and don't want to add a library then check out the below solution(taken from lodash's boolean)
function getTag(value) {
if (value == null) {
return value === undefined ? '[object Undefined]' : '[object Null]'
}
return toString.call(value)
}
function isObjectLike(value) {
return typeof value === 'object' && value !== null
}
function isBoolean(value) {
return value === true || value === false ||
(isObjectLike(value) && getTag(value) == '[object Boolean]')
}
Previous Solution
const isBoolean = (val) => {
const boolValuesRegex = /true|false/; // Add other /true|false|1|0|on|off/
if (val === undefined || val === null) return false;
return boolValuesRegex.test(val.toString().toLowerCase());
}
const values = [true, false, 'true', 'false', 'TRUE', 'FALSE', 'sampletext', 1, undefined, null, (() => {}), {}, []];
document.body.innerHTML = values.map(x => `${x} - ${isBoolean(x)}`).join('</br>');
In nodejs by using node-boolify we can use isBoolean();
var isBoolean = require('node-boolify').isBoolean;
isBoolean(true); //true
isBoolean('true'); //true
isBoolean('TRUE'); //false
isBoolean(1); //true
isBoolean(2); //false
isBoolean(false); //true
isBoolean('false'); //true
isBoolean('FALSE'); //false
isBoolean(0); //true
isBoolean(null); //false
isBoolean(undefined); //false
isBoolean(); //false
isBoolean(''); //false
I have an object (json) like this in node.js:
var data = {
string : "name",
number : 123456789 ,
n : null ,
bool : false ,
bool2 : true
};
But I need to conver it to something like this:
{
string : "name",
number : "123456789" ,
n : "null" ,
bool : "false" ,
bool2 : "true"
};
I used this codes but not works.
for ( var index in data ){
data[index] = data[index].toString();
};
// or this
data.toString();
How can I fix it?
UPDATE
this data object is created as a new mongoose schema.
Your code looks fine, except for one thing: null doesn't have .toString() method. So, it's best to use String instead:
for ( var key in data ){
data[key] = String(data[key]);
};
String is a string constructor. It takes anything and produces a string representation of it.
Update
But this solution won't work for complex data structures. Though, if you need a JSON string, then you could use JSON.stringify with tricky replacer:
function replaceWithString(k, v) {
if ((typeof v === 'object') && (v !== null)) {
return v;
} else {
return String(v);
}
}
JSON.stringify(data, replaceWithString);
and if you want to make it pretty:
JSON.stringify(data, replaceWithString, 2);
N.B. As Bergi noticed in comments, you could use Object(v) === v instead of (typeof v === 'object') && (v !== null) to check that v is an object.
Update2
It looks like data in your example is a mongoose document.
The problem with mongoose is that it wraps all its object with the whole pack of nasty getters and setters to make them look like plain JS objects, which they are not.
So, if you're working with mongoose documents, you should call .toObject() or .toJSON() before trying to do anything with it:
data = doc.toObject(); // converts doc to plain JS object
Though, my second solution with JSON.stringify should work anyway, because stringify calls .toJSON() automatically.
for (var index in data) {
if (data[index] === null) {
data[index] = "null";
}
else if (data[index] === undefined) {
data[index] = "undefined";
}
else {
data[index] = data[index].toString();
}
}
Try this:
var val = null;
for(var key in data){
if(data.hasOwnProperty(key)){
val = data[key];
val = val === null ? 'null' : (val === undefined ? 'undefined' : val.toString());
data[key] = val;
}
}
It simply converts null to "null" and undefined to "undefined"
Note that values of your object must be a primitive data type for this to work. btw, this will work fine for your example.
A simple
JSON.stringify(data);
should work.
when doing
data[index].toString();
you are referencing a null on the third run. null has no such method toString().
Just thought I'd answer with a code that's a bit different:
for(var x in data){
data[x] = ""+data[x]+"";
}
Works.