JS `Array.prototype.includes()`:判断数组是否包含某个元素

各位程序猿/媛,大家好!我是今天的讲师,咱们今天来聊聊JavaScript里一个简单却又非常实用的数组方法:Array.prototype.includes()

开场白:数组中的“捉迷藏”高手

想象一下,你有一堆玩具(一个数组),你想知道里面有没有你最喜欢的变形金刚(一个元素)。 你会怎么做? 一种方法是,你一个一个地找,直到找到为止,或者找完所有玩具都没找到。includes()方法就像一个训练有素的“捉迷藏”高手,能帮你快速地判断某个玩具(元素)是否在你的玩具堆(数组)里。

includes()方法的基本用法

includes()方法用于判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false

语法:

array.includes(valueToFind[, fromIndex])

参数:

  • valueToFind: 要查找的元素值。
  • fromIndex (可选): 从哪个索引位置开始查找。如果为负值,则从 array.length + fromIndex 索引开始搜索。 默认值为 0

返回值:

  • Boolean: 如果数组包含指定的元素,则返回 true;否则返回 false

举个栗子(例子)说明

const fruits = ['apple', 'banana', 'orange'];

console.log(fruits.includes('banana')); // true
console.log(fruits.includes('grape'));  // false

在这个例子中,我们创建了一个名为 fruits 的数组,其中包含三种水果。fruits.includes('banana') 返回 true,因为数组中确实存在 ‘banana’。而 fruits.includes('grape') 返回 false,因为数组中没有 ‘grape’。

fromIndex参数:从哪里开始找?

fromIndex 参数允许你指定从数组的哪个位置开始查找。这在大型数组中特别有用,可以避免不必要的搜索。

const numbers = [1, 2, 3, 4, 5, 1, 2];

console.log(numbers.includes(2));       // true (默认从索引 0 开始)
console.log(numbers.includes(2, 2));    // true (从索引 2 开始)
console.log(numbers.includes(2, 5));    // true (从索引 5 开始)
console.log(numbers.includes(2, 6));    // true (从索引 6 开始)
console.log(numbers.includes(2, 7));    // false (从索引 7 开始)
console.log(numbers.includes(2, -1));   // false (从索引 6 开始,相当于 7-1=6)
console.log(numbers.includes(2, -2));   // true (从索引 5 开始,相当于 7-2=5)
console.log(numbers.includes(2, -6)); // true (从索引 1 开始,相当于 7-6=1)

注意,如果 fromIndex 大于或等于数组的长度,则直接返回 false,不会进行搜索。 如果计算后的索引小于0,则从索引 0 开始。

const arr = [1, 2, 3];

console.log(arr.includes(2, 100)); // false (fromIndex 大于数组长度,直接返回 false)
console.log(arr.includes(2, -5)); // true (相当于 arr.includes(2, 0))

includes()方法与类型判断

includes() 方法在比较元素时,使用“SameValueZero”算法。 这与 === (严格相等)略有不同。 最大的区别在于 NaN 的处理。

const arr = [1, NaN, 3];

console.log(arr.includes(NaN)); // true (includes() 认为 NaN 等于 NaN)
console.log(arr.indexOf(NaN));  // -1 (indexOf() 使用严格相等,认为 NaN 不等于 NaN)
console.log(NaN === NaN); // false

indexOf() 方法使用严格相等(===)进行比较,因此 NaN === NaN 返回 false,导致 indexOf(NaN) 返回 -1。 而 includes() 方法则认为 NaN 等于 NaN,所以 includes(NaN) 返回 true

indexOf()方法的对比

你可能已经注意到,indexOf() 方法也能用来判断数组是否包含某个元素。 那么,includes()indexOf() 有什么区别呢?

特性 includes() indexOf()
返回值 truefalse 元素的索引,或者 -1(未找到)
NaN 处理 能够正确判断数组中是否包含 NaN 无法正确判断数组中是否包含 NaN
可读性 更易于理解,语义更明确 需要与 -1 比较才能判断是否包含

总的来说,如果你的目标仅仅是判断数组是否包含某个元素,includes() 更加简洁明了,并且能正确处理 NaN。 如果你需要知道元素在数组中的索引位置,或者需要兼容不支持 includes() 的旧版本浏览器,则可以使用 indexOf()

代码示例:使用 includes() 简化代码

假设你需要检查一个用户的角色是否具有某个权限。 使用 indexOf() 的写法可能是这样的:

const userRoles = ['admin', 'editor', 'viewer'];

function hasPermission(role) {
  return userRoles.indexOf(role) !== -1;
}

console.log(hasPermission('admin'));   // true
console.log(hasPermission('author'));  // false

使用 includes(),代码会更加简洁:

const userRoles = ['admin', 'editor', 'viewer'];

function hasPermission(role) {
  return userRoles.includes(role);
}

console.log(hasPermission('admin'));   // true
console.log(hasPermission('author'));  // false

处理对象数组

includes() 方法对于原始类型(如字符串、数字、布尔值)的数组非常有效。 但是,对于对象数组,需要特别注意,因为它比较的是对象的引用,而不是对象的内容。

const objects = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];

const alice = { id: 1, name: 'Alice' };

console.log(objects.includes(alice)); // false (尽管对象的内容相同,但引用不同)

const bob = objects[1];

console.log(objects.includes(bob));   // true (引用相同)

在这个例子中,尽管 alice 对象的内容与 objects 数组中的第一个对象相同,但它们是不同的对象,具有不同的引用,因此 objects.includes(alice) 返回 false。 只有当查找的元素与数组中的元素引用相同时,includes() 才会返回 true

如何判断对象数组是否包含特定属性的对象?

如果你需要判断对象数组是否包含具有特定属性的对象,可以使用 some() 方法结合 includes() 实现。

const objects = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];

function hasObjectWithId(id) {
  return objects.some(obj => obj.id === id);
}

console.log(hasObjectWithId(1));  // true
console.log(hasObjectWithId(3));  // false

或者,结合filter来实现:

const objects = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];

function hasObjectWithId(id) {
  return objects.filter(obj => obj.id === id).length > 0;
}

console.log(hasObjectWithId(1));  // true
console.log(hasObjectWithId(3));  // false

兼容性

Array.prototype.includes() 是 ECMAScript 2016 (ES7) 中引入的。 这意味着在一些旧版本的浏览器中可能不支持。 不过,你可以使用 polyfill 来提供兼容性。

一个简单的 polyfill 实现如下:

if (!Array.prototype.includes) {
  Array.prototype.includes = function(searchElement /*, fromIndex*/) {
    'use strict';
    if (this == null) {
      throw new TypeError('Array.prototype.includes called on null or undefined');
    }

    const O = Object(this);
    const len = parseInt(O.length, 10) || 0;
    if (len === 0) {
      return false;
    }

    const n = parseInt(arguments[1], 10) || 0;
    let k;
    if (n >= 0) {
      k = n;
    } else {
      k = len + n;
      if (k < 0) {
        k = 0;
      }
    }

    while (k < len) {
      if (searchElement === O[k]) {
        return true;
      }
      k++;
    }
    return false;
  };
}

这段代码检查浏览器是否支持 includes() 方法。 如果不支持,则会添加一个 includes() 方法的实现。

总结

Array.prototype.includes() 是一个简单而强大的数组方法,用于判断数组是否包含某个元素。 它比 indexOf() 更加易于理解,并且能正确处理 NaN。 在编写代码时,根据你的需求选择合适的方法。 如果只需要判断数组是否包含某个元素,includes() 通常是更好的选择。 如果需要兼容旧版本浏览器,可以使用 polyfill。

练习题

  1. 编写一个函数,接收一个数组和一个值作为参数,如果数组包含该值,则返回 true,否则返回 false。使用 includes() 方法实现。
  2. 编写一个函数,接收一个对象数组和一个属性名作为参数,判断数组中是否包含具有该属性的对象。
  3. 在不支持 includes() 方法的浏览器中测试你编写的 polyfill。

希望今天的讲解对大家有所帮助! 感谢各位的聆听!下课!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注