在前端开发中,常常会遇到要判断数组中是否存在某个元素。其实判断的方式有不少种,咱们一个一个来了解下。javascript
咱们先来定义一个数组:前端
const arr = [ 13, false, 'abcd', undefined, 13, null, NaN, [1, 2], { a: 123 }, () => Date.now(), new Date('2021/03/04'), new RegExp('abc', 'ig'), Symbol('sym'), ];
在这个数组中,咱们包含了好几种类型:number, boolean, string, undefined, null, array, object, Date, Symbol 等。其中数字 13 出现了 2 次。java
咱们最熟悉的就是indexOf
了,毕竟他出现的早,兼容性也好,使用起来也很方便。数组
若是存在该元素,则返回第一次出现的索引值;若整个数组不存在该元素,则返回-1。函数
只要判断返回的数据是否是-1,就能知道数组中是否包含该元素。测试
arr.indexOf(13) >= 0; // true, indexOf返回0 arr.indexOf(2) >= 0; // false, indexOf返回-1
与 indexOf 对应的是 lastIndexOf,从最后往前查找元素,若存在该元素,则返回在数组中的最后一个的索引;若不存在该元素,则返回-1。3d
arr.lastIndexOf(13) >= 0; // true, lastIndexOf返回4, 最后一次出现的索引
两个方法在判断变量是否存在时,调用方式是同样的。code
indexOf 和 lastIndexOf 还有第 2 个可选参数 fromIndex,用来表示从哪一个索引开始进行搜索。regexp
在 indexOf 中,若 fromIndex 超过数组的长度,则直接返回-1,若为负数,则从最后往前数几个索引(arr.length-Math.abs(fromIndex)),而后开始日后搜索。对象
在 lastIndexOf 中,若 fromIndex 达到或超过数组的长度,则搜索整个数组;若为负数,则从最后往前数几个索引(arr.length-Math.abs(fromIndex)),而后开始往前搜索,若负数的绝对值超过了数组的长度,则直接返回-1。
arr.indexOf(13, 2); // 4, 从索引值2开始日后查找,首先找到的13的索引值为4 arr.indexOf(13, -10); // 4, 从索引值1(11-10)开始日后检索 arr.lastIndexOf(13, 2); // 0, 从索引值2往前开始搜索 arr.lastIndexOf(13, -2); // 4, 从索引值9(11-2)开始往前搜索
并且 indexOf 和 lastIndexOf 中采用的是严格相等的方式(===)来判断的。
arr.indexOf(null); // 5, 在null的前面有几个假值false和undefined,也能准确找到null的索引值
indexOf 主要是为了查找元素所在的索引值,只是咱们能够用返回的索引值来间接判断数组中是否存在该元素。
而在 ES7(ES2016)中添加的includes
方法,就是专门用来判断元素是否存在的。返回值为 true 或者 false,true 表示存在,false 表示不存在,简单明了。
arr.includes(13); // true arr.includes('abc'); // false arr.includes(false); // true, 存在false元素
同时,includes 方法中也存在第 2 个可选参数 fromIndex,fromIndex 的用法与 indexOf 中的同样。若 fromIndex 超过数组的长度,则直接返回-1,若为负数,则从最后往前数几个索引(arr.length-Math.abs(fromIndex)),而后开始日后搜索。
arr.includes(13, 5); // false, 从索引值5开始日后检索,没检索到
到目前为止,后面的几种类型,例如 Array, Object, Date 和 Symbol,咱们都没判断呢。咱们如今来判断下后面的几个元素:
// 使用indexOf判断 arr.indexOf(NaN); // -1 arr.indexOf([1, 2]); // -1 arr.indexOf({ a: 123 }); // -1 arr.indexOf(() => Date.now()); // -1 arr.indexOf(new Date('2021/03/04')); // -1 arr.indexOf(new RegExp('abc', 'ig')); // -1 arr.indexOf(Symbol('sym')); // -1 // 使用includes判断 arr.includes(NaN); // false arr.includes([1, 2]); // false arr.includes({ a: 123 }); // false arr.includes(() => Date.now()); // false arr.includes(new Date('2021/03/04')); // false arr.includes(new RegExp('abc', 'ig')); // false arr.includes(Symbol('sym')); // false
结局很惨,这几种元素在数组中都没有检索到。但是实际上在数组中都是真实存在的。
这是由于 indexOf 和 includes 都是采用严格相等的方式(===)来断定的。
NaN === NaN; // false, 两个NaN永远也不会相等 [1, 2] === [1, 2]; // false, 每一个声明出来的数组都有单独的存储地址 {a: 123} === {a: 123}; // false, 同数组 new Date('2021/03/04')===new Date('2021/03/04'); // false, 看着日期是相同的,可是用new出来的对象进行比较的,确定是不相等的 Symbol('sym')===Symbol('sym'); // Symbol类型的出现就是为了不冲突创造出来的类型,括号里的属性仅是为了方便描述而已
针对这些没法被检索的类型,咱们就须要本身写函数来判断特殊的类型了。
find()和 findIndex()容许咱们经过回调函数,来自定义判断的方式。
find()
方法返回数组中知足提供的测试函数的第一个元素的值。不然返回 undefined
。
find()方法没法检测数组中的 undefined 元素。
由于不存在和存在 undefined 元素,find()方法都会返回 undefined。这里咱们就要考虑其余方式了,稍后再讲。
arr.find((item) => item === 13); // 13, 找到了元素13 arr.find((item) => item === 3); // undefined, 没找到元素3 arr.find((item) => item === undefined); // undefined, 也不知道是找到了仍是没找到
对于上面稍微复杂点的类型,咱们就须要特殊的判断了:
arr.find((item) => typeof item === 'number' && isNaN(item)); // NaN // array和object类型进行比较时,状况很复杂,由于每一个元素的类型都没法肯定 // 若是肯定都是基本类型,如string, number, boolean, undefined, null等,能够将其转为字符串再比较 // 转字符串的方式也不少,如JSON.stringify(arr), arr.toString(), arr.split('|')等 // 复杂点的,只能一项一项比较,或者使用递归 arr.find((item) => item.toString() === [1, 2].toString()); // [1, 2] arr.find((item) => JSON.stringify(item) === JSON.stringify({ a: 123 })); // {a: 123} arr.find((item) => { if (typeof item === 'function') { return item.toString() === (() => Date.now()).toString(); } return false; }); // () => Date.now() arr.find((item) => { if (item instanceof Date) { return item.toString() === new Date('2021/03/04').toString(); } return false; }); // Thu Mar 04 2021 00:00:00 GMT+0800 arr.find((item) => { if (item instanceof RegExp) { return item.toString() === new RegExp('abc', 'ig').toString(); } return false; }); // /abc/gi // Symbol确实无法比较,只能比较描述是否同样 arr.find((item) => { if (typeof item === 'symbol') { return item.toString() === Symbol('sym').toString(); } return false; }); // Symbol(sym)
上面的判断代码在后面的方法也将会使用到。
咱们在上面对比了多种类型元素的比较,稍微来总结下。
先来定义一个函数:
const compare = (x, y) => {};
对于元素是 string, number, boolean, undefined, null 等基本类型的,能够直接进行比较:
const compare = (x, y) => { return x === y; };
NaN 用 typeof 来判断是 number 类型,但 NaN 不与任何数字相等,包括它本身。
const compare = (x, y) => { if (typeof x === 'number' && isNaN(x) && typeof y === 'number' && isNaN(y)) { return true; } return x === y; };
这些类型的,能够将变量转为字符串进行比较:
const compare = (x, y) => { if (typeof x === 'number' && isNaN(x) && typeof y === 'number' && isNaN(y)) { return true; } if ( (typeof x === 'function' && typeof y === 'function') || (x instanceof Date && y instanceof Date) || (x instanceof RegExp && y instanceof RegExp) || (x instanceof String && y instanceof String) || (x instanceof Number && y instanceof Number) ) { return x.toString() === y.toString(); } return x === y; };
对于 object 类型和 array 的,咱们能够将每一项拆开,而后利用上面的方式再挨个儿比较。
若是还要判断数组中是否存在 undefined,咱们可使用findIndex()
方法。
findIndex()
方法返回数组中知足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。
arr.findIndex((item) => item === undefined); // 3 arr.findIndex((item) => item === 3); // -1, 没有找到数字3
其余数据格式的判断,与上面的 find()同样。
some()
方法测试数组中是否是至少有 1 个元素经过了被提供的函数测试。它返回的是一个 Boolean 类型的值。
注意:若是用一个空数组进行测试,在任何状况下它返回的都是
false
。
some()方法与 find()方法的使用方式同样,只不过 some()方法返回的是 boolean 类型的数据。
arr.some((item) => item === false); // true arr.some((item) => item === undefined); // true arr.some((item) => typeof item === 'number' && isNaN(item)); // true arr.some((item) => item === 3); // false, 不存在数字3 arr.some((item) => { if (item instanceof Date) { return item.toString() === new Date('2021/03/04').toString(); } return false; }); // true
filter()
方法建立一个新数组, 其包含经过所提供函数实现的测试的全部元素。
不管找到几个元素或者没有元素,filter()方法都是会返回一个数组,数组中的数据就是咱们想要的元素。
arr.filter((item) => item === false); // 1 arr.filter((item) => item === undefined); // 1 arr.filter((item) => typeof item === 'number' && isNaN(item)); // 1 arr.filter((item) => item === 13); // 2 arr.filter((item) => item === 3); // 0 arr.filter((item) => { if (item instanceof Date) { return item.toString() === new Date('2021/03/04').toString(); } return false; }); // 1
所以咱们能够经过该数组的长度,来判断原数组是否包含咱们想要的元素。
查找数组中元素的方式有不少,咱们能够数组中元素的格式,来选择更合适的方式。若是都是一些基本类型,建议优先选择使用includes()
方法;若是格式比较复杂的,建议选择使用some()
方法。这两个方法都是直接返回 boolean 类型,无需更多的转换便可直接使用方法的结果。