多年前我第一次入职腾讯的时候,DC 从杭州给我寄来了一本他刚翻译出炉的《高性能 JavaScript》。那段时间为了帮忙校对,我仔细阅读了书中的每一个段落,结果积累了不少 JavaScript 基础知识。现在还依稀记得书中提到的几个知识点: IE7 浏览器在大字符串处理时的极致性能优化;位运算符用于 config 配置的各种 trick;以及今天想聊的 RegExp 构造器的第一个参数设计问题。

上周接到一个需求,根据页面 url 来决定是否出现一个弹窗提示。为了方便管理这个特性,我将 url 列表配置在了后台,前端通过接口取得列表再进行校验。

其中有一条规则是「所有机构首页需要弹窗」,因为机构会有自己的独立二级域名,所以这里必须要用到location.host 对应的正则表达式 \w+\.ke\.qq\.com


new RegExp(/\w+\.ke\.qq\.com/).test('ktmaster.ke.qq.com') // 返回 true

// 由于正则表达式字符串是 cgi 接口中返回的,所以第一个参数只能用 string 类型
// 而 RegExp 构造器使用 string 参数时,其中的 \w、\ 等特殊含义字符是需要使用反斜杠再做一层转义,这样同时导致正则语义变得很不清晰
new RegExp('\w+\.ke\.qq\.com').test('ktmaster.ke.qq.com') // 返回 false
new RegExp('\\w+\\.ke\\.qq\\.com').test('ktmaster.ke.qq.com') // 返回 true

然而,需求真正落地实现后发现:RegExp 构造器 string 参数需要转义的知识点,其实基本用不到。

1、通过接口返回的字符串在变量赋值时无需转义

前端 AJAX 请求取到的接口数据一定是 string 类型的,这种未通过字符串字面量形式赋值给变量时是无需转义的。以 fetchAPI 为例:

// 1. 其中 data 接口返回的内容是 \w+\.ke\.qq\.com
fetch('/data')
.then(res => res.text())
.then(resText => {
console.log(new RegExp(resText)) // 正确实例化了 /\w+\.ke\.qq\.com/
}) // 2. 字面量形式定义的字符串不转义,会与期望不符
const regText = '\w+\.ke\.qq\.com' // 字符串定义时 \ 会与后面一个字符合并解析掉
console.log(regText === 'w+.ke.qq.com') // 返回 true
console.log(new RegExp(regText)) // 返回的是 /w+.ke.qq.com/

现在大部分的接口数据会使用 JSON string,接口返回后通过 JSON.parse 成 JavaScript Object ,再通过 key 来取值。而对于 JSON 数据来说,后端 JSON.stringify 时,\ 字符是一定会经过一层转义的(这样才符合 JSON 规范)。以 PHP 为例:

<?php
$regText = '\w+\.ke\.qq\.com'; // 注意 PHP 中单引号内的字符串不会经过解析
echo json_encode(array('pattern' => $regText));
// 返回的是 {"pattern":"\\w+\\.ke\\.qq\\.com"}

所以接口场景下,同样不存在 RegExp 构造器的 string 参数转义问题。

2、表单输入项的字符串赋值给变量时也无需转义

假设页面中存在输入框 <input id="test"> ,在输入框中输入字符 \w+\.ke\.qq\.com,则通过 JS 获取到的值可以直接传入 RegExp 构造器,同样无需考虑转义问题。

const regText = document.getElementById('test').value
new RegExp(regText) // 返回 /\w+\.ke\.qq\.com/

因为表单项中的字符串也是直接赋值,而非通过引号字面量的字符串定义方式赋值。

3、JS 代码中的转义处理

另外一种可能用到 RegExp string 参数的场景是:基于 JS 逻辑,动态创建正则表达式。例如正则表达式 /\w{3}/ 中的数字 3,是通过某个变量来传递的。那么在写正则时需要写成:

let n = 3
new RegExp('\\w{' + n + '}') // 这里的 \w 为特殊字符,需要经过 \ 转义

Python 语言中是通过 raw string 修饰符来解决字符串转义问题,在字符串前加上 r 标记,表示这个字符串的内容不经过解析。即 print r'\n' == '\\n' 返回 True。

为了解决模板字符串的解析和转义问题,ES6 模板字面量中引入了反引号(`)和 tag function(知名「CSS in JS」 库 styled-components 中大量使用了这种语法)。这里的场景就可以写成十分类似 Python 的风格,当需要转义的内容比较多时,能保持较好的正则表达式语义:

const r = String.raw
let n = 3
new RegExp(r`\w{${n}}`)

不过这种使用场景十分罕见,我至今还没有遇到过。

回过头来看,JS 正则表达式构造器的参数设计问题,其实不是 RegExp 引起的,而是 JavaScript String 的设计缺陷:单引号和双引号非但没有参考 PHP/Shell 之类的设计,反而给前端社区留下「应该使用单引号还是双引号」的代码风格争论。反观 Golang,在这块的约束就做得非常好。

从 RegExp 构造器看 JS 字符串转义设计的更多相关文章

  1. 浅谈 js 字符串之神奇的转义

    原文:浅谈 js 字符串之神奇的转义 字符串在js里是非常常用的,但是你真的了解它么?翻阅<MDN String>就可以了解它的常见用法了,开门见山的就让你了解了字符串是怎么回事. 'st ...

  2. 从字符串拼接看JS优化原则

    来自知乎的问题:JavaScript 怎样高效拼接字符串? 请把以下用于连接字符串的JavaScript代码修改为更高效的方式: var htmlString ='< div class=”co ...

  3. js 字符串换行 显示 使用 \ 转义

     js 字符串 有没有 像C# @ 那种 换行也可以显示的方法\ 

  4. html 转 js 字符串

    看到一个牛人的博客  http://riny.net/lab/#tools_html2js 看了下他的代码  挺棒的 所依赖的两个库在这里 https://github.com/Bubblings/l ...

  5. JS字符串替换函数:Replace(“字符串1″, “字符串2″),

    JS字符串替换函数:Replace(“字符串1″, “字符串2″), 1.我们都知道JS中字符串替换函数是Replace(“字符串1″, “字符串2″),但是这个函数只能将第一次出现的字符串1替换掉, ...

  6. 从js的repeat方法谈js字符串与数组的扩展方法

    js将字符串重复N次的repeat方法的8个版本 /* *@desc: 将一个字符串重复自身N次 */ //版本1:利用空数组的join方法 function repeat(target, n) { ...

  7. 浅谈 js 字符串 trim 方法之正则篇

    原文:浅谈 js 字符串 trim 方法之正则篇 关于 trim 其实没啥好说的,无非就是去除首位空格,对于现代浏览器来说只是简单的正则 /^\s+|\s+$/ 就可以搞定了.而且支持中文空格   等 ...

  8. js 字符串转换成数字的三种方法

    在js读取文本框或者其它表单数据的时候获得的值是字符串类型的,例如两个文本框a和b,如果获得a的value值为11,b的value值为9 ,那么a.value要小于b.value,因为他们都是字符串形 ...

  9. js字符串 数字 的转换

    js 字符串转化成数字 的 三种方法主要有 转换函数.强制类型转换.利用js变量弱类型转换. 1. 转换函数: js提供了parseInt()和parseFloat()两个转换函数.前者把值转换成整数 ...

随机推荐

  1. SSM-Spring-19:Spring中JdbcTemplate

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- Spring自带一个ORM持久化框架JdbcTemplate,他可以说是jdbc的加强版,但是对最细微的控制肯 ...

  2. 深夜学算法之SkipList:让链表飞

    1. 前言 上次写Python操作LevelDB时提到过,有机会要实现下SkipList.摘录下wiki介绍: 跳跃列表是一种随机化数据结构,基于并联的链表,其效率可比拟二叉查找树. 我们知道对于有序 ...

  3. border-radius 详解及示例

    border-radius [ˈbɔrdə(r)] - [ˈrediəs]   英文示意: border:边界,国界,边疆 radius:半径,范围   定义: 复合写法: border-radius ...

  4. Head First Servlet and JSP

    这书是原本这样还是翻译的问题?好多的地方大小写都写错了,比如javax.servlet,<servlet></servlet>.真是坑啊.

  5. ATOM系列之-atom报错"Cannot load the system dictionary for zh-CN"

    atom报错"Cannot load the system dictionary for zh-CN" 想必很多人(程序猿&程序媛)都和我一样,喜欢的这款很拉风的代码编辑器 ...

  6. bzoj4035 [HAOI2015]数组游戏

    这题显然把每个白格子看成一个子游戏 一个白格子$x$的$sg$值是$mex{[0,sg[2x],sg[2x] XOR sg[3x].....]}$ 打表发现一个数的$sg$值只和$n/x$有关,然后分 ...

  7. Matlab实现画柱状图坐标标签旋转

    引言: 在用matlab画柱状图或者其他图的时候,你是否碰到下图这种情况: 乍一看这个柱状图画的有馍有洋的,but,仔细一看你会发现,横坐标标签如果再长一点就不好看啦,甚至会重叠,就像这样: 这样的图 ...

  8. 你可能忽略的js类型转换

    前言 相信刚开始了解js的时候,都会遇到 2 == '2',但 1+2 == 1+'2'为false的情况.这时候应该会是一脸懵逼的状态,不得不感慨js弱类型的灵活让人发指,隐式类型转换就是这么猝不及 ...

  9. 蓝鲸 CTF web——密码泄露

    今天才知道还有蓝鲸安全这个平台,又涨了一点知识,我来分享一下个人的解题思路以及感受,希望能够帮助到那些需要的人 (我想把每一步都写下来,因为我还是萌新,所以我知道有可能会因为一步的不会导致所有都不会的 ...

  10. MYSQL—— year类型的使用与注意点!

    mysql的日期与时间类型:分为time.date.datetime.timestamp.year,主要总结下year的用法: 1.类型支持:year 与 year(4),注意无year(2)的定义方 ...