js在迴圈中使用正則失效異常的坑

天渺工作室發表於2023-04-30

1、異常案例:

使用正則匹配111

    const regular = /111/g; // 匹配111
    // console.log(regular.test('111')); // true 匹配成功
    // console.log(regular.test('111,111')); // true 匹配成功

    const list = [
        '111',
        '111',
        '111,111',
        '111,111'
    ];
    list.forEach((element, index) => {
        // 異常寫法
        console.log('log_________' + regular.test(element));
    });  
    // 列印結果 
    // log_________true
    // log_________false // 會存在正則匹配為false的異常
    // log_________true
    // log_________true

why? 首先去MDN看了看正則的基礎概念
發現了lastIndex 這個屬性

2、原因分析

    用正規表示式只要設定了全域性匹配標誌 /g ,test()方法 的執行就會改變正規表示式 lastIndex 屬性。再迴圈中連續的執行 test() 方法,後面的執行將會從 lastIndex 數字處開始匹配字串。
原來如此,看來test() 方法確實也不能隨便濫用。
確認驗證一下:

    const regular = /111/g; // 匹配111

    const list = [
        '111',
        '111',
        '111,111',
        '111,111'
    ];
    list.forEach((element, index) => {
        // 異常寫法
        console.log('log_________' + regular.test(element));
        // 列印lastIndex
        console.info('logLastIndex___' + regular.lastIndex); 
    });  
    // 列印結果 
    // log_________true
    // logLastIndex___3
    // log_________false  // 確實因為lastIndex為3導致的
    // logLastIndex___0
    // log_________true
    // logLastIndex___3
    // log_________true
    // logLastIndex___7

3、解決方法1

上面我們發現正則test()方法有lastIndex屬性,每次迴圈給恢復一下。

    const regular = /111/g; // 匹配111
    const list = [
        '111',
        '111',
        '111,111',
        '111,111'
    ];
    list.forEach((element, index) => {
        regular.lastIndex = 0;
        console.log('log_________' + regular.test(element)); // 列印正常
    });  

問題解決 OK了。

3、解決方法2

上面我們發現正規表示式設定了全域性標誌 /g 的問題,去掉/g全域性匹配。(這個具體還得看自己的應用場景是否需要/g)

    const regular = /111/; // 去掉/g全域性匹配
    const list = [
        '111',
        '111',
        '111,111',
        '111,111'
    ];
    list.forEach((element, index) => {
        console.log('log_________' + regular.test(element)); // 列印正常
    });  

OK了。

3、解決方法3

js有基本資料型別和引用資料型別
ECMAScript包括兩個不同型別的值:基本資料型別和引用資料型別。\
基本資料型別:Number、String、Boolen、Undefined、Null、Symbol、Bigint。\
引用資料型別:也就是物件型別Object type,比如:物件(Object)、陣列(Array)、函式(Function)、日期(Date)、正規表示式(RegExp)。

so正規表示式屬於引用資料型別,按傳統思維肯定是需要“深複製”的,需要new 一個新Object。

    const regular = /111/g;
    const list = [
        '111',
        '111',
        '111,111',
        '111,111'
    ];
    list.forEach((element, index) => {
        // 正確寫法 new RegExp的記憶體指向在迴圈過程中每次都單獨開闢一個新的“物件”,不會和前幾次的迴圈regular.test(xxx)改變結果而混淆
        // console.log('log_________' + /111/g.test(element)); // 這樣寫當然也行
        console.log('log_________' + new RegExp(regular).test(element)); // 列印OK了
    });  
    

OK了。

相關文章