字符串的双重转义

缘起

习惯了SPA的开发模式, 现在随便弄点什么东西, 渲染HTML什么的也总是通过JS来了, 但是在JS里写HTML总归是影响代码美观的, 虽然有现成的模板工具可以使用, 但是这人一旦任性起来是真的拉都拉不住。 某日闲得飞起, 就准备写这么一个小东西。

思路是这样的, 在一个目录下专门存放html, 然后通过Nodejs来读取html, 之后根据文件名和文件内容生成一个对象,如下所示

1
2
3
4
var obj = {
test: '<div>div1</div>',
test2: '<div>div2</div>'
};

然后使用JSON.stringify()将这个对象转化为字符串, 存储为一个JS文件, 当然, 同时还会声明一个变量啊啥的, 大概就下面这样:

1
2
// 假设得到的字符串是`{"test":"<div>div1</div>"}`  那么这个存储字符串的文件内容大概就是这样
const template = JSON.parse('{"test":"<div>div1</div>"}');

之后在html里面引入这个JS, 然后就可以通过template.test来访问对应的html内的东西了。

样子大概就是这个样子。然而人生不如意之事十之八九, 这句话放在码代码身上也不例外。也许还得改一改, 人生不如意之事100%!!!

上面的html还是比较简单的, 然而谁会像我一样闲的蛋疼专门建一个html文件就为了放一个div.

稍微来个复杂的, 假如一个叫test2html内容如下:

1
2
3
4
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>

然后最后生成的文件内容是这样的:

1
const template = JSON.parse('{"test":"<div class=\"container\">\r\n  <div class=\"left\"></div>\r\n  <div class=\"right\"></div>\r\n</div>\r\n"}');

耶, 真棒, 看起来好像没什么问题, 人家都还给转义了, 然后放到浏览器里一跑

看看错误详情

在错误详情里面可以明显地看到被引号引起来的container和其他周围的字符串颜色并不一样, 但是明明字符串里就加了转义字符的呀。

症结

先说说转义字符的意思吧(我瞎蒙的, 说错了别打我)。

比如"\""放到浏览器里运行的话, 得到的结果是"""。这里的斜杠就对中间的那个双引号进行了转义, 告诉浏览器这个东西表示的只是一个引号,你别解析错了, 因此得到的就是三个引号。所以在这里, 当浏览器把一个字面量转变为字符串的时候, 转义了一次, 吞了一次转义字符。

如下图所示, 可以看到字符串字面量里面本来是有斜线的, 但是赋值给变量之后那斜线就被吃了。

然后在进行JSON.parse()的时候, 无奈地碰到了双引号, 然后就悲剧地报错了。

所以在对字符串进行转换, 转变为一个对象的时候依然需要进行转义, 前面提到过, 使用\进行转义, 但是\除了表示字符\之外, 也表示转义字符, 所以这里还需要对’'进行转义, 也就是进行双转义。

举个例子, 比如判断某个元素具不具有某个类名

1
2
3
function hasClass (sourceCls, cls) {
return new RegExp('\\b' + cls + '\\b', 'g').test(sourceCls);
}

这里就是先对\进行了转义,因此出现了两个\

所以简单的来说。。。双转义就是'\' * 2, 原本应该是一个的就上俩, 是两个的就上四个。

然后负责读取文件生成数据的JS就成了这样

1
2
3
4
5
6
7
8
9
10
11
12
const fs = require('fs');
const obj = {};
fs.readFile('./test.html', (_, data) => {
obj.test = data.toString();
fs.writeFile('./testJSON.js', 'const template = JSON.parse(\'' + getStr(obj) + '\');');
});

function getStr (data) {
// 通过JSON.stringify序列化后的加了转义符号的字符串大概就是\"这个样子
// 所以这里的两根斜杠前者只是对后者的转义而已
return JSON.stringify(data).replace(/\\/g, '\\\\');
}

当然, 上面那种方式比较简单粗暴, 只是为了说明一下怎么解决这个问题而已。

总结

就我自己的理解来的话, 需要双转义的地方一般是进行了两次解析的, 字面量 => 字符串 => .., 这里的..可能是正则表达式, 也可能是其他的啥啥啥。。。

最后, 感谢aristotll对我问题的回答。