js的indexOf方法内部是怎么实现的?大佬回:说的是Array的还是String的?

1,309 阅读3分钟

这是我参与更文挑战的第22天,活动详情查看: 更文挑战

前言

无聊摸鱼水群中,看到大佬们在讨论js的indexOf方法内部是怎么实现的?

接着有一位大佬,回了一句话:说的是Array.prototype.indexOf还是String.prototype.indexOf?

突然脑子不受使唤使劲哆嗦了一下:等等!ArrayString的方法竟然是不一样的实现办法?不对,他们接收参数和返回结果都是一致的,但为什么大佬要问出那句话呢?不对不对,对于ArrayString来说,在调用indexof时的逻辑肯定不一样。

啪的一下我就打开了MDN,仔细翻看了起来。

MDN

Array.prototype.indexOf的文档中看到了一行提示,ECMA-262 标准 的第5版之前Array的indexof是不存在的。

indexOf 在ECMA-262 标准 的第5版中被加入,但并非所有的浏览器都支持该方法。你可以在编写scripts时,在其开头使用以下代码,它能够允许你在没有本地支持的情况下使用indexOf方法。该算法符合ECMA-262第5版其中一项规定, 即假定 TypeError和 Math.abs 呈现它们原有的值。

ECMA262

于是我跑去看两个对象的indexof的实现方式。

String.prototype.indexOf

22.1.3.8 String.prototype.indexOf ( searchString [ , position ] )

The indexOf method takes two arguments, searchString and position, and performs the following steps:

//查看调用方法的String对象是否为null或者undefined
1. Let O be ? RequireObjectCoercible(this value).
// 调用内部的ToString,将调用方法的String对象转为字符串。
2. Let S be ? ToString(O).
// 和上述一样,第一个参数searchString转为字符串。
3. Let searchStr be ? ToString(searchString).
// 转换position, ToIntegerOrInfinity会将传入的参数转成整数或无穷大或无穷小
4. Let pos be ? ToIntegerOrInfinity(position).
// 如果position 不存在,那就赋值为0
5. Assert: If position is undefined, then pos is 0.
// 新建len为S的字符串长度
6. Let len be the length of S.
// 新建start为0和len之间的数字
7. Let start be the result of clamping pos between 0 and len.
// 返回StringIndexOf并传入参数
8. Return 𝔽(! StringIndexOf(S, searchStr, start)).

//我们看下StringIndexOf

6.1.4.1 StringIndexOf ( string, searchValue, fromIndex )
// 判断传入的string,searchValue 必须为string
1. Assert: Type(string) is String.
2. Assert: Type(searchValue) is String.
// 传入的start必须为非负整数
3. Assert: fromIndex is a non-negative integer.
4. Let len be the length of string.
// 如果searchValue是空字符串并且fromIndex ≤ len,则返回fromIndex。
5. If searchValue is the empty String and fromIndex ≤ len, return fromIndex.
// 新建searchLen 为searchValue的长度
6. Let searchLen be the length of searchValue.
// 对于每个以 fromIndex 开头的整数 i 使得 i ≤ len - searchLen,按升序排列
7. For each integer i starting with fromIndex such that i ≤ len - searchLen, in ascending order, do
   // 三种结果,
a. Let candidate be the substring of string from i to i + searchLen.
b. If candidate is the same sequence of code units as searchValue, return i.
8. Return -1.

Array.prototype.indexOf

下面就不描述了,在MDN中有实现下述的代码,非常容易看懂。

23.1.3.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )

When the indexOf method is called, the following steps are taken:

1. Let O be ? ToObject(this value).
2. Let len be ? LengthOfArrayLike(O).
3. If len is 0, return -1𝔽.
4. Let n be ? ToIntegerOrInfinity(fromIndex).
5. Assert: If fromIndex is undefined, then n is 0.
6. If n is +∞, return -1𝔽.
7. Else if n is -∞, set n to 0.
8. If n ≥ 0, then
a. Let k be n.
9. Else,
a. Let k be len + n.
b. If k < 0, set k to 0.
10. Repeat, while k < len,
a. Let kPresent be ? HasProperty(O, ! ToString(𝔽(k))).
b. If kPresent is true, then
i. Let elementK be ? Get(O, ! ToString(𝔽(k))).
ii. Let same be IsStrictlyEqual(searchElement, elementK).
iii. If same is true, return 𝔽(k).
c. Set k to k + 1.
11. Return -1𝔽.
// Production steps of ECMA-262, Edition 5, 15.4.4.14
// Reference: http://es5.github.io/#x15.4.4.14
if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(searchElement, fromIndex) {

    var k;

    // 1. Let O be the result of calling ToObject passing
    //    the this value as the argument.
    if (this == null) {
      throw new TypeError('"this" is null or not defined');
    }

    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get
    //    internal method of O with the argument "length".
    // 3. Let len be ToUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If len is 0, return -1.
    if (len === 0) {
      return -1;
    }

    // 5. If argument fromIndex was passed let n be
    //    ToInteger(fromIndex); else let n be 0.
    var n = +fromIndex || 0;

    if (Math.abs(n) === Infinity) {
      n = 0;
    }

    // 6. If n >= len, return -1.
    if (n >= len) {
      return -1;
    }

    // 7. If n >= 0, then Let k be n.
    // 8. Else, n<0, Let k be len - abs(n).
    //    If k is less than 0, then let k be 0.
    k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

    // 9. Repeat, while k < len
    while (k < len) {
      // a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the
      //    HasProperty internal method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      //    i.  Let elementK be the result of calling the Get
      //        internal method of O with the argument ToString(k).
      //   ii.  Let same be the result of applying the
      //        Strict Equality Comparison Algorithm to
      //        searchElement and elementK.
      //  iii.  If same is true, return k.
      if (k in O && O[k] === searchElement) {
        return k;
      }
      k++;
    }
    return -1;
  };
}