最近在做JS算法項目時發現一個令我匪夷所思的問題, 這里想記錄一下問題。
首先介紹一下字符串replace()方法的基本用法。
replace() 方法使用一個替換值(replacement)替換掉一個匹配模式(pattern)在原字符串中某些或所有的匹配項,並返回替換后的字符串。這個替換模式可以是字符串或者RegExp
(正則表達式),替換值可以是一個字符串或者一個函數。
語法
str.replace(regexp|substr, newSubStr|function[, flags])
參數
-
regexp
-
一個
RegExp
對象。該正則所匹配的內容會被第二個參數的返回值替換掉。
-
substr
-
一個要被
newSubStr
替換的字符串。
-
newSubStr
- 替換掉第一個參數在原字符串中的匹配部分。該字符串中可以內插一些特殊的變量名。
function
一個用來創建新子字符串的函數,該函數的返回值將替換掉第一個參數匹配到的結果。該函數的參數描述請參考 指定一個函數作為參數 小節。
返回值
一個部分或全部匹配由替代模式所取代的新的字符串。
關於這個方法具體的信息參考MDN再好不過了。
String.prototype.replace() - JavaScript | MDN
現在有一個非常簡單的需求:將HTML代碼中的特殊字符進行實體轉義:
先看一個簡單的輸出沒有錯誤的版本:
正確方法一:
12345678910111213141516171819function
convertHTML(str) {
// :)
var
pattern=/[&<>
"']/g;
return str.replace(pattern,function(match){
switch(match){
case "
<
":
return "
<
";
case "
>
":
return "
>
";
case "
&
":
return "
&
";
case "
\
""
:
return
"""
;
case
"\'"
:
return
"'"
;
}
});
}
console.log(convertHTML(
"<Tom & Jerry>"
));//<Tom & Jerry>
指定一個函數作為第二個參數。在這種情況下,當匹配執行后, 該函數就會執行。 函數的返回值作為替換字符串。 (注意: 上面提到的特殊替換參數在這里不能被使用。) 另外要注意的是, 如果第一個參數是正則表達式, 並且其為全局匹配模式, 那么這個方法將被多次調用, 每次匹配都會被調用。
后來我對上述的代碼進行了改造,使代碼看起來更明了:
正確方法二:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function
convertHTML(str) {
// :)
var
pattern=/[&<>
"']/g;
rules={"
&
":"
&
","
<
":"
<
","
>
":"
>
",'"
':""","'
":
"'"
};
return
str.replace(pattern,
function
(match){
return
rules[match];
});
}
console.log(convertHTML(
"<Tom & Jerry>"
));//<Tom & Jerry>
|
很明顯非常簡單的一個改造,我將規則鍵值對放在了rules對象中,輸出結果同樣也是正確的。
接下來,我發現貌似使用RegExp構造函數屬性還能使代碼變得更加簡單。RegExp構造函數屬性如下表所示:
其中一個叫做lastMatch的屬性深深吸引了我。我在想能不能像下面這樣改造一下:
錯誤方法:
1
2
3
4
5
6
|
function
convertHTML(str) {
var
pattern=/[&<>
"']/g;
rules={"
&
":"
&
","
<
":"
<
","
>
":"
>
",'"
':""","'
":
"'"
};
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) {
// :)
var
pattern=/[&<>
"']/g;
rules={"
&
":"
&
","
<
":"
<
","
>
":"
>
",'"
':""","'
":
"'"
};
//下面是測試代碼
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) {
// :)
var
pattern=/[&<>
"']/g;
rules={"
&
":"
&
","
<
":"
<
","
>
":"
>
",'"
':""","'
":
"'"
};
var
result=str.replace(pattern,
function
(){
return
rules[RegExp.lastMatch]
});
return
result;
}
console.log(convertHTML(
"<Tom & Jerry>"
));
//<Tom & Jerry>
|
注意上面我還是使用了正則表達式函數屬性與方法二是有區別的,但是結果卻是離奇的正確了。(難道閉包作怪)
replace()究竟隱藏着什么秘密,還有待挖掘??