有了 indexOf,为什么 ECMAScript 7 还添加了 Array.prototype.include
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():返回数组中满足提供的测试函数的第一个元素的索引。否则返回 -1Array.prototype.find():返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefinedArray.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: ƒ() }
而这些网站自行 hack 的 contains() 是可以被枚举的,也就是可以通过 for..in 读出来。
发现问题了么?
如果规范实现 contains(),会导致 contains() 无法被 for..in 读出来,而之前自行 hack 的 contains() 是可以被读出来的,所以会出现代码没变动,但是在新规范推出后会产生 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():没有直接暴露的接口,内部实现接口是
Map与Setconst 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 实现规范。
参考和引用
- tc39 - Array.prototype.includes Proposal
- Having a non-enumerable Array.prototype.contains may not be web-compatible
- ECMAScript® 2016 Language Specification - Array.prototype.includes
- ECMAScript® 2015 Language Specification - SameValueZero
- stackoverflow - How do I check if an array includes an object in JavaScript?
- [http://2ality.com/2016/02/array-prototype-includes.html](ES2016 feature: Array.prototype.includes)
- Bugzilla@Mozilla - non-enumerable Array.prototype.contains is not web-compatible (breaks jsfiddle.net)
有了 indexOf,为什么 ECMAScript 7 还添加了 Array.prototype.include的更多相关文章
- 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 ...
- [基础] Array.prototype.indexOf()查询方式
背景 最近在看Redux源码,createStore用于注册一个全局store,其内部维护一个Listeren数组,存放state变化时所有的响应函数. 其中store.subscribe(liste ...
- 使用Array.prototype.indexOf()的几点注意
对应indexOf这个方法,在日常开发中比较常见的应该是String.prototype.indexOf()方法,Array.prototype.indexOf()方法和其有很大的相似性,本文不想去描 ...
- Array.prototype.indexOf
arr.indexOf(searchElement[, fromIndex = 0]) Array.prototype.indexOf()
- 利用Array Prototype的方法来实现对dom集合的筛选、indexOf、map等功能
<!DOCTYPE html><html> <head> <title>TODO supply a title</title> <me ...
- 数组Array和字符串String的indexOf方法,以及ES7(ES2016)中新增的Array.prototype.includes方法
前言 我们在判断某一个字符是否存在于一个字符串中或者某一个值是否存在于一个数组中时,ES7之前我们需要使用indexOf,ES7引入了新的方法includes 语法 数组:Array.inexOf(s ...
- JS高级---为内置对象添加原型方法
为内置对象添加原型方法 我们能否为系统的对象的原型中添加方法, 相当于在改变源码 我希望字符串中有一个倒序字符串的方法 //我希望字符串中有一个倒序字符串的方法 String.prototype. ...
- 第七章:Javascript数组
数组是值的有序结合.每个值叫做一个元素,而每个元素在数组中都有一个位置,用数字表示,称为索引. javascript数组是无类型的:数组的元素可以是任意类型,并且同一个数组中的不同元素也可能有不同的类 ...
- 《JavaScript 闯关记》之数组
数组是值的有序集合.每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引. JavaScript 数组是无类型的,数组元素可以是任意类型,并且同一个数组中的不同元素也可能有不同的类型 ...
随机推荐
- Java 接口-抽象类解析
对于面向对象编程,抽象是它的三大特征(抽象.继承.多态)之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类. 这两者既相似又存异.诸位在初学的时候也会傻傻分不清接口与抽象类的区别,大 ...
- 各种demo:css实现三角形,css大小梯形,svg使用
各种demo: 1.css实现正方形 思路:width为0:height为0:使用boder-width为正方形的边长的一半,不占任何字节:border-style为固体:border-color为正 ...
- (转)用JUnit4进行单元测试
场景:从开始写代码至今,对于单元测试一直没有重视,但是也厌倦了了程序中的额System.out和log日志输出.单元测试使我看到了在开发过程中的安全性和便捷性,所以下决心好好整理下. 有感而发——&l ...
- HDU - 3697 Selecting courses
题目链接:https://vjudge.net/problem/HDU-3697 题目大意:选课,给出每门课可以的选课时间.自开始选课开始每过五分钟可以选一门课,开始 时间必须小于等于四,问最多可以选 ...
- app耗电优化之四 使用AlarmManager对任务进行合理安排
AlarmManager 是用来设定定时任务.即用来设定那个任务在什么时候开始执行.为什么和省电有关系?这个需要和AlarmManager的使用先说起.AlarmManager 实际上只起到一个定时发 ...
- 基于Vivado调用ROM IP core设计DDS
DDS直接数字式频率合成器(Direct Digital Synthesizer) 下面是使用MATLAB生成正弦波.三角波.方波的代码,直接使用即可. t=:*pi/^:*pi y=0.5*sin ...
- JS组件系列——在ABP中封装BootstrapTable
前言:关于ABP框架,博主关注差不多有两年了吧,一直迟迟没有尝试.一方面博主觉得像这种复杂的开发框架肯定有它的过人之处,系统的稳定性和健壮性比一般的开源框架肯定强很多,可是另一方面每每想到它繁琐的封装 ...
- 初学Python(九)——函数
初学Python(九)--函数 初学Python,主要整理一些学习到的知识点,这次是函数. 函数定义: # -*- coding:utf-8 -*- #函数的定义 def my_function(x) ...
- Linux(7)chmod解析
在UNIX和Linux的操作系统中, 每个文件(文件夹也被看作是文件)都按读, 写, 运行设定权限 比如用ls -l或ll命令列文件表时, 得到如下输出: -rw-r--r-- 1 apple use ...
- metasploit快速入门
今天没上班,在小黑屋里看了一个一百多页的书<metasploit新手指南>,在此将笔记分享给大家.欢迎大家批评指正,共同学习进步. metasploit新手指南 笔记 kali 0 ...