Cukr pro výměnu několika strun

Kolikrát jste potřebovali spustit více operací nahrazení na stejném řetězci? Není to tak špatné, ale může to být trochu únavné, pokud takto často píšete kód.

str = str.
	replace( /&(?!#?\w+;)/g , '&'    ).
	replace( /"([^"]*)"/g   , '“$1”'     ).
	replace( /</g           , '&lt;'     ).
	replace( />/g           , '&gt;'     ).
	replace( /…/g           , '&hellip;' ).
	replace( /“/g           , '&ldquo;'  ).
	replace( /”/g           , '&rdquo;'  ).
	replace( /‘/g           , '&lsquo;'  ).
	replace( /’/g           , '&rsquo;'  ).
	replace( /—/g           , '&mdash;'  ).
	replace( /–/g           , '&ndash;'  );

Běžným trikem, jak zkrátit takový kód, je vyhledat náhradní hodnoty pomocí objektu jako hashovací tabulky. Zde je jednoduchá implementace tohoto.

var hash = {
	'<' : '&lt;'    ,
	'>' : '&gt;'    ,
	'…' : '&hellip;',
	'“' : '&ldquo;' ,
	'”' : '&rdquo;' ,
	'‘' : '&lsquo;' ,
	'’' : '&rsquo;' ,
	'—' : '&mdash;' ,
	'–' : '&ndash;'
};

str = str.
	replace( /&(?!#?\w+;)/g , '&amp;' ).
	replace( /"([^"]*)"/g   , '“$1”'  ).
	replace( /[<>…“”‘’—–]/g , function ( $0 ) {
		return hash[ $0 ];
	});

Tento přístup má však určitá omezení.

  • Vzory vyhledávání se opakují v tabulce hash a ve třídě znaků regulárního výrazu.
  • Vyhledávání i nahrazování jsou omezeny na prostý text. Proto musely první a druhé nahrazení zůstat ve výše uvedeném kódu oddělené. První nahrazení použilo vyhledávací vzor regulárního výrazu a druhé použilo zpětný odkaz v nahrazujícím textu.
  • Náhrady neprobíhají kaskádovitě. I proto musela druhá operace výměny zůstat oddělená. Chci text jako "this" nejprve nahradit “this” a nakonec skončí jako &ldquo;this&rdquo; .
  • Nefunguje v Safari 2.xa dalších starých prohlížečích, které nepodporují použití funkcí pro generování náhradního textu.

S několika řádky String.prototype cukr, se všemi těmito problémy se můžete vypořádat.

String.prototype.multiReplace = function ( hash ) {
	var str = this, key;
	for ( key in hash ) {
		str = str.replace( new RegExp( key, 'g' ), hash[ key ] );
	}
	return str;
};

Nyní můžete použít kód takto:

str = str.multiReplace({
	'&(?!#?\\w+;)' : '&amp;'   ,
	'"([^"]*)"'    : '“$1”'    ,
	'<'            : '&lt;'    ,
	'>'            : '&gt;'    ,
	'…'            : '&hellip;',
	'“'            : '&ldquo;' ,
	'”'            : '&rdquo;' ,
	'‘'            : '&lsquo;' ,
	'’'            : '&rsquo;' ,
	'—'            : '&mdash;' ,
	'–'            : '&ndash;'
});

Pokud vám záleží na pořadí nahrazování, měli byste si být vědomi toho, že aktuální specifikace JavaScriptu nevyžaduje zvláštní pořadí výčtu při opakování vlastností objektu pomocí for..in . Nejnovější verze velké čtyřky prohlížečů (IE, Firefox, Safari, Opera) však všechny používají pořadí vložení, což umožňuje, aby to fungovalo tak, jak je popsáno (shora dolů). Návrhy ECMAScript 4 naznačují, že konvence pořadí vložení bude v tomto standardu formálně kodifikována.

Pokud se potřebujete starat o nepoctivé vlastnosti, které se objeví, když si lidé zahrají s Object.prototype, můžete kód aktualizovat následovně:

String.prototype.multiReplace = function ( hash ) {
	var str = this, key;
	for ( key in hash ) {
		if ( Object.prototype.hasOwnProperty.call( hash, key ) ) {
			str = str.replace( new RegExp( key, 'g' ), hash[ key ] );
		}
	}
	return str;
};

Volání hasOwnProperty metoda na Object.prototype spíše než na hash object přímo umožňuje, aby tato metoda fungovala, i když hledáte řetězec "hasOwnProperty".

Dejte mi vědět, jestli si myslíte, že je to užitečné.