如果我要问你,JavaScript 的 replace 函数你会使用吗?任何一位前端肯定会说,我会啊,这么简单的函数,不就是个替换文本的操作码?是的,我也是这么想的。往往有些东西不尝试,我们总是从第一印象来考虑。就如同大家都认为前端非常简单,但是真正可以说深入了解前端技术的人非常少。很多知识点浅尝辄止,并没有深入研究,久而久之就导致了基本功夫不扎实。

是的,上面说到的这个人就是我自己:-(。前端的知识点相对来说比较多,有些东西我们在开发的时候不一定都会用到。以 replace 为例:很多时候我们只是写一个正则表达式去某个文本当中的对应内容。但是没有衍生到 replace 方法的其他方面。最近我在学习 doT.js 这个移动端字符串模板库的时候,发现这个库使用 replace 的方法非常的巧妙。这时才发现自己连 replace 的众多方法都不会使用。羞愧难当,所以今天重新学习下 JavaScript replace 方法。

replace 是 String 构造函数原型上的挂载的一个方法 String.prototype.replace。这个方法接受两个参数:第一个是匹配值可以是字符串,也可以是正则表达式。第二种是替换值:可以是字符串,在第一个参数为正则表达式的情况下,可以是一个函数。本文默认第一个参数是正则表达式,以下内容都是分析第二个参数的。

一、使用字符串作为参数

如果 replace 的第二个参数是字符串的话,那么可以在字符串当中使用变量。在 replace 方法中字串常常被提到, 字串是指一个正则表达式当中的括号部分,比如:/\w+(\d+).?(\d)/ 当中 (\d+) 就是第一个字串, (\d) 就是第二个字串。(注:点击代码区域可直接运行。

变量名代表的值
$$插入一个"$"
$&插入匹配的字串
$`插入当前匹配的字串左边的内容
$'插入当前匹配的字串右边的内容
$n or $nn假如第一个参数是 RegExp 对象,并且 n 或者 nn 是十进制的数字,那么插入第 n 个括号所匹配的字符串。
  • 1、变量名称是 $$: 替换成一个 "$".

const string4 = 'abc123abcd';  
console.log( string4.replace(/(\d+)/g, "$'") ); //abcabcabcd  

这里的 $$ 其实起到了转义的作用,代表了将字符串全部替换为 $。

  • 2、变量名称是 $&: 替换成匹配到的子串.

const string5 = 'abc123abcd1234';  
console.log( string5.replace(/[a-z]+(\d+)[a-z]+(\d+)/g, "$1") ); // 123  
console.log( string5.replace(/[a-z]+(\d+)[a-z]+(\d+)/g, "$2") ); // 1234  

  • 3、变量名称是 $`: 替换成当前匹配子串左边的内容.
const string3 = 'abc123abc';  
console.log( string3.replace(/(\d+)/g, '$`') ); //abcabcabc  

字串 (\d+) 匹配到的是 123,所以数字 123 被其左边的内容 abc 所替换。

  • 4、变量名称是 $': 替换成当前匹配子串右边的内容.
const string4 = 'abc123abcd';  
console.log( string4.replace(/(\d+)/g, "$'") ); //abcabcabcd  

字串 (\d+) 匹配到的是 123,所以数字 123 被其左边的内容 abcd 所替换。

  • 5、变量名称是 $n or $nn: 替换成第 n 个子表达式的子串.
const string5 = 'abc123abcd1234';  
console.log( string5.replace(/[a-z]+(\d+)[a-z]+(\d+)/g, "$1") ); // 123  
console.log( string5.replace(/[a-z]+(\d+)[a-z]+(\d+)/g, "$2") ); // 1234  

$1、$2 分别代表了第一个字串与第二个字串所匹配到的内容。

二、指定一个函数作为参数

指定函数作为参数, 那么这个函数的参数不是固定的。这取决于第一个参数是否是正则表达式, 或者这个正则表达式有几个子表达式, 也就是有几个括号。参数一般为:

参数名对应值
match匹配到的字串
p1第一个字串所匹配到的字符
......
pn第 n 个字串所匹配到的字符
offset正则表达的字符在整个字符串当中的偏移量
string直接输出原字符串
  • 参数数量与字串数量相符
const string6 = 'abc123abc1234';  
string6.replace(/[a-z]+(\d+)[a-z]+(\d+)/g, function(match, p1, p2, offset, string){  
    // {match: "abc123abc1234", p1: "123", p2: "1234", offset: 0, string: "abc123abc1234"}
    // match: 第一个参数是匹配的字串
    // p1: 第一个括号匹配的字符
    // p2: 第二个括号匹配的字符
    // offset: 匹配到的字串在原字符中的偏移量
    // string: 直接输出原字符串
    console.log({match:match, p1:p1, p2:p2, offset:offset, string:string}); 
});
  • 缺少参数的情况
const string7 = 'abc123abc1234acb1221';  
string7.replace(/[a-z]+(\d+)[a-z]+(\d+)[a-z]+(\d+)/g, function(match, p1, p2, offset, string){  
    // {match: "abc123abc1234acb1221", p1: "123", p2: "1234", offset: "1221", string: 0}
    // 无论参数有多少个, 第一个为匹配的字符串.
    // 接下来的参数就是按照子表达式的个数来排.
    // 倒数第二个是偏移量
    // 最后一个是原字符串
    console.log({match:match, p1:p1, p2:p2, offset:offset, string:string}); 
});
  • match 与 string 不相同的情况
const string8 = '+abc123abc1234';  
string8.replace(/[a-z]+(\d+)[a-z]+(\d+)/g, function(match, p1, p2, offset, string){  
    console.log({match:match, p1:p1, p2:p2, offset:offset, string:string}); 
});

三、一些有意思的栗子

  • 将 a 与 b 的值替换
const string9 = '张三&李四';  
console.log( string9.replace(/([\u4e00-\u9fa5]).([\u4e00-\u9fa5])/g, '$2爱$1') ); // 张李爱三四  
  • 提取 transform 值
const string10 = "translate3d(-150px, 0px, 0px)";  
console.log( string10.replace(/translate3d\((\-?\d+)px,\s*(\d+)px,\s*(\d+)px\)/, "$1") ); // -150  
  • 字符串转义
const string11 = "呵呵'呵呵\a";  
console.log( string11.replace(/'|\\/g, "\\$&") ); // 呵呵\'呵呵\\a  
  • 匹配非法字符
const string12 = '测试测试测试骂人的测试测试测试测试骂人测试测试的测试测试';  
console.log(string12.replace(/(\u9A82\u4EBA\u7684)|(\u9A82\u4EBA)/g, function(match, p1, p2, offset, string){  
  return new Array( match.length + 1 ).join('*');
})); // 测试测试测试***测试测试测试测试**测试测试的测试测试

四、结束语

可见我的基础功夫还是不够扎实,看别人写的代码有种分分钟被教育的感觉。还是希望在做项目的同时,踏踏实实的打好基础。慢慢积累,多看看别人写的代码。不要再这么菜了。

(完)。