ES6 推出了模板字符串的语法,用起来非常方便,本节我们就来尝试实现一下模板字符串的部分功能。
当然,真实的模板字符串是使用反引号 (``) 括起来的,本题不需要实现这个功能,只需要实现变量替换的功能即可,让我们来对这个功能的实现一探究竟吧。
在讲解之前,先给出本题的答案,代码如下:
function strRender(str, data) {
const re = /\$\{(\w+)\}/;
if (re.test(str)) {
const key = str.match(re)[1];
str = str.replace(re, data[key]);
return strRender(str, data);
}
return str;
}
或者
function strRender(str, data) {
const re = /\$\{(\w+)\}/g;
return str.replace(re, (match, key) => data[key]);
}
有了上一章正则表达式的基础,本题实现起来就非常简单了,只需要匹配字符串中被 ${}
包裹的内容,也就是字符串中需要被填充的变量,然后把它替换成实际的数据即可,正则匹配流程图如下:
https://doc.shiyanlou.com/courses/9328/2086340/7bb0b2ff5b3c413af7a2303ebbb18bed-0
可以看到,首先匹配的是 ${
,然后匹配任意单字字符重复 1 次或多次的情况,最后匹配 }
,这样就拿到了字符串中诸如 ${name}
形式的内容。
注意上图中被虚线框起来的 Group #1
部分,这里用到了捕获括号的语法,这样我们就可以拿到变量的 key
值,也就是 ${xxx}
中的 xxx
。
拿到了变量的 key
值之后,就能取到需要被替换的数据了,就可以用 replace
方法把 ${xxx}
替换成真正的数据。
最后实现代码如下:
function strRender(str, data) {
const re = /\$\{(\w+)\}/;
// 如果字符串中存在 ${xxx},说明还有变量没有被替换,才执行后面的逻辑
if (re.test(str)) {
// 使用 match 方法获取 key 值
const key = str.match(re)[1];
// 使用 replace 方法替换变量为真实的数据
str = str.replace(re, data[key]);
// 注意这里的逻辑,递归调用自身,直到字符串中不存在 ${xxx}
return strRender(str, data);
}
return str;
}
上面的代码中,我们使用了递归的逻辑,因为每次匹配只匹配到了第一个值就终止了,所以要递归调用自身,直到字符串中不存在 ${xxx}
,才算结束。
其实,只需要在正则中加入全局搜索修饰符,即可轻松优化代码,代码如下所示:
function strRender(str, data) {
// 正则中加入全局搜索修饰符,也就是在尾部加一个 g
const re = /\$\{(\w+)\}/g;
return str.replace(re, (match, key) => data[key]);
}
这里 replace
函数的替换值就不再是一个字符串了,而是一个回调函数,这个回调函数每次匹配都会调用,每次调用时会把匹配到了的模板变量替换为真实的数据,这样就不需要递归调用了。用到的原理就是 replace
函数可以指定一个函数作为参数,如果对具体的语法不太明白,可以参考 MDN
本节我们通过正则轻松实现了模板字符串解析的部分功能,本节的重点是:
replace
方法中的回调函数。replace
这个方法的用途非常灵活,希望做完本题后,同学们能对 replace
方法的理解更进一步。
进入下一章节