Odd behavior replacing string with special replace

2019-02-14 14:44发布

问题:

I've spotted a very strange issue in IE7|8 when using special replacement patterns:

'moo$$e'.replace( /\$/g, '$$$$' );
'moo$$e'.replace( '\$', '$$$$', 'g' );   

Latest Chrome:

 moo$$$$e moo$$$e

Latest Firefox:

 moo$$$$e moo$$$$e

IE7|8:

 moo$$$$e moo$$$$$e

I know that flags parameter is nothing like a standard, hence the difference between Firefox and Chrome in the second case. I'm cool with it.

However, what I see in IE7|8 is really odd (still second case). I tried playing with '\x24', escaping and stuff but I cannot find any way to have this working as expected ($$ stands for $).

I know that this could be easily done with split() and join() like:

'moo$$e'.split( '$' ).join( '$$' );
> "moo$$$$e"

but I'm really, really curious what's wrong with IE. Is there any explanation?

See the live example.

回答1:

Test case

I revisited the test case with results to present it as follows:

var results = [
  'YY'.replace( /Y/g, '$$' ),
  'YY'.replace( 'Y', '$$', 'g' ),
  'YY'.replace( 'Y', function( a, b ) { return '$$'; }, 'g' ),
  'YY'.replace( /Y/g, function( a, b ) { return '$$'; })  
];

console.log( results.join( '\n' ) );

Results

Chrome

$$    // '$$' -> '$', global flag used, every 'Y' -> '$'
$Y    // '$$' -> '$', global flag ignored, first 'Y' -> '$'
$$Y   // '$$' -> '$$', global flag ignored, first 'Y' -> '$$'
$$$$  // '$$' -> '$$', global flag used, every 'Y' -> '$$'

Firefox

$$    // '$$' -> '$', global flag used, every 'Y' -> '$'
$$    // '$$' -> '$', global flag used, every 'Y' -> '$'
$$$$  // '$$' -> '$$', global flag used, every 'Y' -> '$$'
$$$$  // '$$' -> '$$', global flag used, every 'Y' -> '$$'

IE7 and 8

$$    // '$$' -> '$', global flag used, every 'Y' -> '$'
$$Y   // '$$' -> '$$', global flag ignored, first 'Y' -> '$$'
$$Y   // '$$' -> '$$', global flag ignored, first 'Y' -> '$$'
$$$$  // '$$' -> '$$', global flag used, every 'Y' -> '$$'

Conclusions

  1. Chrome ignores 'g' flag as a third parameter of String.replace since flags used this way don't belong to any standard.

  2. IE assumes $$ to be string rather that replacement control and ignores global flag in this case:

    'YY'.replace( 'Y', '$$', 'g' );

  3. The simplest solution make sure that results are always the same is to use a RegExp object with flags (/foo/flags) as a first parameter and either string or function as a second parameter.

    If string is a second parameter, $$ converts to $. If this is a function-driven replacement, there's no such conversion.