• 从 RegExp 构造器看 JS 字符串转义设计


    多年前我第一次入职腾讯的时候,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' 返回 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,在这块的约束就做得非常好。

  • 相关阅读:
    ASP.NET的三层架构(DAL,BLL,UI)
    页面开发辅助类—HtmlHelper初步了解
    ASP MVC之参数传递
    ASP.NET MVC学习之母版页和自定义控件的使用
    ASP.Net MVC开发基础学习笔记(2):HtmlHelper与扩展方法
    UITableView动态改变Cell高度
    UITableView动态改变Cell高度
    nodejs豆瓣爬虫
    nodejs豆瓣爬虫
    苹果系统OSX中Automator批量重命名
  • 原文地址:https://www.cnblogs.com/kaiye/p/9483937.html
Copyright © 2020-2023  润新知