javascript中数组的方法

179 阅读14分钟

数组的方法分为对象继承方法 、数组转换方法、栈和队列方法、数组排序方法、数组拼接方法、创建子数组方法、数组删改方法、数组位置方法、数组归并方法、数组迭代方法、es6数组方法

对象继承方法

数组是一种特殊的对象,继承了对象的Object的toString()、toLocaleString()和valueOf方法

toString()

toString()方法返回一个字符串,其中包含所有数组值,并用逗号分隔。

  let a = ['a', 'b', 'c']
  let b = a.toString()
  console.log(b) // a,b,c

toLocaleString()

toLocaleString()是toString()方法的本地化版本,toLocaleString()跟toString()使用方法一致,不同的是

var person1 = {
    toLocaleString: function(){
        return 'Nikolaos';
    },
    toString: function(){
        return 'Nicholas';
    }
};
var person2 = {
    toLocaleString: function(){
        return 'Grigorios';
    },
    toString: function(){
        return 'Greg';
    }
};
var people = [person1,person2];
console.log(people.toString());//'Nicholas,Greg'
console.log(people.toLocaleString());//'Nikolaos,Grigorios'

延伸:字符串的toString()和toLocaleString()的区别

  let c = new Date()
  console.log(c.toLocaleString(),'--',c.toString())
  //2020/5/7 下午4:24:35 -- Thu May 07 2020 16:24:35 GMT+0800 (中国标准时间)     
  var d=1234
  console.log(d.toString())
  // "1234"
  console.log(d.toLocaleString())
  // "1,234"

1.当数字是四位数及以上时,有区别,区别看以上代码 

2.当目标是标准时间格式时,用以上两种方法是有区别的,区别看以上

valueOf()

valueOf()方法返回数组对象本身

var a = [1, 2, 3];
console.log(a.valueOf());// [1, 2, 3]
console.log(a.valueOf() instanceof Array);//true

数组转换方法

join()

 Array.join()方法是String.split()方法的逆向操作,后者是将字符串分割成若干块来创建一个数组   

join()方法可以使用不同的分隔符来构建这个字符串。 元素将由指定的分隔符分隔。默认的分隔符是逗号(,)

   var a = [1,2,3]
  console.log(a.join())   // 1,2,3
  console.log(a.join(' ')) // 1 2 3

若某一项是null或者undefined,则在返回的结果以空字符串表示

  var a = [1,2,null, 3, undefined]
  console.log(a.join())   // 1,2,,3,

也可以用在类数组对象上

var obj = { 0: 'a', 1: 'b', length: 2 };
console.log(Array.prototype.join.call(obj, '-'));// 'a-b'

若对象没有length属性,就不是类数组,也就不能调用数组的方法

栈和队列方法

push()和pop()方法允许将数组当作栈来使用。unshift()和shift()方法的行为非常类似于push()和pop(),不一样的是前者是在数组的头部而非尾部进行元素的插入和删除操作   

栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除。而栈中项的插入(叫做推入)和移除(叫做弹出),只发生在一个位置——栈的顶部。javascript为数组专门提供了push()和pop()方法,以便实现类似栈的行为   

队列数据结构的访问规则是FIFO(First-In-First-Out,先进先出)。队列在列表的末端添加项,从列表的前端移除项。结合使用shift()和push()方法,可以像使用队列一样使用数组

push()方法可以接受任意数量的参数,把它们逐个添加到数组的末尾,并返回修改后数组的长度,是改数据会改变原数组

var a = []
console.log(a, a.push(1))  //[1] 1
console.log(a, a.push('a'))  //[1, "a"] 2

pop()方法删除数组的最后一个元素,然后返回该元素。更改原数组

对空数组使用pop()方法,不会报错,而是返回undefined

var a = []
console.log(a, a.push(1))  //[1] 1
console.log(a, a.push('a'))  //[1, "a"] 2
console.log(a, a.pop()) // [1] 'a'
console.log(a, a.pop()) // [] 1
console.log(a, a.pop()) // [] undefined

shift()方法移除数组中的第一个项并返回该项,同时数组的长度减1。所以,该数组会改变原数组

对空数组使用shift()方法,不会报错,而是返回undefined

var a = [1,2,3]
console.log(a, a.shift()) // [2, 3] 1
console.log(a, a.shift()) // [3] 2
console.log(a, a.shift()) // [] 3
console.log(a, a.shift()) // [] undefined

unshift()方法在数组前端添加任意个项并返回新数组长度。所以,该数组会改变原数组

var a = [1,2,3]
console.log(a, a.unshift('x')) // ["x", 1, 2, 3] 4

如果是多个参数会按顺序一次性插入

var a = [1,2,3]
console.log(a, a.unshift('x', 'y', 'z')) // ["x", "y", "z", 1, 2, 3] 6

总结

push()插入数组最后一项,unshift插入数组的第一项,返回数组的新长度

pop()删除数组的最后一项,shift删除数组的第一项,返回改项的值,不是数组的长度,四个方法都会改变原数组

数组排序方法

用来重新排序的reverse()和sort() 

reverse()方法用于反转数组的顺序,原数组顺序也会改变

 var a = [1, 2,3,7, 'a']
console.log(a, a.reverse()) // ["a", 7, 3, 2, 1] (5) ["a", 7, 3, 2, 1]

sort()方法对数组的项目进行排序。,sort方法会调用每个数组项的toString()方法,然后比较得到的字符串排序,

 排序顺序可以是字母顺序或数字顺序,可以是升序(向上)或降序(向下)。 

默认情况下,sort()方法将这些值按字母和升序排序为字符串。 这对于字符串非常有效(“ Apple”先于“ Banana”)。

但是,如果数字按字符串排序,则“ 25”大于“ 100”,因为“ 2”大于“ 1”。 因此,对数字进行排序时,sort()方法将产生错误的结果。

var a = ['24', '1000', '30', 80, 90, 10000]
console.log(a, a.sort())
 // ["1000", 10000, "24", "30", 80, 90]  ["1000", 10000, "24", "30", 80, 90]

如果数组包含undefined元素,会被排到数组的而尾部

数组拼接方法

concat()方法用于连接两个或多个数组。 此方法不会更改现有数组,但会返回一个新数组,其中包含联接数组的值。

var a = ['24', '1000', '30', 80, 90, 10000]
var b = ['a', 'b', 'c']
var c = a.concat(b)
console.log(c) //  ["24", "1000", "30", 80, 90, 10000, "a", "b", "c"]

如果不提供参数,concat()方法返回当前数组的一个浅拷贝。所谓“浅拷贝”,指的是如果数组成员包括复合类型的值(比如对象),则新数组拷贝的是该值的引用

var a = [[1, 2]]
var c = a.concat()
a[0][0] = 33
console.log(a,c) //  [[33, 2]][[33, 2]]
//两个一样的值,所以是浅拷贝,只复制了数组的第一维,数组第一维存放的是第二维的引用,而第二维才是实际存放他们的内容

创建子数组方法

子数组的方法在项目中会常用到也是面试中常问的

slice()方法将数组中选定的元素作为新的数组对象返回。 slice()方法选择从给定start参数开始的元素,并以给定end参数结束(但不包括)为结束。

 var a = [1, 2,3,4,5]
console.log(a, a.slice(2, 5))  //  [1, 2, 3, 4, 5] (3) [3, 4, 5]

不会改变原数组的值,得到新的数组

slice的值为负数时,会加上数组的长度length,比如以下例子 start+length 、end+length

var a = [1, 2,3,4,5]
console.log(a, a.slice(-5, -2))  //  [1, 2, 3, 4, 5]  [1,2,3] 

slice()方法涉及到Number()转型函数的隐式类型转换,当start被转换为NaN时,相当于start = 0;当end被转换为NaN时(end为undefined除外),则输出空数组

可以使用slice()方法将类数组对象变成真正的数组

Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })// ['a', 'b']


数组的删改方式 

splice()和slice()的有非常相似的名字,但是却有本质的区别,

splice()方法在数组中添加/删除项目,并返回删除的项目该方法改变原数组

第一个参数start在指定插入或删除的起始位置,如果是负数则start= length+start, 如果是NaN,则相当于0

第二个参数number指定了应该从数组中删除的元素的个数,如果省略第二个参数,从起始点开始到数组结尾的所有元素都将被删除,如果number是负数或者NaN或者undefined则number=0不删除

如果后面还有参数则表示要被插入数组的新元素

var a = [1,2,3,4,7]
console.log(a, a.splice(3, 4, '3', 'q')) // (5) [1, 2, 3, "3", "q"] (2) [4, 7]

数组位置方法

ES5为数组方法:indexOf()、lastIndexOf()

ES6为数组方法:includes()、find()、findIndex()

indexOf()方法在数组中搜索指定的项目,并返回其位置。

 搜索将从指定位置开始,如果未指定起始位置,则从起始位置开始,

然后在数组末尾结束搜索。 如果找不到该项目,则返回-1。  

如果该项目多次出现,则indexOf方法返回第一次出现的位置。

lastIndexOf()方法在数组中搜索指定的项目,并返回其位置。 

搜索将从指定位置开始,或者如果未指定开始位置,则从结尾开始,然后从数组的开头结束搜索。 如果找不到该项目,则返回-1。

如果要搜索的项目多次出现,则lastIndexOf方法将返回最后一次出现的位置。

var a = [1,2,3,4,2,8,7]
console.log(a.indexOf(2), a.indexOf(9)) // 1 -1
console.log(a.lastIndexOf(2), a.lastIndexOf(9)) // 4 -1 

includes()方法确定数组是否包含指定的元素。 如果数组包含元素,则此方法返回true;否则 返回false。

该find()方法返回通过测试的数组中第一个元素的值(作为函数提供)。

 find()方法对数组中存在的每个元素执行一次功能: 如果找到函数返回真值的数组元素,则find()返回该数组元素的值(并且不检查其余值) 否则返回undefined

该findIndex()方法返回通过测试的数组中第一个元素的索引(作为函数提供)。

 findIndex()方法对数组中存在的每个元素执行一次功能: 如果找到函数返回真值的数组元素,则findIndex()返回该数组元素的索引(并且不检查其余值) 否则返回-1

var a = [1,2,3,4,2,8,7]
console.log(a.includes(2), a.includes(9))
console.log(a.find((n) => n<4), a.find((n) => n>9))
console.log(a.findIndex((n) => n<4), a.findIndex((n) => n>9))

数组归并方法

reduce()方法将数组减少为单个值。 该方法为数组的每个值(从左到右)执行提供的函数。 reduce() 函数的返回值存储在累加器中(结果/总计)。

/*
@total, current, currentIndex, arr
【1】初始变量,默认为数组的第一个元素值。函数第一次执行后的返回值作为函数第二次执行的初始变量,依次类推
【2】当前变量,如果指定了第二个参数,则该变量为数组的第一个元素的值,否则,为第二个元素的值
【3】当前变量对应的元素在数组中的索引(从0开始)
【4】原数组对象
*/
let number = a.reduce((total, current, currentIndex, arr) => {
  return total+current
})
console.log(number) // 250

reduceRight()方法为数组的每个值(从右到左)执行提供的函数。其余与reduce()方法一致

数组迭代方法

ECMAScript5为数组定义了5个迭代方法。

每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this的值。传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。根据使用的方法不同,这个函数执行后的返回值可能会也可能不会影响访问的返回值

map()

该map()方法创建一个新数组,并为每个数组元素调用一个函数。

 该map()方法按顺序为数组中的每个元素调用提供的函数一次。  

var a = [2,3,4,5,6,7]
var b = a.map((el, index, arr) => {
  // console.log(el, index, arr)
  return el*4
})
console.log(a, b) // (6) [2, 3, 4, 5, 6, 7] (6) [8, 12, 16, 20, 24, 28]forEach()

该forEach()方法按顺序对数组中的每个元素调用一次函数。跟for循环一样的用法 如果需要返回值使用map方法

filter()

该方法创建一个数组,其中填充了所有通过测试的数组元素(作为函数提供)。

var c = a.filter((el, index, arr) => {
  return el>6
})
console.log(a,c) // [2, 3, 4, 5, 6, 7] [7] 

every()

该every()方法检查数组中的所有元素是否都通过测试(作为函数提供)。

 every()方法对数组中存在的每个元素执行一次功能: 

如果找到函数返回错误值的数组元素,则every()返回false(并且不检查其余值)

 如果没有错误发生,则every()返回true

some()

跟every()方法一致 some()方法任意一个元素满足条件就返回true,否则返回false

var d= a.some((el, index, arr) => {
  return el>6
})
console.log(a,d) // [2, 3, 4, 5, 6, 7] true

var d= a.every((el, index, arr) => {
  return el>6
})
console.log(a,d) // [2, 3, 4, 5, 6, 7] false

ES6数组方法

下面常用的es6数组方法

扩展运算符三个点...

console.log(...[1,3,7]) // 1 3 7
var a = [1,2]
var b = [3, 4, 5]
var c = [6]
// es5写法
a.concat(b,c)
// es6写法
let d = [...a, ...b, ...c]
console.log(d)

Array.from()

类数组变数组

let arrayLike = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

Array.of()

将一组值,转换为数组

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

fill()

fill方法使用给定值,填充一个数组。

fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。

['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

数组实例的 entries(),keys() 和 values()

var a = ['a', 'b', 'c', 'd']
let b = a.keys()
let c = a.values()
let d = a.entries()
for (let index of b) {
  console.log(index)
}
// 0
// 1
// 2
// 3
for (let ele of c) {
  console.log(ele)
}
// a
// b
// c
// d
for (let name of d) {
  console.log(name)
}
// [ 0, 'a' ]
// [ 1, 'b' ]
// [ 2, 'c' ]
// [ 3, 'd' ]

数组实例的 copyWithin()

它接受三个参数。

 target(必需):从该位置开始替换数据。如果为负值,表示倒数。

 start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。 end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

 这三个参数都应该是数值,如果不是,会自动转为数值。

// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

总结:

改变原数组的方法pop()、push()、shift()、unshift()、splice()、reverse()、sort()

数组去重

在项目开发中经常需要的,那么数组去重有几种方式及性能

1. 双层 for 循环

function distinct(arr) {
  for (let i = 0,len=arr.length;i<len;i++) {
    for (let j = i+1;j<len; j++) {
      if (arr[i] === arr[j]) {
        // splice 会改变数组长度,所以要将数组长度 len 和下标 j 减一
        arr.splice(j, 1)
        len--
        j--
      }
    }
  }
  return arr
}
let d = distinct(a)
console.log(d)

先定义一个包含原始数组第一个元素的数组,然后遍历原始数组,将原始数组中的每个元素与新数组中的每个元素进行比对,如果不重复则添加到新数组中,最后返回新数组;因为它的时间复杂度是O(n^2),如果数组长度很大,效率会很低。

2.indexOf

function distinct(arr) {  let b = []  for (let i = 0,len= arr.length; i<len;i++) {   if (b.indexOf(arr[i]) === -1) {    b.push(arr[i])   }  }  return b}let d = distinct(a)console.log(d)

遍历原数组,每个元素判断是否在新数组中存在,如果不存在则push新数组

3.先排序

function distinct(arr) {
  let sortA = arr.sort(), b = []
  for (let i = 0,len = sortA.length; i<len;i++) {
    if (sortA[i] !== sortA[i+1]) {
      b.push(sortA[i])
    }
  }
  return b
}
let d = distinct(a)
console.log(d) 

先sort方法排序,然后让第一个值与下一个值对比,如果不相等则push新数组

4.ES6 中的 Set 去重

function distinct(arr) {

  return Array.from(new Set(arr))
}
console.log(distinct(a))

5.object键值对

function distinct(array) {
  var obj = {};
  return array.filter(function(item, index, array){
      return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
  })
}
console.log(distinct(a))

这种方法是利用一个空的 Object 对象,我们把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的,但是最后请注意这里obj[typeof item + item] = true没有直接使用obj[item],是因为 因为 123 和 '123' 是不同的,直接使用前面的方法会判断为同一个值,因为对象的键值只能是字符串,所以我们可以使用 typeof item + item 拼成字符串作为 key 值来避免这个问题。

怎么判断不同方法那个性能比较好?比如:

let a = Array.from(new Array(100000), (x, index)=>{  return index})
let start = new Date().getTime()
function distinct(arr) {
  // 不同的去重方法
  let sortA = arr.sort(), b = []
  for (let i = 0,len = sortA.length; i<len;i++) {
    if (sortA[i] !== sortA[i+1]) {
      b.push(sortA[i])
    }
  }
  return b
}
let d = distinct(a)
console.log(d) 
let end = new Date().getTime()
console.log('耗时', end - start) // 101

可以再方法执行start,执行后end,差值计算耗费时间

得到以下结果

双重 for 循环 > indexOf > Array.sort()  > Object 键值对去重复 > ES6中的Set去重

这是本人测试的结果,具体情况可能与场景不同

以上是自己总结的数组方法,如果那里有问题欢迎留言