ECMAScript 7 中新增了用于检测数组中是否包含某个元素 Array.prototype.includes() API,想到了 Array 其实有很多相关 API 可以检测到是否包含某个元素,比如 Array.prototype.indexOf,于是好奇为什么要实现这样一个 "看起来功能有点重复的 API"。

前端开发 QQ 群:377786580

原文发表于 http://tasaid.com,转载请参阅 转载授权

前言

最近又看了下 ECMAScript 7 规范,看到新的规范中包含 Array.prototype.includes(),方法签名如下:

Array.prototype.includes(value : any): boolean

Array.prototype.includes() 是用于检测数组中是否包含某个元素。

[0, 1].includes(1) // true
['foo', 'bar'].includes('baz') // false

想到了 Array 其实有很多相关 API 可以检测到是否包含某个元素:

[0, 1].findIndex(i => i == 1) // 1
['foo', 'baz'].find(i => i == 'foo') // foo
['foo', 'baz'].indexOf('foo') // 0
  • Array.prototype.findIndex():返回数组中满足提供的测试函数的第一个元素的索引。否则返回 -1
  • Array.prototype.find():返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
  • Array.prototype.indexOf():返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回 -1

我们可以简单的通过判断实现类似 Array.prototype.includes() 的效果:

export const includes = (sources : any[] searchElement: any): boolean => {
return !!~any.indexOf(searchElement)
}

于是好奇为什么要实现这样一个 "看起来功能有点重复的 API"。

查询了 StackOverflow 和 TC39 (Technical Committee 39,JavaScript 委员会) 的 ECMAScript 提案,找到一些细节。

Array.prototype.includes 前身

早前的 Array.prototype.includes 的提案名为 Array.prototype.contains,但由于有很多网站自行 hack 了 Array.prototype.contains(其实主要是因为 MooTools 导致的),看起来就跟上面的代码类似。

JavaScript 中所有原生提供的方法属性都是 不可枚举的( enumerable ) 的,我们可以通过 Object.getOwnPropertyDescriptor(object: any, prototypeName : String) 来获取这个属性的属性描述符 (Property Descriptor)。

Object.getOwnPropertyDescriptor(Array.prototype, 'indexOf')
// output { writable: true, enumerable: false, configurable: true, value: ƒ() }

给对象赋值,是不会改变原属性的属性描述符,我们可以给 Array.prototype.indexOf 重新赋值,之后获取它的属性描述符,会发现 indexOf 仍是不可枚举的:

Array.prototype.indexOf = () => { return -1 }
Object.getOwnPropertyDescriptor(Array.prototype, 'indexOf')
// output { writable: true, enumerable: false, configurable: true, value: ƒ() }

而这些网站自行 hackcontains() 是可以被枚举的,也就是可以通过 for..in 读出来。

发现问题了么?

如果规范实现 contains(),会导致 contains() 无法被 for..in 读出来,而之前自行 hackcontains() 是可以被读出来的,所以会出现代码没变动,但是在新规范推出后会产生 bug 的情况。

Array.prototype.contains 初稿阶段,考虑到新的规范不能让世界上许多现有的网站出问题,所以改名成了 Array.prototype.includes

细节

起源

虽然我们可以使用 indexOf() 来模拟 includes() 的行为,但是 indexOf() 在语义上无法清晰的描述这个场景。

includes() 是明确的判断 "是否包含该项",而 indexOf() 是 "查找数组中第一次出现对应元素的索引是什么,再针对返回的索引进一步处理逻辑",例如下面的代码:

// indexOf
if (~arr.indexOf(1)) {
// do something
} // includes
if (arr.includes(1)) {
// do something
}

为什么叫做 includes 而不是 has

has 是用于 key 的,而 includes 是检测 value 的:

let foo = new Map()
foo.set('name', 'linkFly')
foo.has('name') // true

SameValueZero

Array.prototype.includes 底层使用了 SameValueZero() 进行元素比较。

目前 ES2015 草案中有四种相等算法:

  • 抽象标准相等比较:实现接口是 == 运算符

  • 严格相等比较:实现接口是 === 运算符,Array.prototype.indexOf 就是使用这种比较

  • SameValueZero():没有直接暴露的接口,内部实现接口是 MapSet

    const foo = new Map()
    foo.set(0, '0') // Map(1) {0 => "0"}
    foo.set('0', 'zero') // Map(2) {0 => "0", "0" => "zero"}
    foo.get(0) // 0
    foo.get('0') // zero
  • SameValue():实现接口是 Object.is()

    NaN === NaN // false
    Object.is(NaN, NaN) // true -0 === +0 // true
    Object.is(-0, +0) // false

SameValue() 不同的是,SameValueZero() 不区分 +0-0。而 includes 为了和 JavaScript 其他特性保持一致 所以内部也采用了 SameValueZero 实现。

所以 Array.prototype.includes 也不区分 +0-0 ,当然也可以检测 NaN

[-0].includes(+0) // true
[NaN].includes(NaN) // true
[NaN].indexOf(NaN) // -1

具体的相等比较运算符差异请参阅 [MDN - Equality comparisons and sameness](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#A model for understanding equality comparisons)。

具体 Array.prototype.includes 实现的细节可以参考 ecma-262/ECMAScript 7 实现规范

参考和引用

有了 indexOf,为什么 ECMAScript 7 还添加了 Array.prototype.include的更多相关文章

  1. ES6中新添加的Array.prototype.fill

    用法 array.fill(start=0, end=this.length) 示例 [1, 2, 3].fill(4) // [4, 4, 4] [1, 2, 3].fill(4, 1) // [1 ...

  2. [基础] Array.prototype.indexOf()查询方式

    背景 最近在看Redux源码,createStore用于注册一个全局store,其内部维护一个Listeren数组,存放state变化时所有的响应函数. 其中store.subscribe(liste ...

  3. 使用Array.prototype.indexOf()的几点注意

    对应indexOf这个方法,在日常开发中比较常见的应该是String.prototype.indexOf()方法,Array.prototype.indexOf()方法和其有很大的相似性,本文不想去描 ...

  4. Array.prototype.indexOf

    arr.indexOf(searchElement[, fromIndex = 0]) Array.prototype.indexOf()

  5. 利用Array Prototype的方法来实现对dom集合的筛选、indexOf、map等功能

    <!DOCTYPE html><html> <head> <title>TODO supply a title</title> <me ...

  6. 数组Array和字符串String的indexOf方法,以及ES7(ES2016)中新增的Array.prototype.includes方法

    前言 我们在判断某一个字符是否存在于一个字符串中或者某一个值是否存在于一个数组中时,ES7之前我们需要使用indexOf,ES7引入了新的方法includes 语法 数组:Array.inexOf(s ...

  7. JS高级---为内置对象添加原型方法

    为内置对象添加原型方法 我们能否为系统的对象的原型中添加方法, 相当于在改变源码   我希望字符串中有一个倒序字符串的方法 //我希望字符串中有一个倒序字符串的方法 String.prototype. ...

  8. 第七章:Javascript数组

    数组是值的有序结合.每个值叫做一个元素,而每个元素在数组中都有一个位置,用数字表示,称为索引. javascript数组是无类型的:数组的元素可以是任意类型,并且同一个数组中的不同元素也可能有不同的类 ...

  9. 《JavaScript 闯关记》之数组

    数组是值的有序集合.每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引. JavaScript 数组是无类型的,数组元素可以是任意类型,并且同一个数组中的不同元素也可能有不同的类型 ...

随机推荐

  1. 使用JDBC技术连接数据库(附源码)--JAVA的简单应用

    一.创建数据库(以mysql数据库为例) mysql数据库的下载安装与配置 -可参考博主之前的随笔:Windows平台下搭建MySQL数据库 创建wxb数据库-create database wxb; ...

  2. luogu P2756 飞行员配对方案问题

    题目链接:P2756 飞行员配对方案问题 题目描述 英国皇家空军从沦陷国征募了大量外籍飞行员.由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的2 名飞行员,其中1 名是英国飞行员,另 ...

  3. 学习笔记TF028:实现简单卷积网络

    载入MNIST数据集.创建默认Interactive Session. 初始化函数,权重制造随机噪声打破完全对称.截断正态分布噪声,标准差设0.1.ReLU,偏置加小正值(0.1),避免死亡节点(de ...

  4. (转)log4j(三)——如何控制不同级别的日志信息的输出?

    一:测试环境与log4j(一)——为什么要使用log4j?一样,这里不再重述 1 老规矩,先来个栗子,然后再聊聊感受 package test.log4j.test3; import org.apac ...

  5. (转)HTTP协议漫谈

    HTTP协议漫谈   简介 园子里已经有不少介绍HTTP的的好文章.对HTTP的一些细节介绍的比较好,所以本篇文章不会对HTTP的细节进行深究,而是从够高和更结构化的角度将HTTP协议的元素进行分类讲 ...

  6. HDU5744 Keep On Movin (思维题,水题)

    Problem Description Professor Zhang has kinds of characters and the quantity of the i-th character i ...

  7. 分布式事务,EventBus 解决方案:CAP【中文文档】

    前言 很多同学想对CAP的机制以及用法等想有一个详细的了解,所以花了将近两周时间写了这份中文的CAP文档,对 CAP 还不知道的同学可以先看一下这篇文章. 本文档为 CAP 文献(Wiki),本文献同 ...

  8. CLI子命令扩展-插件机制实现

    开发CLI工具过程中,为了便于扩展,将CLI的实现分为基础功能和扩展功能.基础功能包括init.build.lint.publish等伴随工程从初始化到最终发布到生产环境,也即为CLI 的core.扩 ...

  9. Django 1.9 admin 使用suit 小记

    最近项目做到了后台管理的部分.Django虽然提供了后台管理,但是界面不咋好看.所以我使用了suit.官网http://djangosuit.com/ 步骤: 1,安装suit 项目settings. ...

  10. Given two binary string, return their sum (also a binary string)

    主要思路:将二进制转化为十进制,然后进行十进制加法,最后再将加法所得的结果转化为二进制 public class BinarySum2 { public static void main(String ...