如何 clone 一个正则?

克隆一个正则,Lodash 库的实现方式是:

const reFlags = /\w*$/

function cloneRegExp(regexp) {
  const result = new regexp.constructor(regexp.source, reFlags.exec(regexp))
  result.lastIndex = regexp.lastIndex
  return result
}

cloneRegExp(/xyz/gim)
// => /xyz/gim
复制代码

通过这段代码,我们顺便复习一下 JS 正则对象的部分知识。

1.构造函数

首先,regexp.constructor 就是 RegExp

了解 JS 原型相关知识的话,这一点应该没问题。

具体说来,/xyz/gim 是正则字面量,是构造函数 RegExp 的实例。/xyz/gimconstructor 属性时,根据原型链原理,对象本身没有此属性时,要再去它的原型里找。而 /xyz/gim 的原型是 RegExp.prototype。同时 RegExp.prototype.constructor 正是 RegExp 本身。

构造函数 RexExp 的一个典型用法是:

var regexp = new RegExp('xyz', 'gim');
// 等价于
var regexp = /xyz/gim;
复制代码

2.正则实例组成

一个正则对象可以大致分成两部分,源码(source) 和修饰符(flags)。比如,/xyz/gimsource"xyz",而其 flags"gim"

var regexp = /xyz/gim
regexp.source
// => "xyz"
regexp.flags
// => "gim"
复制代码

关于修饰符,多说一句。在 JS 中,目前共有 6 个修饰符:gimsuy。正则对象转化为字符串时,其修饰符排序是按字母排序的。

var regexp = /xyz/imgyus;
regexp.flags
// => "gimsuy"
regexp.toString()
// => "/xyz/gimsuy"
复制代码

Lodash 的源码,获取修饰符用时没有通过 flags,而是采用正则提取:

/\w*$/.exec(regexp.toString()).toString()
// => gim
复制代码

其中,正则 /\w*$/ 匹配的是字符串尾部字母。因为目标正则可能没有修饰符,因此这里量词是 *

估计你看出来了。是的,下面代码里有两处类型转换(转字符串):

new regexp.constructor(regexp.source, reFlags.exec(regexp))
复制代码

3.lastIndex是可修改的

clone 正则时,还要 clone 其 lastIndex。这一点学到了!

lastIndex 表示每次匹配时的开始位置。 使用正则对象的 testexec 方法,而且当修饰符为 gy 时, 对 lastIndex 是有影响的。

例如:

var regexp = /\d/g;

regexp.lastIndex
// => 0 
regexp.test("123")
// => true

regexp.lastIndex
// => 1
regexp.test("1")
// => false
复制代码

1test 时,在输入字符串 "123" 中匹配到了第一个数字 "1"lastIndex 此时也变成了 1,表示下次的匹配位置将会跳过第 0 位,直接从第 1 位开始。

2test 时,此时输入是字符串 "1" ,只有一位字符,其第 1 位是空,因此匹配失败。此时 lastIndex 会重置为 0

最关键一点,lastIndex 属性不仅可读,而且可写:

var regexp = /\d/g;
regexp.lastIndex = 3
regexp.test("123")
// => false
复制代码

至此,lodash 的实现,应该都能全部看懂了:

const reFlags = /\w*$/

function cloneRegExp(regexp) {
  const result = new regexp.constructor(regexp.source, reFlags.exec(regexp))
  result.lastIndex = regexp.lastIndex
  return result
}

cloneRegExp(/xyz/gim)
// => /xyz/gim
复制代码

本文完。

欢迎阅读《JS正则迷你书》


本文参考:

cloneRegExp.js

Lodash是如何实现深拷贝的 - 掘金

猜你喜欢

转载自juejin.im/post/5c6247ebe51d45012c3cc6a7