资讯专栏INFORMATION COLUMN

有了 indexOf,为什么 ECMAScript 7 还添加了 Array.prototype.i

alexnevsky / 2877人阅读

摘要:前端开发群原文发表于,转载请参阅转载授权。查询了和,委员会的提案,找到一些细节。前身早前的的提案名为,但由于有很多网站自行了其实主要是因为导致的,看起来就跟上面的代码类似。而为了和其他特性保持一致所以内部也采用了实现。

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。

具体 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

Bugzilla@Mozilla - non-enumerable Array.prototype.contains is not web-compatible (breaks jsfiddle.net)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85023.html

相关文章

  • 阿里云前端周刊 - 第 23 期

    摘要:写的姿势前两天去帝都参加第三届前端开发者大会,见了很多老朋友,也认识了很多新朋友。 推荐 1. 组件化设计思维 – 从规范到工具的构建与探索 http://www.zcool.com.cn/artic... 阿里巴巴在中台战略的背景下,设计提效又再次推动着设计思维的变革。设计师们不仅仅需要出色地完成业务需求的设计,同时还需要思考设计的价值,也就是经常提到的最佳方案性价比。我们需要在设计...

    wuyangnju 评论0 收藏0
  • 阿里云前端周刊 - 第 23 期

    摘要:写的姿势前两天去帝都参加第三届前端开发者大会,见了很多老朋友,也认识了很多新朋友。 推荐 1. 组件化设计思维 – 从规范到工具的构建与探索 http://www.zcool.com.cn/artic... 阿里巴巴在中台战略的背景下,设计提效又再次推动着设计思维的变革。设计师们不仅仅需要出色地完成业务需求的设计,同时还需要思考设计的价值,也就是经常提到的最佳方案性价比。我们需要在设计...

    FrancisSoung 评论0 收藏0
  • JavaScript基础拾遗

    摘要:由于网景公司希望能在静态页面上添加一些动态效果,于是叫这哥们在两周之内设计出了语言。所以简单说来就是,是一种语言标准,而是网景公司对标准的一种实现。 JavaScript基础拾遗 study notes by Tingting 为啥说JavaScript的基础 在平时开发时,我们更多的是在写PHP的逻辑层,但是在写后台时多多少少会写一写JavaScript的代码,有时候我们就会遇到对j...

    zhouzhou 评论0 收藏0
  • JavaScript "相等" 的二三事

    摘要:还规定了无穷及其它的相应规范,有兴趣可自行查找相关资料。其它相同数值相等。类型中,引用同一对象,相等。不同点对的判断上各有不同。以为代表的相等和相等以为代表的不相等和相等以为代表的相等和不相等相同类型采用严格比较。 相等不相等? 先来随便举几个?吧~ 0 == true //? [1] == [1] //? [1] == 1 ...

    wanghui 评论0 收藏0
  • 一次掌握 JavaScript ES5 到 ES8 数组内容

    摘要:可选到该位置前停止读取数据,默认等于数组长度。找出第一个符合条件的数组元素,参数是一个回调函数,所有数组元素依次执行该回调函数,直到找出第一个返回值为的元素,然后返回该元素。回调函数可以接受三个参数,依次为当前的值当前的位置和原数组。 ECMAScript 5.1 中提供的数组方法 ECMA-262/5.1 规范 判断是否是数组 Array.isArray ( arg ) // fal...

    baiy 评论0 收藏0

发表评论

0条评论

alexnevsky

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<