Chciałbym wiedzieć, jak zamienić grupę przechwytywania na wielkie litery w JavaScript. Oto uproszczona wersja tego, co próbowałem do tej pory, ale nie działa:
> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'
Czy możesz wyjaśnić, co jest nie tak z tym kodem?
javascript
regex
replace
uppercase
Evan Carroll
źródło
źródło
Odpowiedzi:
Możesz przekazać funkcję
replace
.var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });
Wyjaśnienie
a.replace( /(f)/, "$1".toUpperCase())
W tym przykładzie przekazujesz ciąg do funkcji replace. Ponieważ używasz specjalnej składni zamiany ($ N przechwytuje N-ty przechwycenie) , po prostu podajesz tę samą wartość.
toUpperCase
Jest rzeczywiście mylące, ponieważ jesteś tylko dzięki czemu zastąpić ciąg dużych liter (co jest trochę bez sensu, bo$
i jedno1
znaków nie ma dużych liter więc wartość zwracana będzie nadal"$1"
) .a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))
Wierz lub nie, semantyka tego wyrażenia jest dokładnie taka sama.
źródło
$1
pierwszej grupy przechwytywania.Wiem, że spóźniłem się na imprezę, ale tutaj jest krótsza metoda, która jest bardziej podobna do twoich początkowych prób.
a.replace('f', String.call.bind(a.toUpperCase));
Więc gdzie popełniłeś błąd i co to za nowe voodoo?
Zadanie 1
Jak wspomniano wcześniej, próbujesz przekazać wyniki wywoływanej metody jako drugi parametr metody String.prototype.replace () , podczas gdy zamiast tego powinieneś przekazać referencję do funkcji
Rozwiązanie 1
To dość łatwe do rozwiązania. Po prostu usunięcie parametrów i nawiasów da nam odniesienie zamiast wykonywania funkcji.
a.replace('f', String.prototype.toUpperCase.apply)
Problem 2
Jeśli spróbujesz teraz uruchomić kod, pojawi się błąd informujący, że undefined nie jest funkcją i dlatego nie można go wywołać. Dzieje się tak, ponieważ String.prototype.toUpperCase.apply jest w rzeczywistości odwołaniem do funkcji Function.prototype.apply () poprzez prototypowe dziedziczenie JavaScript. Więc to, co faktycznie robimy, wygląda bardziej tak
a.replace('f', Function.prototype.apply)
Co oczywiście nie jest tym, czego chcieliśmy. Skąd wie, że należy uruchomić Function.prototype.apply () na String.prototype.toUpperCase () ?
Rozwiązanie 2
Używając funkcji Function.prototype.bind () możemy utworzyć kopię Function.prototype.call z jej kontekstem ustawionym specjalnie na String.prototype.toUpperCase. Mamy teraz następujące
a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))
Problem 3
Ostatnią kwestią jest to, że String.prototype.replace () przekaże kilka argumentów do swojej funkcji zastępującej. Jednak funkcja Function.prototype.apply () oczekuje, że drugi parametr będzie tablicą, ale zamiast tego pobiera ciąg lub liczbę (w zależności od tego, czy używasz grup przechwytywania, czy nie). Spowodowałoby to nieprawidłowy błąd listy argumentów.
Rozwiązanie 3
Na szczęście możemy po prostu podstawić w Function.prototype.call () (który akceptuje dowolną liczbę argumentów, z których żaden nie ma ograniczeń typu) dla Function.prototype.apply () . Doszliśmy do działającego kodu!
a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))
Utrata bajtów!
Nikt nie chce pisać prototypu kilka razy. Zamiast tego wykorzystamy fakt, że mamy obiekty, które odwołują się do tych samych metod poprzez dziedziczenie. Konstruktor String, będący funkcją, dziedziczy po prototypie Function. Oznacza to, że w String.call możemy zastąpić Function.prototype.call (w rzeczywistości możemy użyć Date.call, aby zapisać jeszcze więcej bajtów, ale to mniej semantyczne).
Możemy również wykorzystać naszą zmienną „a”, ponieważ jej prototyp zawiera odniesienie do String.prototype.toUpperCase, które możemy zamienić na a.toUpperCase. Jest to połączenie powyższych 3 rozwiązań i tych środków oszczędzania bajtów, dzięki czemu otrzymujemy kod na początku tego postu.
źródło
Stary post, ale warto rozszerzyć odpowiedź @ChaosPandion na inne przypadki użycia z bardziej ograniczonym wyrażeniem regularnym. Np. Upewnij się, że
(f)
lub przechwytuj grupę surround w określonym formacie/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.
Chcę tylko podkreślić dwa parametry
function
umożliwiające znalezienie określonego formatu i zastąpienie grupy przechwytywania w tym formacie.źródło
return $0.replace($0, $1.toUpperCase())
, gdzie$0
jest pierwszy argumentDlaczego po prostu nie sprawdzimy definicji ?
Jeśli napiszemy:
a.replace(/(f)/, x => x.toUpperCase())
równie dobrze możemy po prostu powiedzieć:
a.replace('f','F')
Co gorsza, podejrzewam, że nikt nie zdaje sobie sprawy, że ich przykłady działają tylko dlatego, że przechwytują całe wyrażenie regularne w nawiasach. Jeśli spojrzysz na definicję , pierwszym parametrem przekazanym do
replacer
funkcji jest w rzeczywistości cały dopasowany wzorzec, a nie wzorzec, który przechwyciłeś w nawiasach:function replacer(match, p1, p2, p3, offset, string)
Jeśli chcesz użyć notacji funkcji strzałek:
a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()
źródło
ROZWIĄZANIE
a.replace(/(f)/,x=>x.toUpperCase())
aby zamienić wszystkie wystąpienia grup, użyj
/(f)/g
wyrażenia regularnego. Problem w twoim kodzie:String.prototype.toUpperCase.apply("$1")
i"$1".toUpperCase()
daje"$1"
(spróbuj sam w konsoli) - więc nic nie zmienia i faktycznie dzwonisz dwa razya.replace( /(f)/, "$1")
(co też nic nie zmienia).Pokaż fragment kodu
let a= "foobar"; let b= a.replace(/(f)/,x=>x.toUpperCase()); let c= a.replace(/(o)/g,x=>x.toUpperCase()); console.log("/(f)/ ", b); console.log("/(o)/g", c);
źródło
Biorąc pod uwagę słownik (obiekt, w tym przypadku a
Map
) właściwości, wartości i użycie,.bind()
jak opisano w odpowiedziachconst regex = /([A-z0-9]+)/; const dictionary = new Map([["hello", 123]]); let str = "hello"; str = str.replace(regex, dictionary.get.bind(dictionary)); console.log(str);
Używanie zwykłego obiektu JavaScript i funkcji zdefiniowanej w celu zwrócenia dopasowanej wartości właściwości obiektu lub oryginalnego ciągu, jeśli nie zostanie znalezione dopasowanie
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);
źródło