96

我想知道如何在 JavaScript 中用大写字母替换捕获组。这是迄今为止我尝试过的但不起作用的简化版本:

> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'

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

4

7 回答 7

177

您可以将函数传递给replace.

var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });

解释

a.replace( /(f)/, "$1".toUpperCase())

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

a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))

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

于 2011-05-26T17:56:28.340 回答
17

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

a.replace('f', String.call.bind(a.toUpperCase));

那么你哪里出错了,这个新的巫术是什么?

问题 1

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

解决方案 1

这很容易解决。简单地删除参数和括号会给我们一个引用而不是执行函数。

a.replace('f', String.prototype.toUpperCase.apply)

问题 2

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

a.replace('f', Function.prototype.apply)

这显然不是我们的本意。它怎么知道在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))

问题 3

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

解决方案 3

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

a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))

脱落字节!

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

我们还可以利用我们的变量“a”,因为它的原型包含对String.prototype.toUpperCase的引用,我们可以将其替换为 a.toUpperCase。正是上述 3 种解决方案和这些字节节省措施的结合,我们才能获得本文顶部的代码。

于 2014-05-08T06:23:46.460 回答
14

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

如果我们写:

a.replace(/(f)/, x => x.toUpperCase())

我们不妨说:

a.replace('f','F')

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

function replacer(match, p1, p2, p3, offset, string)

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

a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()
于 2019-11-20T09:44:52.727 回答
12

旧帖子,但值得扩展 @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.

我只想强调两个参数,function使查找特定格式和替换格式中的捕获组成为可能。

于 2018-02-26T17:34:08.913 回答
2

解决方案

a.replace(/(f)/,(m,g)=>g.toUpperCase())  

替换所有出现的 grup 使用/(f)/g正则表达式。您的代码中的问题:String.prototype.toUpperCase.apply("$1")"$1".toUpperCase()给出"$1"(自己在控制台中尝试) - 所以它不会改变任何东西,实际上您调用了两次a.replace( /(f)/, "$1")(这也不会改变任何东西)。

let a= "foobar";
let b= a.replace(/(f)/,(m,g)=>g.toUpperCase());
let c= a.replace(/(o)/g,(m,g)=>g.toUpperCase());

console.log("/(f)/ ", b);
console.log("/(o)/g", c);

于 2018-12-01T13:49:44.623 回答
0

Map给定一个包含属性、值和使用的字典(对象,在本例中为 a ),.bind()如答案中所述

const regex = /([A-z0-9]+)/;
const dictionary = new Map([["hello", 123]]); 
let str = "hello";
str = str.replace(regex, dictionary.get.bind(dictionary));

console.log(str);

使用 JavaScript 普通对象并定义函数以获取对象的匹配属性值,如果未找到匹配项,则返回原始字符串

const regex = /([A-z0-9]+)/;
const dictionary = {
  "hello": 123,
  [Symbol("dictionary")](prop) {
    return this[prop] || prop
  }
};
let str = "hello";
str = str.replace(regex, dictionary[Object.getOwnPropertySymbols(dictionary)[0]].bind(dictionary));

console.log(str);

于 2019-02-23T23:38:36.517 回答
0

在从 CamelCase 到 bash_case 的字符串转换(即:对于文件名)的情况下,使用带有三元运算符的回调。

在第一个(左)替换 arg 中使用正则表达式 选择的捕获组()被发送到作为回调函数的第二个(右)arg 。 xy给出捕获的字符串(不知道为什么2次!)和index(第三个)给出了引用字符串中捕获组的开头的索引。因此,可以使用三元运算符不放置_在第一次出现。

let str = 'MyStringName';
str = str.replace(/([^a-z0-9])/g, (x,y,index) => {
      return index != 0 ? '_' + x.toLowerCase() : x.toLowerCase();
});
console.log(str);    

于 2022-01-03T06:24:32.737 回答