多年前我第一次入职腾讯的时候,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. 用CSS画小猪佩奇,你就是下一个社会人!

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 作者:江志耿 | 腾讯TEG网络工程师 我是佩奇,哼,这是我的弟弟乔治,呱呱,这是我的妈妈,嚯,这是我的爸爸,嚯~ 背景 小猪佩奇已经火了好 ...

  2. Python实现批量新建SecureCRT Session

    最近因为工作需要,我需要在ssh的时候保存几千台网关的session,工作量相当大(也就是ssh的时候需要记住用户名和密码,然后还要再session选项中录入enable密码,相当繁琐),而且设备的用 ...

  3. 基于gtid的复制

    Ⅰ.GTID的介绍 global transaction id identifier 全局事务id gtid = server_uuid + transaction_id server_uuid是全局 ...

  4. 监听Web容器启动与关闭

    在Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期. 要监听web ...

  5. 利用java反射机制实现读取excel表格中的数据

    如果直接把excel表格中的数据导入数据库,首先应该将excel中的数据读取出来. 为了实现代码重用,所以使用了Object,而最终的结果是要获取一个list如List<User>.Lis ...

  6. python如何优雅的打飞机

    这是一个打飞机的游戏,结构如下:     其中images中包含的素材为   命名为alien.png   命名为ship.png 游戏效果运行是这样的:     敌军,也就是体型稍微大点的,在上方左 ...

  7. scrapy顺序执行多个爬虫

    # -*- coding:utf-8 -*- from scrapy import cmdline from scrapy.cmdline import execute import sys,time ...

  8. python实现简单的一个刷票点赞功能

    投票网址:http://best.zhaopin.com/?sid=121128100&site=sou 在以上网址中找到"XXX技术有限公司",通过Python进行刷票. ...

  9. bzoj5250 [2018多省省队联测]秘密袭击

    博主蒟蒻,目前还不会动态dp,所以下面说的是一个并不优秀的暴力,我会补的! 我们考虑按权值从大到小依次点亮每个点,相同权值可以同时点亮,每次点亮后,我们进行一次树形背包. 处理出$f[i][j]$表示 ...

  10. 面试阿里前端P6血和泪换来的收获

      我的一个朋友在前端耕耘一段时间,也在网上进行了高度培训学习,最近一段时间他打算跳槽去阿里面试前端P6开发岗位,结果被痛虐了一回,估计从此以后会给他留下不可磨灭的阴影啊 真是十年生死两茫茫,一鲁代码 ...