前端记录 记录分享前端知识

撸js基础之数组

前言

前端这两年的新技术铺天盖地,各种框架、工具层出不穷眼花缭乱。最近打算好好复习下 js 基础,夯实的基础才是学习新技术的基石。本文作为读书笔记简单的总结下 js 数组的基础知识。

本系列另外还有一篇:撸js基础之对象,各位有兴趣的可以看下。

本文日后还会更新修改,如若转载请附上原文地址:http://www.ferecord.com/lujs-array.html,以便溯源。

数组基础

简介

数组是应用最广泛的数据存储结构。它被植入到大部分编程语言中。在 ECMAScript 中数组是非常常用的引用类型。

属性

length
length属性表示数组的长度,即其中元素的个数。
JavaScript数组的length属性是可变的,当length属性被设置得更大时,整个数组的状态事实上不会发生变化,仅仅是length属性变大;当length属性被设置得比原来小时,则原先数组中索引大于或等于length的元素的值全部被丢失。

prototype
返回对象类型原型的引用。prototype 属性是 object 共有的。
一般用来给数组实例添加方法。

constructor
表示创建对象的函数。
说明:constructor 属性是所有具有 prototype 的对象的成员。constructor 属性保存了对构造特定对象实例的函数的引用。

基本操作

创建数组

检测数组

数组方法 isArray, from, of

Array.isArray(obj)
检测对象是否 Array ,是则返回true,否则为false。

Array.from(arrayLike, mapFn, thisArg)
该方法从一个类似数组或可迭代对象创建一个新的数组实例。参数 arrayLike 是想要转换成真实数组的类数组对象或可遍历对象。mapFn 是可选参数,如果指定了该参数,则最后生成的数组会经过该函数的加工处理后再返回。thisArg是可选参数,为执行 mapFn 函数时 this 的值。

所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组。
实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的 arguments 对象。

Array.of(item…)
该方法用于创建数组实例。该方法用于替代 Array() 或 new Array()。Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个包含 7 个 undefined 元素的数组。

数组实例方法

转换方法 join

arr.join(separator)
把数组构构造成字符串,它先把数组中的每个元素转换成字符串,然后再用 separator 分隔符把它们链接在一起,separator 分隔符默认是逗号 “,”,要想做到无间隔链接,可以使用空字符串作为 separator:

栈方法 push, pop

arr.push(item…)
将一个或多个新元素添加到数组结尾,并返回数组新长度。

arr.pop()
移除最后一个元素并返回该元素值。

队列方法 unshift, shift

arr.unshift(item…)
将一个或多个新元素添加到数组开始,数组中的元素自动后移,返回数组新长度。

arr.shift()
移除最前一个元素并返回该元素值,数组中元素自动前移.如果这个数组是空的,它会返回 undefined。shift 通常比 pop 慢的多。

重排序方法 reverse, sort

arr.reverse()
反转数组的顺序。

arr.sort(comparefn)
给数组排序,默认升序。

注意 sort 默认会将数组内容视为字符串来排序,所以对数字排序时默认的排序规则会错的离谱。一般我们给sort带入个比较函数来替代原来的默认的比较方法,比较方法接受两个参数,如果两个参数相等则返回0,如果第一个参数应该排在前面则返回一个负数,如果第二个参数应该排在前面则返回一个正数:

操作方法 concat, slice, splice, copyWithin, fill

arr.concat(item…)
该方法产生一份 arr 的潜复制,并将多个数组(也可以是字符串,或者是数组和字符串的混合)附加在其后连接为一个数组,返回连接好的新的数组。

arr.slice(start, end)
该方法对数组中的一段做浅复制,首先复制数组 arr[start] 至 arr[end] 的部分,注意不包括 end 对应的元素,如果省略 end 将复制 start 之后的所有元素(或者理解成 end 的默认值为 arr.length)。字符串也有个同名方法 string.slice。

arr.splice(start, deleteCount, item…)
该方法从 arr 中移除一个或多个元素,并将新的 item 插入至移除元素的开始位置, 参数 start 是移除元素的开始位置,deleteCount 是要移除的元素的个数,item 是要被插入的元素。它返回一个包含被移除元素的数组。

arr.copyWithin(target, start, end)
该方法复制数组的一部分到同一数组中的另一个位置(会覆盖原成员),并返回修改后的数组。使用这个方法,会修改当前数组。参数 target 为开始替换数据的位置,若 target 大于等于 arr.length,将会不发生拷贝。start 是可选参数,为开始读取数据的位置,默认为0。end 是可选参数,为停止读取数据的位置,默认为 arr.length。

上面代码表示将从3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,结果覆盖了原来的1和2。

arr.fill(value, start, end)
该方法使用给定值填充一个数组,参数 value 是用来填充数组的值。start 是可选参数,为填充开始位置,默认为 0。end 是可选参数,为填充的结束位置,默认为 arr.length。

fill 方法是个可变方法, 它会改变调用它的 this 对象本身, 然后返回它, 而并不是返回一个副本。

位置方法 indexOf, lastIndexOf, includes

arr.indexOf(searchElement, start)
该方法返回要查找的项在数组中的位置,如果没找到返回 -1。接受两个参数,searchElement 是要查找的项,start 是查找起始位置的索引,默认是0。

arr.lastIndexOf(searchElement, start)
从 start 位置开始向前查找,start 默认值为 arr.length - 1。

注意该方法在比较查找项与数组中每一项时,会使用全等操作符,也就是要求查找的项必须严格相等。

arr.includes(searchElement, fromIndex)
该方法用来判断当前数组是否包含某指定的值,如果是,则返回 true,否则返回 false。参数 searchElement 为需要查找的元素值。参数 fromIndex 是可选参数,从该索引处开始查找 searchElement,如果为负值,则按升序从 array.length + fromIndex 的索引开始搜索,默认为 0。 如果fromIndex 大于等于数组长度 ,则返回 false 。该数组不会被搜索。
该方法属于ES7,但Babel转码器已经支持。

没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。

indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相当运算符(===)进行判断,这会导致对NaN的误判。includes使用的是不一样的判断算法,就没有这个问题。

includes() 方法有意设计为通用方法。它不要求this值是数组对象,所以它可以被用于其他类型的对象 (比如类数组对象)。

迭代方法 every, some, filter, forEach, map, find, findIndex, entries, keys, values

arr.every(callback,thisArg)
对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true。callback 被调用时传入三个参数:元素值,元素的索引,原数组。thisArg 为可选参数,指定执行 callback 时使用的 this 值。

arr.some(callback, thisArg)
对数组中的每一项运行给定函数,如果该函数对任意一项返回 true,则返回 true。

arr.filter(callback, thisArg)
对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。

arr.forEach(callback)
对数组中的每一项运行给定函数,这个方法没有返回值。本质上与使用 for 循环迭代数组一样。

arr.map(callback)
对数组中的每一项运行给定函数,返回每次函数调用组成的数组。

arr.find(callback, thisArg)
该方法对数组所有成员依次执行 callback 函数,直到找出第一个返回值为 true 的成员,然后返回该成员。如果没有符合条件的成员,则返回 undefined。

find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组

arr.findIndex(callback, thisArg)
该方法与 arr.find() 类似,对数组中的成员依次执行 callback 函数,直至照吃第一个返回值为 true 的成员,然后返回该成员的索引。如果没有符合条件的成员,则返回 -1。

arr.entries()
arr.keys()
arr.values()

这三个方法都返回一个新的Array Iterator对象,可以用for...of循环进行遍历,区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

归并方法 reduce, reduceRight

arr.reduce(callback, initialValue)
arr.reduceRight(callback, initialValue)

这个两个方法都会迭代数组所有的项,然后返回一个最终值。reduce()方法从数组的第一项开始,逐个遍历到最后,而reduceRight()则从数组的最后一项开始,向前遍历到第一项。它们接受两个参数,callback 每一项上调用的函数,callback 被调用时传入四个参数:上一次调用回调返回的值、正在处理的元素、正在处理的元素的索引值(如果提供了 initialValue ,从0开始;否则从1开始)、原数组。initialValue 是可选项,作为归并基础的初始值,其值用于第一次调用 callback 的第一个参数。


数组常见问题练习

数组合并

多个一维数组合并

如有两个一维数组,要将他们合并成一个数组:

Array.prototype.concat()方法

for循环和Array.prototype.push()

reduce()或reduceRight()方法

多维(复合)数组合并成一维数组

例如有这么一组多维数组,要把它拍平成1维数组:

Array.prototype.push()方法

Array.prototype.concat()方法

可以通过Function.prototype.apply()方法,让其变得更简单:

还可以将上面方法做个精简:

在 ES6 中,还可以这样写:

如果要处理三维或更多维数组,则需要加一些判断:

或者:
function flatten(arr) {
var isArray = Object.prototype.toString.call(arr) == ‘[object Array]’;

}

使用join()和split()
利用数组实例的join()方法和字符串实例的split()方法,也可以将多维数组拍平:

这种方法的缺点在于返回的数组内全是字符串,如果不需要校验内容类型的话,可以使用这种方法。


数组去重

双重循环去重

思路:

  • 构建一个空数组来存放去重后的数组
  • 对原数组做循环遍历,每次从数组中取出一个元素与结果数组做对比
  • 如果原数组取出的元素与结果数组元素相同,则跳出循环;反之则将其存放到结果数组

这种方法也可采用forEach()方法和indexOf()方法模拟实现:

排序遍历去重

思路:

  • 构建一个空数组来存放去重后的数组
  • sort()方法对原数组做一个排序,排完序后对数组做遍历,检查数组中第 i 个元素与结果数组中最后一个元素是否相同,如果不同,则放到结果数组中。

这种方法有两个特色:

  • 去重后的数组会做排序,主要是因为原数在去重前做了排序
  • 去重后的数组,与数字相同的数字字符无法区分,比如‘1’和1

对象键值对法

思路:

  • 创建一个 js 对象及新数组
  • 遍历原数组,每次取出一个元素与 js 对象的键作对比
  • 如果不包含,将存入对象的元素的值推入到结果数组中,并将 object 对象中该属性名的值设为1

这种方法比较耗内存,但运行下来耗时最少,是较为优秀的方案。

使用 ES6 的 Set 和 Map 方法


数组随机排序

递归的方法

思路:
不断从原数组中随机取一个元素放进新数组,同时删除原数组中该值,递归重复至全部取出。

使用数组 sort 方法对数组元素随机排序(效果不佳)

思路:
使用srot()每次比较时返回一个随机值,让数组内元素依次前后随机排序

这个方法虽然实现了随机排序,但由于sort()方法是依次比较的,所以每个元素被随机到原位置后面的概率会越来越低。

随机交换数组内的元素 (原理from underscore.js)

思路:
遍历数组,每次从i位置后元素随机挑一个放到i位置,将原i位置元素放至被挑元素原位置

随机从原数组抽取一个元素,加入到新数组

思路:
遍历数组,每次从数组中随机挑一个元素(随机数最大值为原数组剩余长度),将该元素拿出来放入新数组。


取数组中最大值最小值

遍历比较方法

思路:

  • 设一个变量存放最大值,将数组中第一个值赋值给该变量
  • 遍历数组与最大值变量比较,如果大于最大值,则将该值赋值最大值变量
  • 遍历结束后,变量里储存的就是数组里的最大值

归并比较方法

思路:

  • 使用数组实例的 reduce(function(prev, curv, index, arr)) 方法
  • 依次比较回调函数中参数 prev 与 curv 的大小,返回大的那个

利用 Math.max() 与 Math.min() 方法

思路:
将数组转换成参数传进Math.max()Math.min()


将一个数按大小顺序插入数组并返回索引值

任务:
构建一个函数where(arr, num),函数接受两个参数,一个是数组arr,一个是要插进数组的数字num,函数将num按大小顺序插进arr,并返回num的索引值。例如:

使用 push + sort + indexOf 方法

思路:

  • 通过push()unshift()方法将 num 插入 arr
  • 使用sort()对 arr 进行排序
  • 使用indexOf()找出 num 在 arr 中的 index(或使用for、forEach、some循环)
  • 返回 index

或者:

使用 sort + findIndex + splice 方法

思路:

  • 使用sort()对 arr 进行排序
  • 使用 findIndex() 找出 arr 中第一个大于 num 的元素的位置
  • 使用 splice() 将 num 插入该元素前


从数组中寻找元素并删除元素

任务:
构建一个函数destroyer(arr, item…),传入数组与要删除的指定值,返回删除指定值后的数组,例如:

arguments + indexOf + filter 遍历判断法

思路:

  • 通过 arguements 对象分别获取到数组 arr 和指定要删除的元素,将指定元素转换成数组 removeArgs
  • 使用filter()筛选出 arr 内与 removeArgs 内不相同的元素,筛选规则通过indexOf()方法判断

slice(或解构运算符) + es6 set + filter 方法

思路:

  • 利用call()对 arguments 使用数组的slice()分别获取到原数组和指定值
  • 将指定值传进 new Set 对象
  • 使用filter()筛选出 arr 内与 set 对象内不相同的元素,筛选规则通过 set 对象的has()方法判断

也可以写成:


引用列表

本文内容主要参考摘录网站及数目为以下列表,感谢。

MDN 的 JavaScript 标准库 Array 部分
W3cplus 博客
《JavaScript高级程序设计(第3版)》
《JavaScript语言精髓(修订版)》
《ECMAScript 6 入门》
《高性能JavaScript(第1版)》

5 条评论

欢迎留言