Difference between JSON.parse() and eval() - javascript

May need a Javascript language lawyer for this one:
var s1 = "{\"x\":\"y:z\"}"
var o = JSON.parse(s1)
var s2 = JSON.stringify(o)
$('span#s').text(s1);
$('span#s2').text(s2);
if (s1 === s2) {
$('span#areEqual').text('s1 === s2')
} else {
$('span#areEqual').text('s1 !== s2')
}
JSON.parse(s2) // okay
$('span#jsonParse').text("JSON.parse(s2) okay")
eval(s2) // bad mojo!
$('span#eval').text("eval(s2) okay")
eval("("+s2+")") // bad mojo, too!
$('span#eval2').text("eval((s2)) okay")
eval fails on s1, s2, and "("+s2+")".
jsFiddle here.

Your problem is that you mixing two unrelated things.
eval() is built-in javascript function, which main purpose is to interpret string of javascript code (thus make potentional security hole)
JSON.parse() function is for parse JSON string. Although very simmilar, do not make mistake, JSON is not Javascript and there are tiny differences. You should not use eval() for parsing JSON
What are the differences between JSON and JavaScript object?

$eval is automatically evaluated against a given scope.
For example:
$scope.a = 2;
var result = $scope.$eval('1+1+a');
// result is 4
$parse does not require scope. It takes an expression as a parameter and returns a function. The function can be invoked with an object that can resolve the locals:
For example:
var fn = $parse('1+1+a');
var result = fn({ a: 2 });
// result is 4

When you use eval for parsing JSON you need to wrap your expression with parentheses
eval('(' + s2 + ')');
jsfiddle

Check out what the specification says about JSON and eval
http://www.json.org/js.html
Notice this part specifically
The eval function is very fast. However, it can compile and execute
any JavaScript program, so there can be security issues. The use of
eval is indicated when the source is trusted and competent. It is much
safer to use a JSON parser. In web applications over XMLHttpRequest,
communication is permitted only to the same origin that provide that
page, so it is trusted. But it might not be competent. If the server
is not rigorous in its JSON encoding, or if it does not scrupulously
validate all of its inputs, then it could deliver invalid JSON text
that could be carrying dangerous script. The eval function would
execute the script, unleashing its malice.
JSON is just a javascript object, and nothing more. Valid javascript could include functions, execution blocks, etc. If you just eval() a string, it could have code it in. JSON will parse if it's just JSON, but you can't know for sure by just stuffing it into eval. For example
var s = "(function(){ /* do badStuff */ return {s: 123, t: 456}; })()";
var result = eval(s);
Would give you a var result with the contents {s: 123, t: 456} but would also execute any code hidden in your function. If you were taking this input from elsewhere, code could be executing and not actually break anything on your end. Now the same example with JSON.parse
var result = JSON.parse(s);
It throws an error with the message:
Uncaught SyntaxError: Unexpected token (
So the parse saves you from remote code execution here, even if someone tried to sneak it in.

eval wasn't an expression - i've updated it to evaluate eval(s2 === s1);
Otherwise it will try & execute what's within the eval & stop execution.

eval() attempts to evaluate a block of JavaScript code. If you had created a script file that started with the same text, you would have gotten the same error. In that context, I believe the braces signify a compound statement, as in an if-statement or for-statement body, but at the beginning of the compound statement is a string followed by a colon, which is not valid syntax.
If you wanted a string that would evaluate to an object, you'd have to enclose the object expression in parentheses to make it explicit that it's an expression. But as apocalypz says, you should not attempt to eval JSON. It's wrong on so many levels.

if you really want to use eval instead of JSON.parse() for parsing JSON then you should write something like
var o2; // declare o2 out of eval in case of "use strict"
eval("o2 = "+s1); // parse s1 and the assignment to the local o2
console.log(o2); // enjoy the local variable :)
...

Related

What does .join`` mean in JavaScript? [duplicate]

This question already has answers here:
Backticks (`…`) calling a function in JavaScript
(3 answers)
Closed 2 years ago.
I came across this code:
new Array(10).fill('1').join``;
I do not know what the meaning of the signs `` used immediately after .join is.
I thought the correct syntax would be new Array(10).fill('1').join('').
Any ideas are welcome thanks!
const data = new Array(10).fill('1').join``;
console.log(data)
What you're seeing is a tagged template literal, part of the ES6 specification. That pattern is heavily used in e.g. Google's Polymer Project (now lit-element). It invokes the supplied function (or in this case method) with the supplied template literal.
In addition to being used to create a Domain-Specific Language (DSL) of sorts, it's also frequently used to reduce the byte count for Javascript answers in code golf In the case of the Google project I linked, it's used as part of a domain-specific HTML templating language.
arr.join``; is a tagged template literal.
It applies join (or any other function) to "the content of the template literal string".
When I say "applies to the content of the template literal string" it is actually a little bit more complicated than that: all static parts of the string are put into an array as the first argument and the interpolated values as the remaining arguments.
A simple example will make it easier to understand:
var demo = (str, ...names) => {
console.log(str);
console.log(names);
};
var users = ['john', 'jane', 'joe'];
demo`Members: ${users[0]}, ${users[1]} and ${users[2]}.`;
// LOG: ["Members: ", ", ", " and ", "."]
// LOG: ["john", "jane", "joe"]
demo``;
// LOG: [""]
// LOG: []
The last line answers your question because arr.join``; is the same as arr.join(""); just two characters shorter which is useful when you compete in JS1K for example.
In this particular case I'm not sure that the author wanted to save a few bytes because we can shorten the initial statement as follow:
new Array(10).fill('1').join``;
new Array(10).fill(1).join``;
Array(10).fill(1).join``;
'1'.repeat(10);
'1111111111';
Is a tagged template just a glorified function call?
These two expressions may look similar but they are not:
var yy = 20;
year`19${yy}`;
year(`19${yy}`);
In the first expression we have access to the static and dynamic parts of the string: "19" and 20. In the second expression we only get the "final" string "1920".
This is useful when one wants to preserve intent yet allow complex text processing behind the scene.
We know that this is unsafe:
var sql = `SELECT * FROM users where email="${email}" AND password="${password}"`;
To mitigate the risk of SQL injection, it is not uncommon to see things like:
var sql = select('*').from('users').where('AND').eq('email', email).eq('password', password);
However it could be argued that we have traded the expressiveness of SQL with a somewhat awkward API that people are less likely to be familiar with.
If a tagged template literal has access to both static and dynamic parts of a string we can build domain-specific tagged template literals:
var sql = safe_sql`SELECT * FROM users where email="${email}" AND password="${password}"`;
This syntax is called Tagged Template literals , Calling a function by passing backitlists.
It works that way
function taggedTemplate(str, ...exps){
//str is an [] containg the strings in the tagged literals.
//exps is an [] containg the expressions passes in the tagged literals
console.log(str[0]) // "Number "
console.log(exps[0]) // "19"
}
taggedTemplate`Number ${19}`
So, The join method supports tagged literals, It could be something like this.
Array.prototype.join = function(arg){
let str = typeof arg === "string" ? arg : arg[0];
//joins the array using str
}
It is an abomination that works. So you are correct in assuming that when you do a join function call, you pass a empty string so that there is nothing put in between the resulting string.
In JavaScript, you can declare strings with "quotes", 'apostrophes', and `template literals`.
The developer who wrote that line of code thought they were being clever by skipping the opening and closing parentheses, when in reality they just made the code slower, more complex, and more confusing for everyone else.

What difference does it makes to parse something as an object literal rather than as a block?

Sorry for my ignorance on JavaScript basic concepts.
It boils down to this:
Literal - A value found directly in the script. Examples:
3.14
"This is a string"
[2, 4, 6]
Expression - A group of tokens, often literals or identifiers, combined
with operators that can be evaluated to a specific value. Examples:
2.0
"This is a string"
(x + 2) * 4
There is a very clear difference b/w the above two in Javascript.
I happen to read this article. And I am familiar with the difference b/w function declaration & function expression and when to use one over other or vice-versa.
From the same article:
....You might also recall that when evaluating JSON with eval, the string
is usually wrapped with parenthesis — eval('(' + json + ')'). This is
of course done for the same reason — grouping operator, which
parenthesis are, forces JSON brackets to be parsed as expression
rather than as a block:
try {
{ "x": 5 }; // "{" and "}" are parsed as a block
} catch(err) {
// SyntaxError
}
({ "x": 5 }); // grouping operator forces "{" and "}" to be parsed as object literal
So, what difference does it make to parse something as an object literal other than parsing them as a block?
And for what purpose should I consider to make use of grouping character, in context of parsing?
First, don't eval JSON, use JSON.parse on the String source
A block is a "group of expressions" for example,
let x = 0;
if (true) {
// this is a block
++x;
}
However, equally this is also a block
let x = 0;
{ // hi there, I'm a block!
++x;
}
This means when the interpreter sees block notation, it assumes a block even if you do something like this
{ // this is treated as a block
foo: ++x
}
Here, foo acts as a label rather than a property name and if you try to do more complex things with the attempted object literal, you're going to get a Syntax Error.
When you want to write an Object literal ambiguously like this, the solution is to force the interpreter into "expression mode" explicitly by providing parenthesis
({ // this is definately an Object literal
foo: ++x
})
A group that begins with a { and ends with a } is treated as either object literal or a block depending on context*.
Within an expression context the group is interpreted as an object literal. Writing a block within an expression context will generate a syntax error:
// Valid code:
foo = {a:b};
({a:b});
// Syntax errors:
foo = {var a = b};
({var a = b});
Outside of an expression context the group is interpreted as a block. Depending on exactly how the code is written, an object literal written outside of an expression context is either a syntax error or will be interpreted as a label.
*note: In the ECMAscript spec the word "context" is used to mean something specific. My use of the word here is with the general meaning in computer science with regards to parsing.

eval is evil, but is it flawed? [duplicate]

This question already has answers here:
Why does JavaScript's eval need parentheses to eval JSON data?
(7 answers)
Closed 7 years ago.
If I run this:
eval('{ear: {"<=": 6}}');
I get an error:
Uncaught SyntaxError: Unexpected token :
Let's create the object manually:
var foo = {};
foo.ear = {};
foo.ear["<="] = 6;
Now, the following code:
JSON.stringify(foo)
Returns the following string:
'{"ear":{"<=":6}}'
The same string as the one I started with (except the white characters, but those are irrelevant), so eval(JSON.stringify(foo)) returns the same syntax error error message. However:
$.parseJSON(JSON.stringify(foo))
is executed correctly. What is the reason of that?
EDIT:
As nnnnnn and Ron Dadon pointed out, the initial string and the result of stringify are different. However, as I pointed out in the question, even the result of stringify used as input for eval will result in the syntax error message.
EDIT2:
Based on the answers and experiments conducted, this function is interesting:
function evalJSON(text) {
return eval("(" + text + ")");
}
Main {} are parsed as block statement.
try to wrap in parenthesis:
eval('({ear: {"<=": 6}})');
In javascript {} can be parsed as a block or an object
examples:
//object
var user = {
name: "John",
age: "32"
};
//block
{
let a = 5;
console.log(a);
}
//object:
var a = {};
console.log({});
return {};
({});
//block:
function(){}
for(k in o){}
{}
Object literal notations need to be evaluated. This happens when you assign a variable:
var a = {ear: {"<=": 6}};
or when you put parentheses around it, a anonymous object:
({ear: {"<=": 6}});
Otherwise curly brackets are parsed as block markers. In your case this means {ear:...} is a label definition, the label is named ear. The next block {"<=": 6} gives you a syntax error because "<=": 6 is invalid syntax.
The same applies if you put this into an eval statement.
It's not flawed. To understand what is happening you need to understand what kind of statements are seen (left to right) by the parser.
A simple way to get into it is to play around with a Javascript AST Visualizer
You will get the same exception with much simpler {"b":4}. It's parsed as "b":4 inside a block. That's not valid javascript. No AST tree for you...
However it's due to an exception inside of a {} statement. That's a BlockStatement. AST tree:
A similar {b:4} would be understood as b:4, a valid js statement - a b label for 4... That's parsed as
Lastly, a ({b:4}) would be understood as an object declaration with a b property equal to 4. That's parsed as
ECMAScript 2015
On Blocks:
Block : { StatementList }
On eval itself:
Eval creates a new Realm, which is parsed (several steps here) as a sequence of Statements ( a StatementList ), which in turn this section has BlockStatement as a first option. This must start with { (see above), so if you wrap it with a bracket (({})) it cannot be a BlockStatement... But if it matches as BlockStatement it must be a BlockStatement.
A side note in section on Expressions:
An ExpressionStatement cannot start with a U+007B (LEFT CURLY BRACKET) because that might make it ambiguous with a Block

Converting JSON string to Angular function?

I have an angular service fetching some JSON data, and in this data I have a function as a string (building it server side and passing it to angular), something like:
{
"fn": "function( data ) { data.rendered = data.value; }"
}
Is it possible to parse this and use it as an expression with angular? I.e.
settings.myCustomFunction = serviceData.fn;
I've attempted to do this with different combinations of eval, $eval, $parse, but I haven't had any luck with any of them. They all either throw errors about "Unexpected *" or don't throw errors, but also don't seem to do anything.
You could wrap the string in parentheses to avoid the syntax error.
eval("(" + fnString + ")")
This makes it a syntactically valid JavaScript statement which is composed of a single function expression that you can pass around freely.
An anonymous function by itself, without any parentheses or LHS operators (like the commonly used assignment operator =) acting upon it, is not a syntactically valid JavaScript statement. That's why you'll see IIFEs (immediately invoked function expressions) wrapped up in parentheses in JS:
(function() {
// ...
})()
If you really want to get eval to work, then you can do this:
var settings = {};
eval("settings.myCustomFunction = " + serviceData.fn);
Using eval in this way of course opens your app up to possible malicious script injections.
You could do user new Function():
settings.myCustomFunction = new Function('argument',functionbody);
To execute:
settings.myCustomFunction();//
You might want to check out eval.call. This allows you to not care what the arguments are and use any arguments from strings. Of course to call it you must use the same arguments, BUT, you could string-drive that too, by concatting the args together when you eval.call it.
{
"fn": "function( data ) { return data + 1; }"
}
Example:
var serviceData = {
fn: '(function (data) {return data + 1;})'
};
var data = 44;
var result = window.eval.call(window, serviceData.fn)(data);
console.log(result); // 45

JavaScript: alert object name as a string

I'm trying to alert any JavaScript object as a string, in a function. This means if the parameter given to the function is window.document, the actual object, it should alert "window.document" (without quotes) as a literal string.
The following calls...
example(window);
example(window.document);
example(document.getElementById('something'));
...calling this function...
function example(o) {/* A little help here please? */}
...should output the following strings...
window
window.document
document.getElementById('something')
I've attempted to do this with combinations of toString() and eval() among some more miscellaneous shots in the dark without success.
No need insane backwards compatibility, newer ECMAScript / JavaScript features/functions are fine. Feel free to inquire for clarifications though the goal should be pretty straight forward.
This is not possible to do in a self contained script.
If using a preprocessor would be an option, then you could write one which converts example(whatever) into example('whatever'). Other than that I'm afraid you're out of luck.
The first problem is that objects don't have names.
The second problem is that from your examples, you're not really wanting to print the (nonexistent) name of an object, you want to print the expression that evaluated into a reference to an object. That's what you're trying to do in this example:
example(document.getElementById('something'));
For that to print document.getElementById('something'), JavaScript would have had to keep the actual text of that expression somewhere that it would make available to you. But it doesn't do that. It merely evaluates the parsed and compiled expression without reference to the original text of the expression.
If you were willing to quote the argument to example(), then of course it would be trivial:
example( "document.getElementById('something')" );
Obviously in this case you could either print the string directly, or eval() it to get the result of the expression.
OTOH, if you want to try a real hack, here's a trick you could use in some very limited circumstances:
function example( value ) {
var code = arguments.callee.caller.toString();
var match = code.match( /example\s*\(\s*(.*)\s*\)/ );
console.log( match && match[1] );
}
function test() {
var a = (1);
example( document.getElementById('body') );
var b = (2);
}
test();
This will print what you wanted:
document.getElementById('body')
(The assignments to a and b in the test() function are just there to verify that the regular expression isn't picking up too much code.)
But this will fail if there's more than one call to example() in the calling function, or if that call is split across more than one line. Also, arguments.callee.caller has been deprecated for some time but is still supported by most browsers as long as you're not in strict mode. I suppose this hack could be useful for some kind of debugging purposes though.
Don't know why you need this, but you can try walking the object tree recursively and compare its nodes with your argument:
function objectName(x) {
function search(x, context, path) {
if(x === context)
return path;
if(typeof context != "object" || seen.indexOf(context) >= 0)
return;
seen.push(context);
for(var p in context) {
var q = search(x, context[p], path + "." + p);
if(q)
return q;
}
}
var seen = [];
return search(x, window, "window");
}
Example:
console.log(objectName(document.body))
prints for me
window.document.activeElement

Categories