在Javascript中用大写替换正则表达式捕获组

Eva*_*oll 67 javascript regex replace uppercase

我想知道如何用JavaScript中的大写替换捕获组.这是我迄今为止尝试过的一个简化版本,它不起作用:

> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'
Run Code Online (Sandbox Code Playgroud)

你能解释一下这段代码有什么问题吗?

Cha*_*ion 141

你可以传递一个函数replace.

var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });
Run Code Online (Sandbox Code Playgroud)

说明

a.replace( /(f)/, "$1".toUpperCase())
Run Code Online (Sandbox Code Playgroud)

在此示例中,您将字符串传递给replace函数.由于您使用的是特殊替换语法($ N抓取第N次捕获),因此您只需提供相同的值.这toUpperCase实际上是欺骗,因为你只是使替换字符串大写(这有点无意义,因为$和一个1字符没有大写,所以返回值仍然是"$1").

a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))
Run Code Online (Sandbox Code Playgroud)

信不信由你,这个表达的语义是完全一样的.

  • 啊,我明白你的意思了,我是"u $ 1".取代巫毒的结果不是显然会用'$ 1'代替第一个捕获组. (4认同)

Jos*_*ari 11

我知道我迟到了,但这是一个更短的方法,更符合你最初的尝试.

a.replace('f', String.call.bind(a.toUpperCase));
Run Code Online (Sandbox Code Playgroud)

所以你哪里出错了,这个新的伏都教是什么?

问题1

如前所述,您试图将被调用方法的结果作为String.prototype.replace()的第二个参数传递,而您应该将引用传递给函数

解决方案1

这很容易解决.简单地删除参数和括号将为我们提供参考,而不是执行该功能.

a.replace('f', String.prototype.toUpperCase.apply)
Run Code Online (Sandbox Code Playgroud)

问题2

如果您现在尝试运行代码,则会收到错误消息,指出undefined不是函数,因此无法调用.这是因为String.prototype.toUpperCase.apply实际上是通过JavaScript的原型继承对Function.prototype.apply()的引用.所以我们实际上看起来更像是这样

a.replace('f', Function.prototype.apply)
Run Code Online (Sandbox Code Playgroud)

这显然不是我们想要的.如何知道在String.prototype.toUpperCase()上运行Function.prototype.apply ()

解决方案2

使用Function.prototype.bind(),我们可以创建一个Function.prototype.call的副本,其上下文专门设置为String.prototype.toUpperCase.我们现在有以下内容

a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))
Run Code Online (Sandbox Code Playgroud)

问题3

最后一个问题是String.prototype.replace()会将几个参数传递给它的替换函数.但是,Function.prototype.apply()期望第二个参数是一个数组,而是获取一个字符串或数字(取决于您是否使用捕获组).这将导致无效的参数列表错误.

解决方案3

幸运的是,我们可以简单地在Function.prototype.apply()中替换Function.prototype.call()(它接受任意数量的参数,其中没有一个具有类型限制).我们现在已经到了工作代码!

a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))
Run Code Online (Sandbox Code Playgroud)

脱落字节!

没有人想要打造原型很多次.相反,我们将利用我们拥有通过继承引用相同方法的对象这一事实.作为函数的String构造函数继承自Function的原型.这意味着我们可以在String.call中替换Function.prototype.call(实际上我们可以使用Date.call来保存更多的字节但是语义更少).

我们也可以利用变量'a',因为它的原型包含对String.prototype.toUpperCase的引用,我们可以用a.toUpperCase交换它.它是上述3种解决方案和这些字节保存措施的组合,这就是我们如何获得此帖子顶部的代码.

  • 您保存了8个字符,同时模糊了代码,以至于需要对更明显的解决方案进行解释.我不相信这是一场胜利. (4认同)

Ber*_*ner 10

我们为什么不查一下定义呢?

如果我们写:

a.replace(/(f)/, x => x.toUpperCase())
Run Code Online (Sandbox Code Playgroud)

我们不妨说:

a.replace('f','F')
Run Code Online (Sandbox Code Playgroud)

更糟糕的是,我怀疑没有人意识到他们的例子之所以有效,只是因为他们用括号捕获了整个正则表达式。如果查看定义,传递给replacer函数的第一个参数实际上是整个匹配的模式,而不是您用括号捕获的模式:

function replacer(match, p1, p2, p3, offset, string)
Run Code Online (Sandbox Code Playgroud)

如果要使用箭头函数表示法:

a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()
Run Code Online (Sandbox Code Playgroud)


Cal*_*aNN 6

旧的帖子,但值得扩展@ChaosPandion答案的其他用例与更受限制的RegEx.例如(f),使用特定格式确保或捕获组环绕/z(f)oo/:

> a="foobazfoobar"
'foobazfoobar'
> a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());})
'foobazFoobar'
// Improve the RegEx so `(f)` will only get replaced when it begins with a dot or new line, etc.
Run Code Online (Sandbox Code Playgroud)

我只想强调function查找特定格式并在格式内替换捕获组的两个参数.