探索 JavaScript 中字符串和数组的 indexOf 方法

当您需要在数组或字符串中找到一个元素时,‘indexOf()’是您最好的朋友之一。

「indexOf」在 Arrays

代码先,解释后:

1[label Module: findSpencer.js]
2const zoo = ['🐒', '🦄', '🐊', '🐸', '🐙'];
3const spencersIndex = zoo.indexOf('🐊');
4// spencersIndex === 2
5const spencer = zoo[spencersIndex];
6// spencer === '🐊'

在其最简单的版本中,indexOf方法采用一个参数,这是我们试图找到的元素,然后它返回了它在数组中找到的第一个元素的索引,满足了el ===目标。这意味着即使你的数组中有两个匹配, indexOf 只会返回一个结果。

当数组中的任何项目都没有满足el ===目标检查时,返回-1的值。

但是假设我们也在寻找Skyler(Spencer的妹妹),然后我们可以添加一个额外的可选参数来从不同的索引开始搜索。

1[label Module: findSkyler.js]
2const zoo = ['🐒', '🦄', '🐊', '🐸', '🐙',  '🐊']; // Skyler just arrived!
3const spencersIndex = zoo.indexOf('🐊'); // === 2 same as before
4
5// We start searching after 
6const skylersIndex = zoo.indexOf('🐊', spencersIndex + 1);
7// skylersIndex === 5

我们还可以在 Array 原型上创建一种方法,该方法会根据 indexOf 返回所有索引。

 1[label Module: indicesOf.js]
 2// Try to think of ways to make indicesOf more performant
 3Array.prototype.indicesOf = function (target) {
 4  const result = [];
 5  let currentIndex = 0;
 6  while(true) {
 7    // here this ===  the array on which we call `indicesOf`
 8    const targetIndex = this.indexOf(target, currentIndex);
 9    if (targetIndex == -1)
10      break;
11
12    result.push(targetIndex);
13
14    currentIndex = targetIndex +1;
15  }
16
17  return result;
18}
19const zoo = ['🐒', '🦄', '🐊', '🐸', '🐙',  '🐊']; // Skyler just arrived!
20const alligatorsIndices = zoo.indicesOf('🐊');
21// alligatorsIndices returns [2, 5]

你不能用 indexOf 找到什么?

您可能已经注意到,我们通过使用三重等同比较 el ===目标(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness)来中断搜索,这意味着,例如,我们不能通过参考来测试其他数组、对象或函数。

 1const simpleArray = [1, 2, 3];
 2const simpleFunction = () => {console.log('hey')};
 3const simpleObject = {alligator: 'cage'};
 4
 5const compositeArray = [simpleArray, simpleFunction, simpleObject];
 6
 7// These all work as expected because we compare by reference
 8compositeArray.indexOf(simpleArray); // returns 0
 9compositeArray.indexOf(simpleFunction); // returns 1
10compositeArray.indexOf(simpleObject); // returns 2
11
12// These won't work 
13compositeArray.indexOf([1, 2, 3]); // returns -1
14compositeArray.indexOf(() => {console.log('hey')}); // returns -1
15compositeArray.indexOf({alligator: 'cage'}) // returns -1

深度指数

假设我们想创建一个实用程序,以便反复检查对象、数组和函数:

 1[label Module: inDepthIndexOf.js]
 2Array.prototype.deepIndexOf = function (target) {
 3  // If the target is an object, array or a function, we give it a special treatment
 4  if (typeof target === 'object' || typeof target === 'function') {
 5    // We stringify the target 
 6    const searchTarget = target.toString()
 7    // We loop through all of the elements of the array
 8    for (let index = 0; index < this.length; index++){
 9      const element = this[index]
10      // We check if the element in the array is an object or a function AND if so we check if its' stringified value is equal to our target
11      if ((typeof element === 'object' || typeof target === 'function') && element.toString() === searchTarget) {
12        // if we have a match we interrupt the loop and return the index
13        return index
14      }
15    }
16    // if nothing matched we return -1
17    return -1
18  }
19  return this.indexOf(target)
20}
21
22const simpleArray = [1, 2, 3];
23const simpleFunction = () => {console.log('hey')};
24const simpleObject = {alligator: 'cage'};
25
26const compositeArray = [simpleArray, simpleFunction, simpleObject];
27
28// These all work as expected because we compare by reference
29compositeArray.deepIndexOf(simpleArray); // returns 0
30// ... You know the rest
31// These will work!
32compositeArray.deepIndexOf([1, 2, 3]); // returns 0
33compositeArray.deepIndexOf(() => {console.log('hey')}); // returns 1
34compositeArray.deepIndexOf({alligator: 'cage'}) // returns 2

有很多方法可以改进这个代码 如果你有时间,试着想想如何让它更准确和性能。

表演

使用indexOf比简单地做for loop要慢得多,这并不意味着indexOf是慢的,如果你的数组很小,你永远不会看到 indexOf() 或 a for loop 之间的差异,如果你的数组如此之大,以至于你可以注意到两种方法之间的差异,那么你可能想知道为什么你的数组如此之大,以及如何优化搜索。

1var spencersIndex
2// This is faster than indexOf('🐊') but it is much uglier
3for(var index = 0; index < zoo.length; index ++) {
4  if (zoo[index] === '🐊')
5    spencersIndex = index
6}
7// spencers Index === 2

字符串中的indexOf

你可以将从Array.indexOf的所有逻辑转移到这个部分。

 1[label Module: whereIsSpencer.js]
 2const whereIsSpencer = "We are all looking for Spencer the alligator. Spencer is a dear friend. Lookout here comes 🐊!"
 3
 4const spencersIndex = whereIsSpencer.indexOf('Spencer');
 5// spencersIndex ===  23
 6
 7// to find find the second occurence of 'Spencer',
 8// we need to add one to the position of spencer #1
 9const secondSpencerIndex = whereIsSpencer.indexOf('Spencer', spencersIndex + 1);
10// secondSpencerIndex ===  46
11
12const alligatorIndex = whereIsSpencer.indexOf('🐊');
13// alligatorIndex ===  91
14
15// Be careful the search is case sensitive!
16const lowerCaseSpencer = whereIsSpencer.indexOf('spencer');
17// lowerCaseSpencer === -1

现在我们可以为 Array.prototype.string 创建相同的 indexOf 函数。

 1[label Module: indicesOfStrings.js]
 2// This is a bit more concise than the previous indicesOf function
 3// But it is exactly the same logic
 4String.prototype.indicesOf = function (target) {
 5  let currentIndex = this.indexOf(target);
 6  const allIndices = []
 7  while(currentIndex != -1) {
 8    allIndices.push(currentIndex);
 9    currentIndex =  this.indexOf(target, currentIndex +1 );
10  }
11
12  return allIndices;
13}
14
15const whereIsSpencer = "We are all looking for Spencer the alligator. Spencer is a dear friend. Lookout here comes 🐊!";
16
17const spencerIndices = whereIsSpencer.indicesOf('Spencer');
18// spencerIndices equals [23, 46]

我希望你有趣的阅读这个帖子! 如果你有任何建议,问题或评论,请自由地问在Twitter(https://twitter.com/alligatorio)。

Published At
Categories with 技术
Tagged with
comments powered by Disqus