This is the 17th day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021″

JS string data parsing

Json.parse () is often used to parse JSON strings passed from the back end. There is nothing wrong with this in itself, but it can cause errors in some cases.

Tipping site

Recently, when processing string-like data, the code reported an error, the scene situation is how, we see the following simulation scene

Why? I usually look at the MDN documentation to see if I have a problem with my usage. Sure enough, two of them stood out:

JSON.parse() does not allow trailing commas

JSON.parse() does not allow single quotes

It is also specified in the standard JSON format

Objects and Arrays

Property names must be double-quoted strings; trailing commas are forbidden.

Looking at the figure above, we made two mistakes

  1. JsonStr in the stringnamearrIt should be wrapped in “double-quote”
  2. [1, 2, 3,]In the end.Need to delete. The trailing commas are forbidden.

B: well… When parsing a string, we may encounter a string that does not conform to the JSON specification. How can we effectively parse the data in the string?

The first thing that comes to mind is the eval() method, because the eval() function executes the string as if it were JavaScript code.

So let’s see what happens?

const jsonStr = '{name: "hello," arr: [1, 2, 3,]}'
eval(jsonStr)
// ERROR: Uncaught SyntaxError: Unexpected token ':' at <anonymous>:1:6
Copy the code

en… An error was reported because the token was unexpected. On reflection eval will parse the string we pass in, and our string will start and end with {}, so the JS engine will treat it as a statement block, so it must be forced to convert it to an expression.

How do I convert that into an expression?

The easiest way to do this is to put parentheses around it.

So let’s experiment

const jsonStr = '{name: "hello," arr: [1, 2, 3,]}'
eval(` (${jsonStr}) `)
// return {name: 'hello', arr: Array(3)}
Copy the code

So let’s try another way to get the JS engine to write this as an expression.

const jsonStr = '{name: "hello," arr: [1, 2, 3,]}'
eval(`!${jsonStr}`)
/ / return false. {} = false;} = false; {} = false;} = false;
Copy the code

That seems to have solved our problem.

So the question is, are there any side effects to eval? There is a clear message in MDN Web Docs, and here I’ve excerpted two of the most important ones.

  1. If you use it indirectlyeval(), such as calling it by reference rather than directlyeval. fromECMAScript 5It works in a global scope, not a local scope.
  2. eval()Is a dangerous function that executes code with the same permissions as the caller. If you are usingeval()The string code being run has been modified by a malicious party (someone with bad intentions) and you may end up running malicious code on the user’s computer under the authority of your web page/extension. More importantly, third-party code can see oneeval()Scope when called, which can also lead to some different ways of attacking. similarFunctionThey’re less likely to be attacked.

It seems that when writing code, we should try not to use Eval, and if we do, we should make sure that our code with eval content blocks cannot be accessed by users or third-party code.

What happens if a user or a third party writes something and we use an eval call, a piece of code to show you

const userData = '{name: "bamboo", age: alert("It is a secret!" )} '

eval(` (${userData}) `)

Copy the code

When you copy the code to the browser console, we can see that the alert method is called in the browser. If the alert method is a malicious attack code, it can get really bad.

Function

If you really need to use Eval to parse JSON-like code, use Function, since this method provides slightly more security and is much more efficient than Eval.

// Bad code using eval
function looseJsonParse(obj){
    return eval("(" + obj + ")");
}
console.log(looseJsonParse(
   "{a:(4-1), b:function(){}, c:new Date()}"
))

// Better code not to use eval
function looseJsonParse(obj){
    return Function('"use strict"; return (' + obj + ') ') (); }console.log(looseJsonParse(
   "{a:(4-1), b:function(){}, c:new Date()}"
))

Copy the code

Compare the two snippets above, and they seem to work in the same way, but think again: Eval’s code is much slower. Note that c: new Date() is in the body. In functions without eval, objects are evaluated globally, so browsers can safely assume that Date comes from window.date rather than a local variable named Date.

Thus, in the Eval () version of the code, the browser is forced to make costly lookup calls to check for the presence of any local variables named Date(). This is very inefficient compared to Function().

In a similar situation, what if you really want to be able to call the Date Function from code inside Function()? Should you duck and retreat to eval()? Absolutely not. Don’t ever do that. Instead, try to solve the problem with closures and so on.

function Date(n){
    return ["Monday"."Tuesday"."Wednesday"."Thursday"."Friday"."Saturday"."Sunday"][n%7 || 0];
}
function runCodeWithDateFunction(obj){
    return Function('"use strict"; return (' + obj + ') '() ()Date
    );
}
console.log(runCodeWithDateFunction(
   "function(Date){ return Date(5) }"
))
Copy the code

Parse. If jSON. parse cannot parse, we can use eval or Function. However, I will try to use Function instead of eval to improve the security and efficiency of the code. And never use eval or Function to parse user or third-party data directly.