为何第一个脚本调用replaceAll会触发第二个脚本的自定义方法并报错?
问题与解决方案
问题描述
两段脚本运行时出现异常:第一个脚本调用replaceAll时触发了第二个脚本的自定义函数,导致正则语法错误,最终第一个脚本也终止运行。已知失败原因,但不清楚第二个脚本为何被触发,且无法修改第二个脚本,需修复第一个脚本。
当前执行流程
- 调用
evaluate(expression) - 执行
cleanExpression.replaceAll("[","")时触发第二个脚本的自定义函数 - 自定义
function(search, replacement)执行return target.replace(new RegExp(search, 'g'), replacement)时因正则语法错误失败 - 最终导致
evaluate函数执行失败
第一个脚本
class CustomProduct extends HTMLElement { evaluate(expression) { let cleanExpression = expression; cleanExpression = cleanExpression.replaceAll("[",""); return math.evaluate(cleanExpression) } }
第二个脚本
function SocialLoginNamespace() { (function() { String.prototype.replaceAll = function(search, replacement) { var target = this; return target.replace(new RegExp(search, 'g'), replacement); }; }).call(this); var socialLogin = new SocialLoginNamespace().socialLogin; socialLogin.init();
报错信息
resource.js?shop=ccc.myshopify.com&cache_key=1650344:1241 Uncaught (in promise) SyntaxError: Invalid regular expression: /[/: Unterminated character class at new RegExp (<anonymous>) at SocialLoginNamespace.String.replaceAll (resource.js?shop=ccc.myshopify.com&cache_key=1650344:1241:31) at CustomProduct.evaluate (global.js:7956:39) at CustomProduct.evaluateFormula (global.js:7017:43) at async CustomProduct.textSelecting (global.js:5935:31) at async HTMLInputElement.<anonymous> (global.js:8564:9)
原因分析
- 第二个脚本在
String.prototype上添加了自定义的replaceAll方法,该方法会覆盖环境中原生的String.replaceAll(若环境支持ES2021+原生方法),或在不支持原生方法的环境中直接成为replaceAll的唯一实现。 - 第一个脚本中调用
cleanExpression.replaceAll("[", "")时,本质是调用字符串原型链上的replaceAll方法,也就是第二个脚本定义的自定义函数,因此触发了第二个脚本的代码执行。 - 自定义
replaceAll方法直接将未转义的search参数传入new RegExp,而[是正则的特殊字符(字符类起始标记),未转义会导致正则语法错误,进而抛出异常终止执行。
修复方案
由于无法修改第二个脚本,可通过以下两种方式在第一个脚本中规避自定义replaceAll:
方式1:使用String.replace配合全局正则
将原replaceAll调用替换为正则全局替换,手动转义特殊字符:
class CustomProduct extends HTMLElement { evaluate(expression) { let cleanExpression = expression; cleanExpression = cleanExpression.replace(/\[/g, ""); // 转义[后用全局正则替换 return math.evaluate(cleanExpression) } }
这里\[是转义后的左方括号,g标记实现全局替换,效果与原生replaceAll一致,且不会触发自定义方法。
方式2:强制调用原生String.replaceAll
通过call方法直接调用原生的replaceAll,绕过原型上的自定义实现(要求环境支持ES2021+):
class CustomProduct extends HTMLElement { evaluate(expression) { let cleanExpression = expression; // 直接调用原生replaceAll方法,指定上下文为cleanExpression cleanExpression = String.prototype.replaceAll.call(cleanExpression, "[", ""); return math.evaluate(cleanExpression) } }
内容的提问来源于stack exchange,提问作者Guy Online
相关产品推荐
相关产品推荐

