JS中集合对象(Array、Map、Set)及类数组对象的使用与对比
在使用js编程的时候,常常会用到集合对象,集合对象其实是一种泛型,在js中没有明确的规定其内元素的类型,但在强类型语言譬如Java中泛型强制要求指定类型。

ES6引入了iterable类型,Array,Map,Set都属于iterable类型,它们可以使用for…of循环来遍历,都内置forEach方法。

数组
遍历
普通遍历
最简单的一种,也是使用频率最高的一种。

let arr = ['a', 'b', 'c', 'd', 'e']
for (let i = 0; i < arr.length; i++) {
  console.log(i, ' => ', arr[i])
}
1
2
3
4
优化: 缓存数组长度:

let arr = ['a', 'b', 'c', 'd', 'e']
for (let i = 0, len = arr.length; i < len; i++) {
  console.log(i, ' => ', arr[i])
}
1
2
3
4
使用临时变量,将长度缓存起来,避免重复获取数组长度,当数组较大时优化效果才会比较明显。

for-in
这个循环很多人爱用,但实际上,经分析测试,在众多的循环遍历方式中它的效率是最低的。

let arr = ['a', 'b', 'c', 'd', 'e']
for (let i in arr) {
  console.log(i, ' => ', arr[i])
}
1
2
3
4
for-of
这种方式是es6里面用到的,性能要好于forin,但仍然比不上普通for循环。

let arr = ['a', 'b', 'c', 'd', 'e']
let index = 0
for (let item of arr) {
  console.log(index++, ' => ', item)
}
1
2
3
4
5
forEach
数组自带的foreach循环,使用频率较高,实际上性能比普通for循环弱。

let arr = ['a', 'b', 'c', 'd', 'e']
arr.forEach((v, k) => {
  console.log(k, ' => ', v)
})
1
2
3
4
forEach接受第三个参数,指向原数组,没有返回值,对其进行操作会改变原数组对象

let ary = [12, 23, 24, 42, 1]
let res = ary.forEach((item, index, input) => {
   input[index] = item * 10
})
console.log(res) //-->undefined
console.log(ary) //-->会对原来的数组产生改变
1
2
3
4
5
6
如果版本低的浏览器不兼容(IE8-),可以自定义方法实现:

/**
* forEach遍历数组
* @param callback [function] 回调函数;
* @param context [object] 上下文;
*/
Array.prototype.myForEach = function (callback,context) {
  context = context || window;
  if('forEach' in Array.prototype) {
    this.forEach(callback,context)
    return
  }
  // IE6-8下自己编写回调函数执行的逻辑
  for(let i = 0,len = this.length; i < len; i++) {
    callback && callback.call(context, this[i], i, this)
  }
}
let arr = [12, 23, 24, 42, 1]
arr.myForEach((v, k) => {
  console.log(k, ' => ', v)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
map
map会返回一个全新的数组,同样接受第三个参数,如果对其进行操作会改变原数组。

let ary = [12, 23, 24, 42, 1]
let res = ary.map((item, index, input) => {
   return item * 10
})
console.log(res) //-->[120,230,240,420,10]
console.log(ary) //-->[12,23,24,42,1]
1
2
3
4
5
6
如果版本低的浏览器不兼容(IE8-),可以自定义方法实现:

/**
* map遍历数组
* @param callback [function] 回调函数;
* @param context [object] 上下文;
*/
Array.prototype.myMap = function myMap(callback,context){
  context = context || window
  if('map' in Array.prototype) {
    return this.map(callback, context)
  }
  //IE6-8下自己编写回调函数执行的逻辑
  let newAry = []
  for(var i = 0,len = this.length; i < len; i++) {
    if(typeof callback === 'function') {
      var val = callback.call(context, this[i], i, this)
      newAry[newAry.length] = val
    }
  }
  return newAry
}
arr.myMap((v, k) => {
  console.log(k, ' => ', v)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
过滤
filter
对数组中的每个元素都执行一次指定的函数(callback),并且创建一个新的数组,该数组元素是所有回调函数执行时返回值为 true 的原数组元素。它只对数组中的非空元素执行指定的函数,没有赋值或者已经删除的元素将被忽略,同时,新创建的数组也不会包含这些元素。

let arr = [12, 5, 8, 130, 44]
let ret = arr.filter((el, index, array) => {
  return el > 10
})
console.log(ret) // [12, 130, 44]
1
2
3
4
5
map
map也可以作为过滤器使用,不过返回的是对原数组每项元素进行操作变换后的数组,而不是每项元素返回为true的元素集合。

let strings = ["hello", "Array", "WORLD"]
function makeUpperCase(v) {
  return v.toUpperCase()
}
let uppers = strings.map(makeUpperCase)
console.log(uppers) // ["HELLO", "ARRAY", "WORLD"]
1
2
3
4
5
6
some
对数组中的每个元素都执行一次指定的函数(callback),直到此函数返回 true,如果发现这个元素,some 将返回 true,如果回调函数对每个元素执行后都返回 false ,some 将返回 false。它只对数组中的非空元素执行指定的函数,没有赋值或者已经删除的元素将被忽略。

// 检查是否有数组元素大于等于10:
function isBigEnough(element, index, array) {
  return (element >= 10)
}
let passed1 = [2, 5, 8, 1, 4].some(isBigEnough) // passed1 is false
let passed2 = [12, 5, 8, 1, 4].some(isBigEnough) // passed2 is true
1
2
3
4
5
6
every
对数组中的每个元素都执行一次指定的函数(callback),直到此函数返回 false,如果发现这个元素,every 将返回 false,如果回调函数对每个元素执行后都返回 true ,every 将返回 true。它只对数组中的非空元素执行指定的函数,没有赋值或者已经删除的元素将被忽略。

// 测试是否所有数组元素都大于等于10:
function isBigEnough(element, index, array) {
  return (element >= 10)
}
let passed1 = [12, 5, 8, 1, 4].every(isBigEnough) // passed1 is false
let passed2 = [12, 15, 18, 11, 14].every(isBigEnough) // passed2 is true
1
2
3
4
5
6
排序
let arr = ['a', 1, 'b', 3, 'c', 2, 'd', 'e']
console.log(arr) // ["a", 1, "b", 3, "c", 2, "d", "e"]
console.log(arr.reverse()) // 反转元素(改变原数组) // ["e", "d", 2, "c", 3, "b", 1, "a"]
console.log(arr.sort()) // 对数组元素排序(改变原数组) // [1, 2, 3, "a", "b", "c", "d", "e"]
console.log(arr) // [1, 2, 3, "a", "b", "c", "d", "e"]
1
2
3
4
5
sort自定义排序
let arr = [1, 100, 52, 6, 88, 99]
let arr1 = arr.sort((a, b) => a-b) // 从小到大排序
console.log(arr1) // [1, 6, 52, 88, 99, 100]
let arr2 = arr.sort((a, b) => b-a) // 从大到小排序
console.log(arr2) // [100, 99, 88, 52, 6, 1]
console.log(arr) // 原数组也发生改变
1
2
3
4
5
6
搜索
let arr = [12, 5, 4, 8, 1, 4]
arr.indexOf(4) // 2,从前往后搜索,返回第一次搜索到的数组下标,搜索不到返回-1
arr.lastIndexOf(4) // 5,从后往前搜索,返回第一次搜索到的数组下标,搜索不到返回-1
arr.indexOf(0) // -1
1
2
3
4
增删、清空操作
添加元素
let arr = ['a', 'b', 'c', 'd', 'e']
arr.push(10, 11) // 模仿栈进行操作,往数组末尾添加一个或多个元素(改变原数组)
arr.unshift(0, 1) // 模仿队列进行操作,往数组前端添加一个或多个元素(改变原数组)
console.log(arr) // [0, 1, "a", "b", 'c', "d", "e", 10, 11]
1
2
3
4
删除元素
arr.pop() // 移除最后一个元素并返回该元素值(改变原数组)
arr.shift() // 移除最前一个元素并返回该元素值,数组中元素自动前移(改变原数组)
console.log(arr) // ["b", "c", "d"]
1
2
3
清空数组
将数组的length设置为0即可

let arr = ['a', 1, 'b', 3, 'c', 2, 'd', 'e']
arr.length = 0
1
2
length详解:

因为数组的索引总是由0开始,所以一个数组的上下限分别是:0和length-1;

当length属性被设置得更大时,整个数组的状态事实上不会发生变化,仅仅是length属性变大;

当length属性被设置得比原来小时,则原先数组中索引大于或等于length的元素的值全部被丢失。
splice
既可以删除也可以添加元素

let arr = ['a', 'b', 'c', 'd', 'e']
arr.splice(2, 1, 1,2,3)
console.log(arr) // ["a", "b", 1, 2, 3, "d", "e"]
1
2
3
splice(start, len, elems) : 删除并添加元素(改变原数组)

start: 起始位置
len: 删除元素的长度
elems: 添加的元素队列
几种形式:
splice(start, 0, elems) : 从start位置添加元素
splice(start, len) : 从start位置删除len个元素
截取、合并与拷贝
let arr = ['a', 'b', 'c', 'd', 'e']
let arr1 = arr.slice(1, 2) // 以数组的形式返回数组的一部分,注意不包括 end 对应的元素,如果省略 end 将复制 start 之后的所有元素(返回新数组)
let arr2 = arr.concat([1,2,3]); // 将多个数组(也可以是字符串,或者是数组和字符串的混合)连接为一个数组(返回新数组)
console.log(arr) // ["a", "b", "c", "d", "e"]
console.log(arr1) // ["b"]
console.log(arr2) // ["a", "b", "c", "d", "e", 1, 2, 3]
1
2
3
4
5
6
其实slice和concat也可以作为数组的拷贝方法:

arr.slice(0) // 返回数组的拷贝数组,注意是一个新的数组,不是指向
arr.concat() // 返回数组的拷贝数组,注意是一个新的数组,不是指向
1
2
Map
Map是一组键值对的结构,具有极快的查找速度。

创建
方法一: 创建的时候初始化

let mapObj = new Map([
  ['a', 1],
  ['b', 2],
  ['c', 3]
])
console.log(mapObj.size) // 3
1
2
3
4
5
6
方法二: 创建空Map,之后添加元素

let mapObj = new Map()
mapObj.set('a', 1)
mapObj.set('b', 2)
mapObj.set('c', 3)
console.log(mapObj.size) // 3
1
2
3
4
5
注意: Map对象的长度不是length,而是size

基础操作
Map对象的创建、添加元素、删除元素…

mapObj.set('a', 1) // 添加元素
mapObj.delete('d') // 删除指定元素
mapObj.has('a') // true
mapObj.get('a') // 1
1
2
3
4
遍历
使用上面创建的Map进行操作

forEach
同数组的forEach遍历,三个参数分别代表: value、key、map本身

mapObj.forEach((e, index, self) => {
  console.log(index, ' => ', e)
})
1
2
3
打印出:

a  =>  1
b  =>  2
c  =>  3
1
2
3
for-of
for (const e of mapObj) {
  console.log(e)
}
1
2
3
打印出:

["a", 1]
["b", 2]
["c", 3]
1
2
3
注意: for-of遍历出来的是一个数组,其中e[0]为key,e[1]为value

Set
Set和Map类似,但set只存储key,且key不重复。

创建
方法一: 创建的时候初始化

let setObj = new Set([1, 2, 3])
console.log(setObj.size)
1
2
方法二: 创建空Map,之后添加元素

let setObj = new Set()
setObj.add(1)
setObj.add(2)
setObj.add(3)
console.log(setObj.size)
1
2
3
4
5
注意: Map对象的长度不是length,而是size

基础操作
let s = new Set([1, 2, 3])
s.add(3) // 由于key重复,添加不进
s.delete(3) // 删除指定key
console.log(s) // 1 2
1
2
3
4
遍历
使用上面创建的Set进行操作

forEach
Set与Array类似,但Set没有索引,因此回调函数的前两个参数都是元素本身

s1.forEach(function (element, sameElement, set) {
  console.log(element) // element === sameElement
})
// 打印 1 2 3
1
2
3
4
for-of
for (const item of s1) {
  console.log(item)
}
// 打印 1 2 3
1
2
3
4
类数组对象
JavaScript中,数组是一个特殊的对象,其property名为正整数,且其length属性会随着数组成员的增减而发生变化,同时又从Array构造函数中继承了一些用于进行数组操作的方法。而对于一个普通的对象来说:

如果它的所有property名均为正整数,同时也有相应的length属性,那么虽然该对象并不是由Array构造函数所创建的,它依然呈现出数组的行为,在这种情况下,这些对象被称为“类数组对象”。

形如:

let obj = {
  0: 'qzy',
  1: 22,
  2: false,
  length: 3
}
1
2
3
4
5
6
类数组对象可以使用Array对象原生方法进行操作。

遍历
沿用上述对象进行操作

forEach
Array.prototype.forEach.call(obj, function(el, index){  
   console.log(index, ' => ', el)
})
1
2
3
map
Array.prototype.map.call(obj, function(el, index){  
   console.log(index, ' => ', el)
})
1
2
3
注意: 类数组对象不支持使用for-of进行遍历,否则会报错: [Symbol.iterator] is not a function

增删截取操作
沿用上述对象进行操作

Array.prototype.join.call(obj, '-') // qzy-22-false
Array.prototype.slice.call(obj, 1, 2) // [22]
Array.prototype.push.call(obj, 5) // Object {0: "qzy", 1: 22, 2: false, 3: 5, length: 4}
1
2
3
String也是一个类数组对象
由于字符串对象也存在length,且序号从0开始增加,因此字符串也可以看做一个只读的类数组对象,这意味着String对象可以使用Array的所有原型方法。

let str = 'hello world'
console.log(Array.prototype.slice.call(str, 0, 5)) // ["h", "e", "l", "l", "o"]
1
2
String也可以使用for-of进行遍历
let str = 'hello world'
for (const s of str) {
  console.log(s)
}
1
2
3
4
String独有方法
除了使用Array原型对象的方法,String还包含其他一些自己独有的方法:

与Array使用方法相同的方法

搜索: indexOf()、lastIndexOf()、concat()

转换
toLowerCase()、toUpperCase()

截取
substr(start, len)

substring(start, end)

slice(start, end)

let str = 'hello world'
let ret1 = str.substr(6, 5) // "world"
let ret2 = str.substring(6, 11) // "world"
let ret3 = str.slice(3, 8) // "lo wo"
1
2
3
4
substring 是以两个参数中较小一个作为起始位置,较大的参数作为结束位置。

slice 是第一参数为起始位置,第二参数为结束位置,如果结束位置小于起始位置返回空字符串

console.log(str.substring(11, 6) === str.substring(6, 11)) // true
1
接收负数为参数时:

slice会将它字符串的长度与对应的负数相加,结果作为参数;
substr则仅仅是将第一个参数与字符串长度相加后的结果作为第一个参数;
substring则干脆将负参数都直接转换为0。
let str = 'hello world'
let ret1 = str.substr(-5) // "world"
let ret2 = str.substr(-5, 3) // "wor"
let ret3 = str.substring(6, -1) // "hello"
let ret4 = str.slice(6, -1) // "worl"

console.log(ret1 === str.substr(str.length - 5)) // true
console.log(ret2 === str.substr(str.length - 5, 3)) // true
console.log(ret3 === str.substring(6, 0)) // true
console.log(ret4 === str.slice(6, str.length - 1)) // true
1
2
3
4
5
6
7
8
9
10
正则
match()
replace()
search()
let str = 'hello world'
let ret0 = str.match(/r/) // 非全局搜索,返回匹配的第一个字符串数组(length为1),包括index和input
let ret1 = str.match(/o/g) // 全局搜索,返回匹配的字符串数组,length为搜索到的匹配正则表达式的长度
let ret2 = str.replace(/o/g, 'e') // 全局替换
let ret3 = str.replace(/O/i, 'e') // 不区分大小写,只替换搜索到的第一个字串
let ret4 = str.search(/l/) // 返回搜索到的第一个匹配字串的索引
let ret5 = str.search(/l/g) // 全局无效,同上

console.log(ret0) // ["r", index: 8, input: "hello world"]
console.log(ret1) // ["o", "o"]
console.log(ret2) // "helle werld"
console.log(ret3) // "helle world"
console.log(ret4) // 2
console.log(ret5) // 2
console.log(str) // 不改变源字符串 'hello world'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
转化
Map => Object
let mapObj = new Map([ ['a', 1], ['b', 2], ['c', 3] ])

let obj = {}
for (const item of mapObj) {
  obj[item[0]] = item[1]
}

console.log(obj)
1
2
3
4
5
6
7
8
Set => Array
let setObj = new Set([1, 2, 3])

let arr = []
for (const item of setObj) {
  arr.push(item)
}

console.log(arr)
1
2
3
4
5
6
7
8
Array => String
arr.join(separator)

['a', 'b', 'c', 'd', 'e'].join('')
// toLocaleString 、toString 、valueOf:可以看作是join的特殊用法,不常用
1
2
String => Array
str.split(separator)

'hello world'.split(' ') // ["hello", "world"]
1
参考资料
————————————————
版权声明:本文为CSDN博主「xiaoxiao昱」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014302759/article/details/78650131

JS中集合对象(Array、Map、Set)及类数组对象的使用与对比相关推荐

  1. JS 中的类数组对象如何转换为数组?

    大家好,我是前端西瓜哥,今天说一下 JS 的类数组对象是什么,以及如何将类数组对象转为数组. 类数组对象是什么? 类数组对象,就是含有 length 属性的对象,但这个对象不是数组. 通常来说还会有 ...

  2. JS 函数中的 arguments 类数组对象

    1. arguments 介绍 2. arguments 转为数组 3. 箭头函数中没有 arguments 1. arguments 介绍 众所周知,js 是一门非常灵活的语言.当我们在 js 中调 ...

  3. 如何把类数组对象转为数组对象

    目录 一.数组 二.对象 三.类数组对象 3.1 什么是类数组对象 3.2 js中哪些是类数组对象 3.3 类数组对象怎么转化为数组 1.Array.from()(ES6+) 2.slice 3.sp ...

  4. 什么叫做类数组对象?

    JavaScript中,数组是一个特殊的对象,其property名为正整数,且其length属性会随着数组成员的增减而发生变化,同时又从Array构造函数中继承了一些用于进行数组操作的方法.而对于一个 ...

  5. js之数组,对象,类数组对象

    2019独角兽企业重金招聘Python工程师标准>>> 许久不写了,实在是不知道写点什么,正好最近有个同事问了个问题,关于数组,对象和类数组的,仔细说起来都是基础,其实都没什么好讲的 ...

  6. js基础(数组)--数组类型、类数组对象、作为数组的字符串

    1.数组类型 我们到处都可以看见数组是具有特殊行为的对象.给定一个未知的对象,判定它是否为数组通常非常 有用.在ECMAScript 5中,可以使用Array.isArray()函数来做这件事情:Ar ...

  7. python中组合与继承的区别_python类与对象的组合与继承

    1.把类的对象实例化放到一个新的类里面叫做类的组合,组合就是指几个横向关系的类放在一起,纵向关系的类放在一起是继承,根据实际应用场景确定.简单的说,组合用于"有一个"的场景中,继承 ...

  8. Python学习 Day31 JS类数组对象

    JS类数组对象 一.三元运算符 概述:三元运算符由三部分组成,使用语法如下: A ? B : C 三元运算符A:它需要的是一个布尔值(其他类型数值也可以,但是需要隐式转换为布尔值) 如果A部分为布尔值 ...

  9. C++模板学习02(类模板)(类模板语法、类模板与函数模板的区别、类模板中的成员函数创建时机、类模板对象做函数参数、类模板与继承、类模板成员函数类外实现、类模板分文件编写、类模板与友元)

    C++引用详情(引用的基本语法,注意事项,做函数的参数以及引用的本质,常量引用) 函数高级C++(函数的默认参数,函数的占位参数,函数重载的基本语法以及注意事项) C++类和对象-封装(属性和行为作为 ...

最新文章

  1. 洛谷P4382 劈配
  2. 4.Boost之ref
  3. 【STM32】程序下载(串口方式)
  4. Linux 重启php
  5. opsforlist 存在贼覆盖_RedisTemplate集合使用说明-opsForList(二)
  6. BiANet:用于快速高效实现RGB-D数据显著性目标检测的双边注意力模型
  7. linux 下安装wps
  8. 程序员必备的书籍有哪些?
  9. JS遍历map集合以及map对象
  10. 电子厂计算机维修周记,电子厂实习周记【三篇】【完整版】
  11. 爪哇国新游记之七----使用ArrayList统计水果出现次数
  12. 批处理之批量修改文件扩展名
  13. Qt播放HTML网页视频
  14. 玩转华为ENSP模拟器系列 | 配置OSPF的DR选择示例
  15. 厉害了!华为将发布国产编程语言,打破国外垄断!
  16. 程序员上下班途中都在想什么
  17. Flume拦截器实战案例
  18. 【RF】Receiver Function接收函数
  19. 我不会编程,但也不是一点都不会, 我稍微会一些
  20. p8z java快速命名工具

热门文章

  1. python不小心用关键字做了变量名,怎么改回来
  2. 职高计算机教学案例 反思,关于职高数学优质课教学案例的研究与反思
  3. js截屏 video_js获取video任意时间的画面截图
  4. 阿里云物模型层功能分析
  5. 变参模板、完美转发和emplace
  6. python 常见函数_Python基础函数:初学者常用的十个Python函数,非常全面!
  7. 荧光皮肤有哪些_价格适中又显白的口红有哪些?MAC占大头,KIKO卡拉泡泡超平价...
  8. 4che3 scu发送超时设置_Redis实现订阅发布与批量发送短信
  9. install onnx_tensort
  10. 可视化应用实战案例:metacoder-相关进化树图的绘制