接口突然返回字符串,eval派上用场了

lxf2023-03-13 17:39:02

1、背景

在请求某个文件时,正常情况下接口会返回json对象,但是这个比较特殊,竟然返回json字符串

大概率是因为数据量太大,接口自动转化为字符串来传输

接口突然返回字符串,eval派上用场了

心想,这也简单,判断一下类型,是字符串就将其转化为json对象即可

if (typeof data === 'string') {
  data = JSON.parse(data)
}

然后就报错了:

接口突然返回字符串,eval派上用场了

大概意思就是json字符串中存在非法字符,使用JSON.parse()解析就报错了

JSON.parse()只能解析标准JSON字符串

可是24M的数据,哪里知道是出现了有哪些非法字符

google了一下,发现eval也有将字符串转化为对象的功能,过程也很简单

if (typeof data === 'string') {
  data = eval("(" + data + ")")
}

好了 问题解决了

2、eval究竟是啥

MDN上的介绍eval() 会将传入的字符串转化成js代码进行执行

eval(String)

参数:

  • 如果传入String,会转化为js进行执行
  • 如果传入非String,直接返回该值

返回值:

  • 字符串的执行结果
  • 如果执行结果为空,则返回undefined

小试一下:

eval('1+1')  // 2
eval(new String('1'))  // String {'1'}
eval({})  // {}

2.1 为啥需要加上括号eval('(' + data + ')')

先看一下直接传入对象字符串是怎么样的

eval('{}')  // undefined

!! 居然是一个undefined

这是因为当'{}'被转成js代码后,{}会被当成一个语句来执行,其执行结果为空,所以也就返回了undefined


js中,用()将一个语句括起来,那么语句就变成表达式了

let a = 1
const falg = true

if (flag) {
  a = 2
}

这是一个很简单的逻辑,但是写法还是有点繁琐了,可以用&&代替这个if语句

flag && (a = 2)

因为&&后面必须跟表达式而不能是语句,所以用()a=2赋值语句转化为表达式即可


同样道理,既然eval('{}') 会将 {} 当成语句来执行,那就给它拼接上 () ,让它变成一个表达式, eval将其转化为js代码后就会解析成对象了

eval('({})')  // {}

// 区别于
eval('{}')  // undefined
eval({})  // {},为什么eval({})又会返回{},因为它不是字符串 会直接返回其本身

2.2 永远不要使用eval

eval存在两个原因,让我们不推荐使用它:

  1. 不安全
  2. 性能问题

不安全

所谓的不安全就是容易被脚本注入

eval拥有执行代码的权力,如果我们的字符串被替换成了一段恶意的代码,攻击者就可以在我们电脑上运行恶意代码了

eval("alert('我是恶意代码')")

性能问题

eval会调用js解析器,会进行冗余的变量名查找、确定位置、设置值等一系列操作

另外,新内容将会通过 eval() 引进给变量,比如更改该变量的类型,因此会强制浏览器重新执行所有已经生成的机器代码以进行补偿

2.3 使用Function代替eval

鉴于eval()不安全和性能问题,有一个很好的方法可以代替eval

  • new Function()

它和eval一样,可以对无效的json对象进行转化

const str = "{name:'zxc',age:18}"

new Function('return' + str)()  // {name: 'zxc',age: 18}

几点区别:

// 转化json字符串
new Function('return' + '{}')()  // {}

// 转化array字符串
new Function('return' + '[1, 2]')()  // [1, 2]

// 转化function字符串
new Function("return function foo() { console.log(123)}")()() // 123

// 转化对象
new Function('return' + {})()  // error: Unexpected identifier 'Object'

// 转化字符串
new Function('return' + '1+1')()  // error: return1 is not defined
  1. new Function()只能接受对象字符串
  2. 不需要使用括号()进行转义

3、总结

一、现在知道了,字符串转化为对象有3种方式

  1. JSON.parse(strJSON)

只能转化标准的JSON字符串(键必须是字符串,字符串必须使用双引号括起来)

  1. eval(str)
  • 将字符串转化为js语句,并执行
  • 如果不希望转化后是js语句,可使用括号将字符串包裹起来
  1. Function(str)()

只能转化对象字符串

二、非必要不适用eval ,尽量使用Function代替

三、转化标准json字符串使用JSON.parse(),非标使用Function


参考:developer.mozilla.org/zh-CN/docs/…