为何本地正常的$.grep+eval代码部署到服务器后报item未定义错误?
嘿,这个问题我之前帮朋友排查过,核心是服务器端的代码压缩工具和eval的作用域特性撞在一起导致的,具体来拆解下:
本地和服务器的代码不一样!
你本地跑的是没压缩的原始代码,但部署到服务器后,项目肯定启用了代码压缩(比如用Terser、UglifyJS这类工具)。压缩工具会做“无用代码清除”——当它发现你的$.grep回调里,item参数在eval之前完全没被显式调用时,会觉得这个参数是多余的,直接把它从函数参数里删掉,或者把它重命名成比如a、b这种极短的变量名。eval的作用域陷阱坑了你
你eval执行的paramBuilder字符串里,是直接写了item这个变量名的,但压缩后的回调函数里已经没有item这个参数了(或者被改成别的名字了),eval在当前作用域里找不到这个变量,自然就抛出ReferenceError了。为啥加个
console.log(item)就好了?
很简单,当你加了这行代码,压缩工具就能检测到item参数被实实在在用到了,就不会删除或重命名它,eval执行时就能在作用域里找到item,错误自然就消失了。
彻底抛弃
eval(最推荐)eval本身就有安全风险和性能损耗,还特别容易和代码压缩、混淆工具冲突。如果可以的话,把paramBuilder从字符串改成一个真正的函数:// 把原来的paramBuilder字符串改成函数 const checkItem = (item, index) => { // 这里写原来paramBuilder里的判断逻辑 return item.status === 'active'; // 举个例子 }; var data = $.grep(list, checkItem);这样既安全,又不会和压缩工具打架,代码可读性也更高。
临时救急:强制保留参数
如果实在没法改eval,可以像你那样加一行无用的item引用(比如void(item);,比console.log更隐蔽),或者在压缩配置里给这个函数加个标记,让压缩工具不要动它的参数。比如给Terser加注释标记:var data = $.grep(list, /* terser-ignore */ function (item, index) { return eval(paramBuilder); });不过这种方法只是治标,还是建议尽量换掉
eval。
内容的提问来源于stack exchange,提问作者Mustafa Tığ

