如何 clone 一個正則?

克隆一個正則,Lodash 庫的實現方式是:javascript

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 正則對象的部分知識。java

1.構造函數

首先,regexp.constructor 就是 RegExpgit

瞭解 JS 原型相關知識的話,這一點應該沒問題。github

具體說來,/xyz/gim 是正則字面量,是構造函數 RegExp 的實例。/xyz/gimconstructor 屬性時,根據原型鏈原理,對象自己沒有此屬性時,要再去它的原型裏找。而 /xyz/gim 的原型是 RegExp.prototype。同時 RegExp.prototype.constructor 正是 RegExp 自己。markdown

構造函數 RexExp 的一個典型用法是:函數

var regexp = new RegExp('xyz', 'gim');
// 等價於
var regexp = /xyz/gim;
複製代碼

2.正則實例組成

一個正則對象能夠大體分紅兩部分,源碼(source) 和修飾符(flags)。好比,/xyz/gimsource"xyz",而其 flags"gim"oop

var regexp = /xyz/gim
regexp.source
// => "xyz"
regexp.flags
// => "gim"
複製代碼

關於修飾符,多說一句。在 JS 中,目前共有 6 個修飾符:gimsuy。正則對象轉化爲字符串時,其修飾符排序是按字母排序的。post

var regexp = /xyz/imgyus;
regexp.flags
// => "gimsuy"
regexp.toString()
// => "/xyz/gimsuy"
複製代碼

Lodash 的源碼,獲取修飾符用時沒有經過 flags,而是採用正則提取:spa

/\w*$/.exec(regexp.toString()).toString()
// => gim
複製代碼

其中,正則 /\w*$/ 匹配的是字符串尾部字母。由於目標正則可能沒有修飾符,所以這裏量詞是 *prototype

估計你看出來了。是的,下面代碼裏有兩處類型轉換(轉字符串):

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是如何實現深拷貝的 - 掘金

相關文章
相關標籤/搜索