Подтвердить что ты не робот

Замените группу захвата Regex в верхнем регистре в Javascript

Я хотел бы знать, как заменить группу захвата на верхний регистр в JavaScript. Вот упрощенная версия того, что я пробовал до сих пор, что не работает:

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

Не могли бы вы объяснить, что не так с этим кодом?

4b9b3361

Ответ 1

Вы можете передать функцию replace.

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

Объяснение

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

В этом примере вы передаете строку функции replace. Поскольку вы используете специальный синтаксис замены ($ N захватывает N-й захват), вы просто даете одинаковое значение. toUpperCase на самом деле обманчив, потому что вы делаете только строчный верхний регистр строки (что несколько бессмысленно, потому что символы $ и один 1 не имеют верхнего регистра, поэтому возвращаемое значение будет "$1").

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

Верьте или нет, семантика этого выражения точно такая же.

Ответ 2

Я знаю, что опаздываю на вечеринку, но вот более короткий метод, который больше соответствует вашим первоначальным попыткам.

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 на самом деле является ссылкой на Function.prototype.apply() через прототипическое наследование JavaScript. То, что мы на самом деле делаем, больше похоже на это.

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

Это, очевидно, не то, что мы намеревались. Как это известно для запуска Function.prototype.apply() в String.prototype.toUpperCase()?

Решение 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-х решений выше и эти меры сохранения байтов, как мы получаем код в верхней части этого сообщения.

Ответ 3

Старый пост, но стоит увеличить @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 чтобы найти конкретный формат и заменить группу захвата в пределах возможного формата.

Ответ 4

РЕШЕНИЕ (для замены всех случаев разбора используйте /(f)/g regexp)

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

Проблема в коде: String.prototype.toUpperCase.apply("$1") и "$1".toUpperCase() дает "$1" (попробуйте в консоли самостоятельно) - так что это не меняет ничего, и на самом деле вы звоните дважды a.replace(/(f)/, "$1") (что тоже ничего не меняет).