javascript - 为什么原生 JS FILTER 函数的输出会有所不同

标签 javascript filter

我使用 native JS FILTER 函数时遇到以下情况,如下所述。

var array = ['hello', 'you', 'hello', 'you', 'hello', 'you', 'hello', 'you', 'hello', 'you', 'hello', 'you', 'hello', 'you', 'hello'];

var regex = new RegExp( "hello", "gi" );

function allMatches( item, index, array ){
  return this.test( item );
}

现在像这样运行...

array.filter( allMatches, regex ).length;
=> 7 // should be 8.

注意:传入 allMatches 函数的所有 8 个项目按其应有的方式返回 true,但数组缺少一项。

现在让我们按照如下所述更正 allMatches 函数。

 var array = ['hello', 'you', 'hello', 'you', 'hello', 'you', 'hello', 'you', 'hello', 'you', 'hello', 'you', 'hello', 'you', 'hello'];

// NO LONGER USE THIS var regex = new RegExp( "hello", "gi" );

function allMatches( item, index, array ){
  return /hello/gi.test( item );
}

现在像以前一样运行...

array.filter( allMatches ).length;
=> 8 // as it should be.

请注意所有示例中的以下项目...

  1. 每次迭代THIS都是正则表达式,正如它应该的那样。
  2. 每次应该为TRUE的迭代都为真,即全部 8 次。
  3. THIS 始终是正确的正则表达式。

我并不是在寻找如何完成这项工作,因为我知道一种方法可以做到这一点。我希望对为什么会发生这种情况有一个核心了解,以便我将来可以避免这个问题。

这里还有一些使用以下函数的尝试

var regex = new RegExp( "hello", "gi" ); // as before

function allMatches( item, index, array ){
  return this.test( item );
}

array.filter( allMatches, regex ).length;
=> 7 // again, NOT correct.

array.filter( allMatches, /hello/gi ).length;
=> 8 // correct. Passing a regex litteral.

array.filter( allMatches, new RegExp( "hello", "gi" )).length;
=> 8 // correct. Passing the same regexp from 

Here is the same non-working example but with a referenced regex literal. var nonConstructorRegex = /hello/gi;

array.filter( allMatches, nonConstructorRegex ).length;
=> 7 // NOT correct.

注意:我认为这与传递对正则表达式的引用有关,但是使用引用的正则表达式文字可以工作,而从构造函数构建的新 RegExp 变量则不行。请参阅上面的第一项。

更新

当结果不正确时,会将第一个 HELLO 计为 false,而它应该为 TRUE

当正则表达式值作为引用传入时,似乎会发生这种情况。当正则表达式(无论是构造的还是文字的)传入时,不需要引用。

难道只是时间问题???

更新

在研究了 MDN 引用答案后,我明白了原因。

Examples Finding successive matches

If your regular expression uses the "g" flag, you can use the exec() method multiple times to find successive matches in the same string. When you do so, the search starts at the substring of str specified by the regular expression's lastIndex property (test() will also advance the lastIndex property). For example, assume you have this script:

var myRe = /ab*/g;
var str = 'abbcdefabh';
var myArray;

while ((myArray = myRe.exec(str)) !== null) {
  var msg = 'Found ' + myArray[0] + '. ';
  msg += 'Next match starts at ' + myRe.lastIndex;
  console.log(msg);
}

如何用我的例子来测试这个想法......

var array = ['hello', 'hello', 'hello', 'hello'];
var regex = new RegExp( "hello", "gi" );

function allMatches( item, index, array ){
  console.log(this.lastIndex) 
 return this.test( item );
}

array.filter( allMatches, regex ).length;
=> 2.

每次迭代都会console.log .lastIndex 的累积位置。

因此,第一项将导致 .lastIndex 为 5,如字符串 "hello" 的位置 5 所示。

下一个 hello 将命中,但 .lastIndex 将位于位置 5。 .test() 函数将检查字符串是否与正则表达式匹配,但位于位置 5。这将导致 0,从而将 .lastIndex 重置为 0。

第三次迭代将从 0 开始,并检查 hello 是否与正则表达式匹配。

然后将进行第四次迭代,但 .lastIndex 将再次设置在位置 5 处,因此我们将继续执行此操作,直到数组末尾,在本例中就是这样。

因此...

IT WAS COLONEL MUSTARD, IN THE COATROOM, WITH THE "g" FLAG!!!

最佳答案

这与.filter()没有太大关系。

当您在正则表达式中包含“g”标志时,每次调用 .test() 都会执行源字符串搜索,如果找到匹配项,则会设置RegExp 对象上的 .lastIndex 属性的值到下一个搜索应开始的源字符串的索引。

当您在过滤器函数中使用正则表达式文字时,这并不重要,因为对该函数的每次调用都会创建一个新的 RegExp 实例。但是,当您在连续调用中重复使用正则表达式时,.lastIndex 的值将会产生影响。

您发布的示例包含交替匹配和不匹配的字符串数组,不会显示任何明显的问题。但是,如果连续有两个 "hello" 字符串,那么就会出现这种情况,因为在匹配第一个字符串后,.lastIndex 值将为 5,因此当为下一个搜索调用 .test() ,搜索将从字符串末尾开始并失败。

底线:去掉“g”标志。

关于javascript - 为什么原生 JS FILTER 函数的输出会有所不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32236756/

相关文章:

ios - iPhone 上的慢速 CSS 过滤器?

javascript - 如何在 es6 中启用 ts-check

javascript - vue.js - 从另一个组件调用组件方法

javascript - Twilio - 一个接一个地调用电话

javascript - AJAX 在我的项目中不起作用

python - Django post_delete : count all objects which have one matching attribute with deleted object

javascript - 当我们崩溃时过渡不好

perl - .vim 文件夹中有存放文本过滤器的好地方吗?

ios - 如果 "submit_instance_key"的值为 1,则在此数组上使用的最佳高阶函数返回 bool 值?

javascript - 使用 forEach 过滤数组