字符串的replace()方法隐藏着什么不可告人秘密?


最近在做JS算法项目时发现一个令我匪夷所思的问题, 这里想记录一下问题。

首先介绍一下字符串replace()方法的基本用法。

replace() 方法使用一个替换值(replacement)替换掉一个匹配模式(pattern)在原字符串中某些或所有的匹配项,并返回替换后的字符串。这个替换模式可以是字符串或者RegExp(正则表达式),替换值可以是一个字符串或者一个函数。

语法EDIT

str.replace(regexp|substrnewSubStr|function[, flags])

参数

regexp
一个  RegExp 对象。该正则所匹配的内容会被第二个参数的返回值替换掉。
substr
一个要被  newSubStr  替换的字符串。
newSubStr
替换掉第一个参数在原字符串中的匹配部分。该字符串中可以内插一些特殊的变量名。
function

一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。该函数的参数描述请参考 指定一个函数作为参数 小节。

返回值

一个部分或全部匹配由替代模式所取代的新的字符串。

关于这个方法具体的信息参考MDN再好不过了。

String.prototype.replace() - JavaScript | MDN

 

现在有一个非常简单的需求:将HTML代码中的特殊字符进行实体转义:

先看一个简单的输出没有错误的版本:

正确方法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function convertHTML(str) {
   // :)
   var pattern=/[&<> "']/g;
   return str.replace(pattern,function(match){
     switch(match){
         case " < ":
         return " &lt; ";
         case " > ":
         return " &gt; ";
         case " & ":
         return " &amp; ";
         case " \ "" :
         return "&quot;" ;
         case "\'" :
         return "&apos;" ;
     }
   });
}
console.log(convertHTML( "<Tom & Jerry>" ));//&lt;Tom &amp; Jerry&gt;

指定一个函数作为第二个参数。在这种情况下,当匹配执行后, 该函数就会执行。 函数的返回值作为替换字符串。 (注意:  上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是, 如果第一个参数是正则表达式, 并且其为全局匹配模式, 那么这个方法将被多次调用, 每次匹配都会被调用。

后来我对上述的代码进行了改造,使代码看起来更明了:

正确方法二:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
function convertHTML(str) {
   // &colon;&rpar;
   var pattern=/[&<> "']/g;
   rules={" & ":" &amp; "," < ":" &lt; "," > ":" &gt; ",'" ':"&quot;","' ": "&apos;" };
  return str.replace(pattern, function (match){
     return  rules[match];
   });
 
 
}
console.log(convertHTML( "<Tom & Jerry>" ));//&lt;Tom &amp; Jerry&gt;

很明显非常简单的一个改造,我将规则键值对放在了rules对象中,输出结果同样也是正确的。

接下来,我发现貌似使用RegExp构造函数属性还能使代码变得更加简单。RegExp构造函数属性如下表所示:

其中一个叫做lastMatch的属性深深吸引了我。我在想能不能像下面这样改造一下:

错误方法:

1
2
3
4
5
6
function convertHTML(str) {
   var pattern=/[&<> "']/g;
   rules={" & ":" &amp; "," < ":" &lt; "," > ":" &gt; ",'" ':"&quot;","' ": "&apos;" };
   return str.replace(pattern,rules[RegExp.lastMatch]);
}
console.log(convertHTML( "<Tom & Jerry>" ));

写完这个代码,突然觉得自己聪明绝世,飘飘然了呢,然而一瓢冷水泼下来了。运行结果如下:

 

 
1
undefinedTom undefined Jerryundefined

这是什么鬼,貌似该替换的字符都被替换成了undefined。接下来我加入了一条测试语句:

 

 

 
1
2
3
4
5
6
7
8
9
function convertHTML(str) {
   // &colon;&rpar;
   var pattern=/[&<> "']/g;
   rules={" & ":" &amp; "," < ":" &lt; "," > ":" &gt; ",'" ':"&quot;","' ": "&apos;" };

  var result=str.replace(pattern,rules[RegExp.lastMatch]);

//下面是测试代码

   console.log(RegExp.lastMatch);//>
   return result;
}
console.log(convertHTML( "<Tom & Jerry>" ));

根据测试代码,最后匹配的是“>”说明匹配是正确的,但是最后为什么没有按照规则进行替换呢,我想应该是replace()方法的实现机制我们没有弄清楚,网上没有找到相关的资料,所以只能提醒自己以后不能写这样的代码了,在这种情况下,还是乖乖函数作为第二个参数。。。。。。等等,用函数做第二个参数,我又进行了下面的修改:

1
2
3
4
5
6
7
8
9
10
function convertHTML(str) {
   // &colon;&rpar;
   var pattern=/[&<> "']/g;
   rules={" & ":" &amp; "," < ":" &lt; "," > ":" &gt; ",'" ':"&quot;","' ": "&apos;" };
   var result=str.replace(pattern, function (){
     return rules[RegExp.lastMatch]
   });
   return result;
}
console.log(convertHTML( "<Tom & Jerry>" )); //&lt;Tom &amp; Jerry&gt;

注意上面我还是使用了正则表达式函数属性与方法二是有区别的,但是结果却是离奇的正确了。(难道闭包作怪)

 

 

replace()究竟隐藏着什么秘密,还有待挖掘??




免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM