这是我参与更文挑战的第22天,活动详情查看: 更文挑战
前言
无聊摸鱼水群中,看到大佬们在讨论js的indexOf方法内部是怎么实现的?
。
接着有一位大佬,回了一句话:说的是Array.prototype.indexOf还是String.prototype.indexOf?
突然脑子不受使唤使劲哆嗦了一下:等等!Array
和String
的方法竟然是不一样的实现办法?不对,他们接收参数和返回结果都是一致的,但为什么大佬要问出那句话呢?不对不对,对于Array
和String
来说,在调用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;
};
}