likes
comments
collection
share

JS07 - Array对象、遍历、复制、冒泡排序、选择排序、数组方法、数组去重

作者站长头像
站长
· 阅读数 37

什么是数组

数组(Array):是有序的元素序列。数组是在程序设计中,为了处理方便,把具有相同类型的若干元素按有序的形式组织起来的一种形式。这些有序排列的同类数据元素的集合称为数组。但JavaScript是弱数据类型,数组中元素类型可以不同

元素:组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量

下标:用于区分数组的各个元素的数字编号称为下标

创建数组

字面量创建(Literal Array)

  • 明确给定要创建的数组元素,如果是创建空数组,则只需要[]
//空数组
var tools = []
//非空数组
var cars = ["Volvo","Lamborghini","Saab","Maserati"]
console.log(tools)  //[]
console.log(cars)   //["Volvo", "Lamborghini", "Saab", "Maserati"]

构造函数创建

  • JavaScript 内置了 Array() 的构造函数,可以通过关键字 new 创建一个普通数组对象,但成员需要在创建之后添加
var cars = new Array()
cars[0] = "Volvo"
cars[1] = "Lamborghini"
cars[2] = "Saab"
cars[3] = "Maserati"
console.log(cars)   //["Volvo", "Lamborghini", "Saab", "Maserati"]

密集阵列创建(Condensed Array)

  • 直接在内置的 Array() 构造函数中,将元素传入参数
var cars = new Array("Volvo","Lamborghini","Saab","Maserati")
console.log(cars)   //["Volvo", "Lamborghini", "Saab", "Maserati"]

数组基本操作

长度 length

  • Array数组的length属性,用于获取并修改数组的长度(元素个数),该属性可读可写
  • 应用1:arr.length 读取数组的长度(元素的个数)
  • 应用2:arr.length = N 保留数组的前N个元素,多的删除,少的赋undefined
  • 应用3:arr.length = 0 清空数组
var cars = new Array("Volvo","Lamborghini","Saab","Maserati")
//读
console.log(cars.length)    //4
//写
cars.length = 5
console.log(cars)       //["Volvo", "Lamborghini", "Saab", "Maserati", 空]
console.log(cars[4])    //undefined
cars.length = 3
console.log(cars)       //["Volvo", "Lamborghini","Saab"]
cars.length = 0
console.log(cars)       //[] --> 清空数组

索引 arr[num]

  • 索引从0开始,依次加1,超过长度的索引结果是undefined
var cars = new Array("Volvo","Lamborghini","Saab","Maserati")
//读
console.log(cars[0],cars[1],cars[2],cars[3])    //Volvo Lamborghini Saab Maserati
//写
cars[1] = "Alto"        
console.log(cars[1])    //Alto      --> 已有,覆盖元素
cars[6] = "Audi"        
console.log(cars[6])    //Audi      --> 未有,新增元素
console.log(cars[5])    //undefined --> 跳过新增下一个元素,被跳过的元素就是undefined

遍历 for/for in/for of/forEach()

var cars = new Array("Volvo","Lamborghini","Saab","Maserati")
//for循环
for(let index=0;index<=cars.length;index++){
    console.log(`cars[${index}] -> ${cars[index]}`)
}
/**
 * cars[0] -> Volvo
 * cars[1] -> Lamborghini
 * cars[2] -> Saab
 * cars[3] -> Maserati
 * cars[4] -> undefined
 */
//for in
for(key in cars){
    console.log(`cars[${key}] - >${cars[key]}`)
}
/**
 * cars[0] - >Volvo
 * cars[1] - >Lamborghini
 * cars[2] - >Saab
 * cars[3] - >Maserati
 */
//for of
for(value of cars){
    console.log(`cars -> ${value}`)
}
/**
 * cars -> Volvo
 * cars -> Lamborghini
 * cars -> Saab
 * cars -> Maserati
 */
//forEach()方法
cars.forEach((function(value,index,cars){
    console.log(`[${cars}] : cars[${index}] -> ${value}`)
}))
/**
 * [Volvo,Lamborghini,Saab,Maserati] : cars[0] -> Volvo
 * [Volvo,Lamborghini,Saab,Maserati] : cars[1] -> Lamborghini
 * [Volvo,Lamborghini,Saab,Maserati] : cars[2] -> Saab
 * [Volvo,Lamborghini,Saab,Maserati] : cars[3] -> Maserati
 */

复制(遍历元素给新数组)

  • 数组也是引用数据类型,因此,数组要实现复制,不能单纯地只将数组名赋值给新变量,需要通过遍历将里面的元素逐一赋值给新数组的元素
var cars = new Array("Volvo","Lamborghini","Saab","Maserati")
var carsCop = new Array()

//直接将数组赋值给新数组
// carsCop = cars
// console.log(carsCop[0]) //Volvo
// cars[0] = "Audi"
// console.log(carsCop[0]) //Audi --> 修改原数组影响到了新数组,说明这里复制的只是地址值

//通过遍历将数组的元素赋值给新数组,实现数组复制
cars.forEach((element,index) => {
    carsCop[index] = element
})
cars[0] = "Alto"
if (cars[0] != carsCop[0]) {
    console.log(`复制成功 -> 新数组:${carsCop}`)
    cars[0] = carsCop[0]
    console.log(`原数组:${cars}`)
} else {
    console.log("复制失败")
}
//复制成功 -> 新数组:Volvo,Lamborghini,Saab,Maserati
//原数组:Volvo,Lamborghini,Saab,Maserati

数组排序

  • 排序原理(遍历+交换)

冒泡排序

  • 先遍历数组,让挨着的两个进行比较,如果前一个比后一个大,那么就把两个换个位置,在数组遍历一遍后,最后一个数字就是最大的那个,接着按照同样的规则进行第二遍遍历,那么第二大的数字就会跑到倒数第二的位置,以此类推,直到实现由小到大的有序排列,因此,也称为沉底排序,小的冒上来,大的沉后面
var numArr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
/** 
 * 外层循环 --> 数组一共有9个元素,那么最多需要排序8轮
 */
for(i=0;i<numArr.length-1;i++){
    /** 
     * 内层循环 --> 每一次遍历排序,都会把最大的值沉到最后,
     * 因此,每一轮遍历排序结束之后,最大的值就已经排到后面了,就不用再往后进行遍历排序
     * 此外,在遍历过程中,是比较前后相邻的两个值,因此到最后一个值的时候,
     * 已经没有更多的值进行比较了,所以index可以少读取一个长度
     */
    for(index=0;index<numArr.length-1-i;index++){
        var temp
        //让挨着的两个进行比较,如果前一个比后一个大,交换位置
        if(numArr[index]>numArr[index+1]){
            //借助中间临时的值进行交换
            temp = numArr[index]
            numArr[index] = numArr[index+1]
            numArr[index+1] = temp
        }
    }
}
console.log(numArr) //[1, 2, 3, 4, 5, 6, 7, 8, 9]
function chose(numArr){
    var temp;
    for (let index = 0; index < numArr.length-1; index++) {
        var maxIndex = index;
        for (let choseIndex = index+1; choseIndex < numArr.length; choseIndex++) {
            if (numArr[choseIndex]>numArr[index]) {
                maxIndex = choseIndex
            } // else nothing to do...            
            console.log(index,choseIndex,maxIndex,numArr[choseIndex])
        }
        if (maxIndex !== index) {
            temp = numArr[index]
            numArr[index] = numArr[maxIndex]
            numArr[maxIndex] = temp
        }   //else nothing to do
    }
    return numArr
}
 console.log(chose([12,1,2,4,0,65]))     //(6) [65, 12, 4, 2, 1, 0]

Array.sort() 方法

选择排序

  • 先假定数组中最小数字的索引是0,然后遍历数组,只要有一个数字比它小,就替换之前记录的索引,这样在遍历结束后,就能找到最小数字的那个索引,然后让最小的索引换到第0个的位置,接下来假定第1个是最小数字的索引,再遍历一次数组,找到比它小的数字的索引,在遍历结束后换到第1个的位置,以此类推
var numArr = [354,21,5,46,4,9,7,2,8];
//数组一共有9个元素,那么最多需要选择排序8次
for(let i = 0;i<numArr.length-1;i++){
    //假定最小数字的索引为i,最开始的时候为0
    var minIndex = i;
    var temp;
    //如果已经假定i是最小的,就可以从i+1开始进行遍历比较,没必要从自身开始遍历
    for (let index = i+1; index < numArr.length; index++) {
        //如果在遍历过程中有更小的数字,就把之前记录的索引替换为该数字的索引
        if (numArr[index]<numArr[minIndex]) {
            minIndex = index;
        }
    }
    //每一次遍历后,都将比之前记录索引所对应的数字进行替换,最终替换出最小值
    if(minIndex != i){  //如果索引发生了改变,再执行交换,减少代码不必要的交换次数
        temp = numArr[minIndex]
        numArr[minIndex] = numArr[i]
        numArr[i] = temp
    }
}
console.log(numArr) //[2, 4, 5, 7, 8, 9, 21, 46, 354]

数组常用方法

方法功能返回
push()末尾追加元素length 返回新的长度
unshift()将新项添加到数组的开头length 返回新的长度
pop()移除数组的最后一个元素element 被移除的元素
shift()方法移除数组的第一项element 被移除的元素
splice()splice() 给数组添加或删除项目element 被移除的元素
reverse()将数组中元素的位置颠倒array 新数组
sort()用原地算法对数组的元素进行排序array 新数组
方法功能返回
concat()方法用于合并两个或多个数组array 新数组
join()将数组或类数组对象元素连接成字符串string 新字符串
slice()抽取数组的一部分array 新数组
indexOf()给定元素第一个索引,如果不存在返回-1index 索引值
lastIndexOf()给定元素最后一个索引,如果不存在返回-1index 索引值

1 更改原数组

追加 push() unshift()

  • push()方法向数组末尾添加新项目,并返回新长度
var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];
console.log("数组初始长度 : "+arr.length)   //14
/**push()
 * 功能:将值追加到数组中
 * 返回:新数组长度length
 * 语法:
 * push(element0)
 * push(element0, element1)
 * push(element0, element1, ..., elementN)
 */
var pushReturn_1 = arr.push("end")
var pushReturn_2 = arr.push("end",007)
console.log(pushReturn_1,pushReturn_2)  //15,17
  • unshift() 将新项添加到数组的开头,并返回新的长度
var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];
console.log("数组初始长度 : "+arr.length)   //14
/**unshift()
 * 功能:将值添加到数组前
 * 返回:新数组长度length
 * 语法:
 * array.unshift(item1, item2, ..., itemN)
 */
var unshiftReturn_1 = arr.unshift("unshift")
var unshiftReturn_2 = arr.unshift("unshift01","unshift02")
console.log(arr)
//["unshift01", "unshift02", "unshift", 1, 2, 3, 4, 3, 5, 6, 2, 1, 5, 3, 4, 2, 5]
console.log(unshiftReturn_1,unshiftReturn_2)
//15 17

删除 pop() shift()

  • pop() 移除数组的最后一个元素,返回被移除的元素element
var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];
console.log("数组初始长度 : "+arr.length)   //14
/**pop()
 * 功能:移除数组的最后一个元素
 * 返回:被移除的元素element
 * 语法:
 * array.pop()
 */
var popReturn = arr.pop();
console.log(arr)        //[1,2,3,4,3,5,6,2,1,5,3,4,2]
console.log(popReturn)  //5
  • shift() 方法移除数组的第一项,并返回被移除的元素element
var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];
console.log("数组初始长度 : "+arr.length)   //14
/**shift()语法:array.shift()
 */
var shiftReturn = arr.shift();
console.log(arr)            //[2,3,4,3,5,6,2,1,5,3,4,2,5]
console.log(shiftReturn)    //5
console.log(arr.length)     //13

增删 splice()

  • splice() 给数组添加或删除项目,并返回删除的项目
var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];
console.log("数组初始长度 : "+arr.length)   //14
/**splice()语法:array.splice(index, howmany, item1, ....., itemX)
 * 返回:删除的元素
 */
//删除从索引8到数组尾的项目
var spliceReturn1 = arr.splice(8)
console.log(spliceReturn1)  //[1, 5, 3, 4, 2, 5]
//从索引2开始,删除两个
var spliceReturn2 = arr.splice(2,2)
console.log(spliceReturn2)  //[3, 4]
//从索引0开始,删除0个项目 --> 相当于复制数组
var spliceReturn3 = arr.splice(1,0)
console.log(spliceReturn3)  //[]
//从索引2开始,删除0个项目,并添加元素"splice1"和"splice2" --> 相当于在指定位置添加元素
var spliceReturn4 = arr.splice(2,0,"splice1","splice2")
console.log(spliceReturn4)  //[]

console.log(arr)            //[1, 2, 'splice1', 'splice2', 3, 5, 6, 2]

排序 reverse() sort()

  • reverse() 按照原数组的反向排列数组,并返回新数组
var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];
console.log("数组初始长度 : "+arr.length)   //14
/**reverse() 将数组中元素的位置颠倒(不是数字倒序)
 * 返回:新数组
 */
var reverseReturn = arr.reverse()
console.log(reverseReturn)  //[5, 2, 4, 3, 5, 1, 2, 6, 5, 3, 4, 3, 2, 1]
console.log(arr)            //[5, 2, 4, 3, 5, 1, 2, 6, 5, 3, 4, 3, 2, 1]
  • sort() 方法用原地算法对数组的元素进行排序,并排序后的新数组

  • 语法

    1. 无函数 --> arr.sort()
    2. 有函数 --> arr.sort(compareFn(a,b){...})
  • 语法说明:

    1. 无函数:元素会按照转换为字符串的各个元素的 Unicode 位点进行排序。
      • eg1. "Banana"会被排到"cherry"之前;
      • eg2. 当数字从小到大排序时,9自然排在80之前,但如果sort()没有指明compareFn函数,比较的数字就会转换为字符串 "9" 和 "80",此时,比较两者的 Unicode 位点,"80" 比 "9" 更靠前了
    2. 有函数:数组会按照函数compareFn(a, b)的返回值排序。其中,参数a和b分别表示数组中两个挨着的元素,b在a前面,例如:第一个元素是b,那么第二个就是a,接着b移动到第二个元素,a就是第三个,以此类推,直到b指向倒数第二个,a指向最后一个元素。在这里,如果参数不只设置a和b,还加上更多的c、d等等,此时的c、d将不会接受数组的元素,结果会是undefined
      • 如果 compareFn(a, b) 返回值大于 0,b 会被重新排列到 a 之前
      • 如果 compareFn(a, b) 返回值小于 0,a 会被重新排列到 b 之前
      • 如果 compareFn(a, b) 返回值等于 0,a 和 b 的相对位置不变
      • 代码原理:
        function compareFn(a, b) {
            if (a < b) { return -1;}
            if (a > b) { return 1; }
            if (a = b) { return 0; }
        }
        
    3. 技巧 - 数字: 数组元素是数字,可以用a-b实现升序或b-a实现倒序
    4. 技巧 - 对象:数组元素是对象,可以用a、b调用对象属性进行排序
  • 无函数

var arrStr = ["Blue", "Humpback", "Beluga","Lamborghini","Maserati"]
var arrNum = [12,34,3,56,2,15,342,5];
var arrPerson = [{name:"James",age:50},{name:"Holi",age:21},{name:"Hellen",age:35},{name:"Paul",age:19},{name:"Jack",age:28}];
var sortReturnStr = arrStr.sort()
var sortReturnNum = arrNum.sort()
var sortReturnPerson = arrPerson.sort()
console.log(sortReturnStr)
//['Beluga', 'Blue', 'Humpback', 'Lamborghini', 'Maserati']
console.log(sortReturnNum)
//[12, 15, 2, 3, 34, 342, 5, 56]
console.log(sortReturnPerson)
//(5) [{…}, {…}, {…}, {…}, {…}]
// 0:{name: 'James', age: 50}
// 1:{name: 'Holi', age: 21}
// 2:{name: 'Hellen', age: 35}
// 3:{name: 'Paul', age: 19}
// 4:{name: 'Jack', age: 28}
// length: 5
  • 有函数
//正序排列原理:如果a<b,就把a放在b之前
//字符串 - 按照首字母的Unicode编码正序排列
var sortReturnStr = arrStr.sort(function(second,first){
    // second < first ? return -1 : null //-->return 语句不能放在三目运算符中
    if(second < first) return -1
})
//数字 - 按照数字的大小正序排列
function compareFn(a,b){
    if (a < b) { return -1 }
}
var sortReturnNum = arrNum.sort(compareFn)
//对象 - 根据某个属性正序排列
var sortReturnPerson = arrPerson.sort((a,b) => {
    const ageA = parseInt(a.age)
    const ageB = parseInt(b.age)
    if (ageA < ageB) { return -1 }
})
console.log(sortReturnNum)
// [2, 3, 5, 12, 15, 34, 56, 342]
console.log(sortReturnStr)
// ['Beluga', 'Blue', 'Humpback', 'Lamborghini', 'Maserati']
console.log(sortReturnPerson)
//(5) [{…}, {…}, {…}, {…}, {…}]
// 0: {name: 'Paul', age: 19}
// 1: {name: 'Holi', age: 21}
// 2: {name: 'Jack', age: 28}
// 3: {name: 'Hellen', age: 35}
// 4: {name: 'James', age: 50}
// length: 5
/* 排序简明方式 */
var number = [1,85,4,64,94,2,51,73,34]
//正序
var numberSortPositive = number.sort((a,b) => { 
    return a-b 
})
console.log(number) //[1, 2, 4, 34, 51, 64, 73, 85, 94]
//倒序
var numberSortNegative = number.sort((a,b) => {
    return b-a
})
console.log(number) //[94, 85, 73, 64, 51, 34, 4, 2, 1]

2 保留原数组

拼接 concat() join()

  • concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组
  • concat() 方法中不传递参数时,也可用于数组浅拷贝
var arrStr = ["Maserati","Lamborghini"]
var arrNum = [90,56,{num:600}];
/**concat() 方法用于合并两个或多个数组。
 * 返回:此方法不会更改现有数组,而是返回一个新数组。
 * 语法:concat(value0, value1, ..., valueN)
 * 数组和/或值,将被合并到一个新的数组中。
 * 如果省略了所有 valueN 参数,则 concat 会返回调用此方法的现存数组的一个浅拷贝,
 * 即数组中如果有对象作为元素,那么只是拷贝对象这个元素的地址值。
 */

//拼接
var concatStrNum = arrStr.concat(arrNum)
console.log(concatStrNum)   
//(5) ['Maserati', 'Lamborghini', 90, 56, {…}]
// 0: "Maserati"
// 1: "Lamborghini"
// 2: 90
// 3: 56
// 4: {num: 600}
//     length: 5

//浅拷贝
var concatIsCopy = arrNum.concat()
console.log(concatIsCopy)   
//(3) [90, 56, {…}]
// 0: 90                   
// 1: 56
// 2: {num: 600} --> 拷贝,与原数组一致
//     length: 3
arrNum[0] = 900         //改变原数组元素为基本数组类型的值
arrNum[2].num = 500     //改变原数组元素为引用数据类型的属性
console.log(arrNum[0])  //900
console.log(arrNum[2])  //{num:500}
console.log(concatIsCopy)
//(3) [90, 56, {…}]
// 0: 90            --> 基本数据类型的元素没有被改变
// 1: 56
// 2: {num: 500}    --> 引用数据类型的元素被改变了
//     length: 3
  • join() 方法将一个数组或一个类数组对象的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
var arrSimulate = {0:""}
/**join() 方法将一个数组或一个类数组对象的所有元素连接成一个字符串
 * 返回:连接后的字符串。
 * 如果数组只有一个项目,那么将返回该项目而不使用分隔符。
 * 语法:
 *      join()
 *      join(separator) 
 *      如果缺省separator,数组元素用逗号,分隔
 *      如果separator是空字符串 "",则所有元素之间都没有任何字符
 * 注意:如果一个元素为 undefined 或 null,它会被转换为空字符串。
 */

//连接数组
var arr = [90,56,{num:600},"Maserati","Lamborghini"];
var joinDefault = arr.join()
var joinBar = arr.join(" - ") 
var joinEmp = arr.join("")
console.log(joinDefault)    //90,56,[object Object],Maserati,Lamborghini
console.log(joinBar)        //90 - 56 - [object Object] - Maserati - Lamborghini
console.log(joinEmp)        //9056[object Object]MaseratiLamborghini
//连接类数组
var arrSimulate = {
    0:90,
    1:56,
    2:{num:600},
    3:"Maserati",
    4:"Lamborghini",
    length:5
}
//类数组不具有join()方法,以下语句均会报错
// console.log(arrSimulate.join())
// console.log(arrSimulate.join(" + "))
// console.log(arrSimulate.join(""))
/**连接类数组对象(arguments),
 * 通过在Array.prototype.join上调用Function.prototype.call
 */
function simulateJoin(a,b,c){
    let result = Array.prototype.join.call(arguments);
    console.log(arguments)
    return result;
}
console.log(simulateJoin(10,50,30))

抽取 slice()

  • slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
var arr = [123,435,621,53,425];
/**slice() 从数组中抽取一部分,但原始数组不会被改变
 * 返回:抽取出来的新数组(新数组对象是一个由 begin 和 end(包括 begin,不包括end)决定的原数组的浅拷贝)
 * begin 可选:
 *      提取起始处的索引(从 0 开始),从该索引开始提取原数组元素。
 *      如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,
 *      slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
 *      如果省略 begin,则 slice 从索引 0 开始。
 *      如果 begin 超出原数组的索引范围,则会返回空数组。
 * end 可选:
 *      提取终止处的索引(从 0 开始),在该索引处结束提取原数组元素。
 *      slice 会提取原数组中索引从 begin 到 end 的所有元素(包含 begin,但不包含 end)。
 *      slice(1,4) 会提取原数组中从第二个元素开始一直到第四个元素的所有元素(索引为 1, 2, 3 的元素)。
 *      如果该参数为负数,则它表示在原数组中的倒数第几个元素结束抽取。 
 *      slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。
 *      如果 end 被省略,则 slice 会一直提取到原数组末尾。
 *      如果 end 大于数组的长度,slice 也会一直提取到原数组末尾。
 * slice 浅拷贝规则:slice 只会返回一个浅复制了原数组中的元素的一个新数组
 *      如果该元素是个对象引用(不是实际的对象),slice 会拷贝这个对象引用到新的数组里。
 *      两个对象引用都引用了同一个对象。
 *      如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
 *      对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。
 *      在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
 *      如果向两个数组任一中添加了新元素,则另一个不会受到影响。
 */
//抽取:从索引5开始,到索引6-1结束
var sliceArr1 = arr.slice(4,5)
console.log(sliceArr1)          //[425]
//抽取:从索引2开始,到索引-3-1结束(倒数第四个,倒数的直接看数字就可以)
var sliceArr2 = arr.slice(0,-3)
console.log(sliceArr2)          //[123, 435]
var sliceArr3 = arr.slice(2)    //从索引2开始,截取到结尾
console.log(sliceArr3)          //[621,53,425]
var sliceArrCopy = arr.slice()  //浅拷贝
console.log(sliceArrCopy)       //[123,435,621,53,425]

索引 indexOf() lastIndexOf()

  • indexOf() 方法返回在数组中可以找到给定元素的第一个索引,如果不存在,则返回 -1
var arr = [123,435,621,53,425];
/**indexOf() 通过全等运算 === 查找元素索引
 * 返回: 索引值,如果不存在,则返回 -1
 * 语法: indexOf(searchElement)
 *       indexOf(searchElement, fromIndex)
 * fromIndex 可选:开始查找的位置。
 *      如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回 -1
 *      如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,
 *      即 -1 表示从最后一个元素开始查找,-2 表示从倒数第二个元素开始查找,以此类推
 * 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。
 *      如果抵消后的索引值仍小于 0,则整个数组都将会被查询。其默认值为 0。
 */
console.log(arr.indexOf(2));     // -1
console.log(arr.indexOf(53));    // 3
console.log(arr.indexOf(425, 2));// 4 
console.log(arr.indexOf(2, -1)); // -1
console.log(arr.indexOf(53, -3));// 3
//应用:判断商品是否在数组里,不在则更新数组
var veggies = ["potato","tomato","chillies","pepper"]
function updateVegetablesCollention(veggies,veggie){
    if ( veggies.indexOf(veggie) == -1) {
        veggies.push(veggie)
        console.log(`${veggie}不存在且已新增`)
    } else {
        console.log(`${veggie}已存在`)
    }
    document.writeln("Vegetable List:")
    veggies.forEach(element=>{
        document.write(`<li>${element}</li>`)
    })
}
updateVegetablesCollention(veggies,"potato")
//potato已存在
//Vegetable List:
// potato
// tomato
// chillies
// pepper
updateVegetablesCollention(veggies,"broccoli")
//broccoli不存在且已新增
//Vegetable List:
// potato
// tomato
// chillies
// pepper
// broccoli
  • lastIndexOf() 方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。
var arr = [123,53,435,621,53,53];
/**lastIndexOf() 从数组的后面向前查找指定元素最后一个的索引
 * 返回:索引值,如果不存在则返回-1
 * 语法:lastIndexOf(searchElement)
 *      lastIndexOf(searchElement, fromIndex)
 * fromIndex 可选:从此索引开始逆向查找。
 *      默认为数组的长度减 1(arr.length - 1),即最后一个元素的索引,此时整个数组都被查找。
 *      如果该值大于或等于数组的长度,也是整个数组被查找。
 *      如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。
 *      如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。 
 */
var lastIndexOfArr0 = arr.lastIndexOf()
var lastIndexOfArr1 = arr.lastIndexOf(53)
var lastIndexOfArr2 = arr.lastIndexOf(53,2)
var lastIndexOfArr3 = arr.lastIndexOf(53,-6)
console.log(lastIndexOfArr0)    //-1
console.log(lastIndexOfArr1)    //5
console.log(lastIndexOfArr2)    //1
console.log(lastIndexOfArr3)    //-1

3 数组其它功能

遍历 forEach()

  • forEach()  方法对数组的每个元素执行一次给定的函数
var arr = [123,53,435,621,53,53];
/**forEach() 
 * 返回:无返回值
 * 
 * 语法:
 * (1) 箭头函数
 * forEach((element) => { ... })
 * forEach((element, index) => { ... })
 * forEach((element, index, array) => { ... })
 * (2) 回调函数
 * forEach(callbackFn)
 * forEach(callbackFn, thisArg)
 * (3) 内联回调函数
 * forEach(function(element) { ... })
 * forEach(function(element, index) { ... })
 * forEach(function(element, index, array){ ... })
 * forEach(function(element, index, array) { ... }, thisArg)
 * 
 * 函数说明:
 * callbackFn 为数组中每个元素执行的函数
 * 
 * 参数说明:
 * element 数组中正在处理的当前元素
 * index 数组中正在处理的当前元素的索引
 * array forEach()方法正在操作的数组
 * thisArg 可选参数。当执行回调函数 callbackFn 时,用作 this 的值
 */
//通过forEach()遍历数组
arr.forEach((element,index) => {console.log(index+" - "+element)})
/*  
 0 - 123
 1 - 53
 2 - 435
 3 - 621
 4 - 53
 5 - 53
 */

映射 map()

  • map() 方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成
var arr = [123,53,435,621,53,53];
/**map() 方法会给原数组中的每个元素都按顺序调用一次 callbackFn 函数。
 *       callbackFn 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。
 * 返回:新数组,每个元素都是回调函数的返回值
 * 
 * 语法:
 * // 箭头函数
 * map((element) => { ... })
 * map((element, index) => { ... })
 * map((element, index, array) => { ... })
 * // 回调函数
 * map(callbackFn)
 * map(callbackFn, thisArg)
 * // 内联回调函数
 * map(function(element) { ... })
 * map(function(element, index) { ... })
 * map(function(element, index, array){ ... })
 * map(function(element, index, array) { ... }, thisArg)
 * 
 * 函数说明:
 *      callbackFn 函数只会在有值的索引上被调用;
 *      那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。
 * 
 * 方法提醒:
 *      map 方法设计初衷是弥补forEach不返回新数组的问题,
 *      因此当你不打算使用返回的新数组或不使用回调函数中返回值却使用 map 是违背设计初衷的,
 *      请用 forEach 或者 for-of 替代
 */

//求数组每个元素的平方根
var square = arr.map(element => {
    return element*element
})
console.log(square) //(6) [15129, 2809, 189225, 385641, 2809, 2809]

筛选 filter() every() some() find()

  • filter() 为数组中的每个元素调用一次 callbackFn 函数,并利用所有使得 callbackFn 返回 true 的元素创建一个新数组。callbackFn 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过 callbackFn 测试的元素会被跳过,不会被包含在新数组中。
var arr = [-123,53,435,621,53,54];
/**filter() 为数组中的每个元素调用一次函数,把返回 true 的元素(筛选条件)创建为一个新数组
 * 返回:通过筛选的元素组成的数组
 * 语法:格式同forEach
 */
//剔除小于100的值,保留大于等于100的值
var filterArr1 = arr.filter(element => {
    if(element >= 100){
        return element
    }
})
console.log(filterArr1)  //(3) [123, 435, 621]
//找出数组中所有的素数
var filterArr2 = arr.filter(element => {
    for (let index = 2; index < element; index++){
        if(element % index === 0){
            return false
        }
    }
    return element > 1
})
console.log(filterArr2) //(2) [53, 53]
  • every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
var arr1 = [-123,53,435,621,53,54];
var arr2 = [123,153,435,621,153,154];
/**every() 方法为数组中的每个元素执行一次 callback 函数,直到它找到一个会使 callback 返回 falsy 的元素。如果发现了一个这样的元素,every 方法将会立即返回 false。否则,callback 为每一个元素返回 true,every 就会返回 true。
 * 返回:如果回调函数的每一次返回都为 truthy 值,返回 true,否则返回 false
 * 语法:格式同forEach
 */
//检测数组中的所有元素是否都大于 100
var everyArrLargerHundred1 = arr1.every(element => element>100 ? true : false)
console.log(everyArrLargerHundred1)     //false
var everyArrLargerHundred2 = arr2.every(element => element>100 ? true : false)
console.log(everyArrLargerHundred2)     //true
  • some() 方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。
var arr1 = [-123,53,35,21,53,54];
var arr2 = [123,153,435,621,153,154];
/**some() 为数组中的每一个元素执行一次 callback 函数,直到找到一个使得 callback 返回一个“真值”(即可转换为布尔值 true 的值)。如果找到了这样一个值,some() 将会立即返回 true。否则,some() 返回 false。
 * 返回:true/false
 * 语法:同forEach()
 */
//检测在数组中是否有元素大于 100
var arrSomeLarger1 = arr1.some(element => element>100?1:0)
var arrSomeLarger2 = arr2.some(element => element>100?1:0)
console.log(arrSomeLarger1,arrSomeLarger2)  //false true
  • find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined
const inventory = [
    {name: 'apples', quantity: 2},
    {name: 'bananas', quantity: 0},
    {name: 'cherries', quantity: 5}
];
  
/**find() 方法对数组中的每一项元素执行一次 callbackFn 函数,直至有一个 callbackFn 返回 true。当找到了这样一个元素后,该方法会立即返回这个元素的值,否则返回 undefined。注意 callbackFn 函数会为数组中的每个索引调用即从 0 到 length - 1,而不仅仅是那些被赋值的索引,这意味着对于稀疏数组来说,该方法的效率要低于那些只遍历有值的索引的方法。
 * 返回:元素/undefined
 * 语法:同forEach()
 */
var findInventory = inventory.find(() => "apples")
console.log(findInventory) //{name: "apples", quantity: 2}

叠加 reduce()

  • reduce()  方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
/**reduce() 叠加
 * 返回:reducer函数遍历整个数组后的结果
 * 语法:
 * // 箭头函数
 * reduce((previousValue, currentValue) => { ... } )
 * reduce((previousValue, currentValue, currentIndex) => { ... } )
 * reduce((previousValue, currentValue, currentIndex, array) => { ... } )
 * 
 * reduce((previousValue, currentValue) => { ... } , initialValue)
 * reduce((previousValue, currentValue, currentIndex) => { ... } , initialValue)
 * reduce((previousValue, currentValue, currentIndex, array) => { ... }, initialValue)
 * 
 * // 回调函数
 * reduce(callbackFn)
 * reduce(callbackFn, initialValue)
 * 
 * // 内联回调函数
 * reduce(function(previousValue, currentValue) { ... })
 * reduce(function(previousValue, currentValue, currentIndex) { ... })
 * reduce(function(previousValue, currentValue, currentIndex, array) { ... })
 * 
 * reduce(function(previousValue, currentValue) { ... }, initialValue)
 * reduce(function(previousValue, currentValue, currentIndex) { ... }, initialValue)
 * reduce(function(previousValue, currentValue, currentIndex, array) { ... }, initialValue)
 * 
 * 参数说明:
 * callbackFn 一个“reducer”函数,包含四个参数
 *  previousValue:
 *      上一次调用 callbackFn 时的返回值。在第一次调用时,若指定了初始值 initialValue,其值则为 initialValue,否则为数组索引为 0 的元素 array[0]。
 *  currentValue:
 *      数组中正在处理的元素。在第一次调用时,若指定了初始值 initialValue,其值则为数组索引为 0 的元素 array[0],否则为 array[1]。
 *  currentIndex:
 *      数组中正在处理的元素的索引。若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始。
 *  array:
 *      用于遍历的数组。
 *  initialValue 可选:
 *      作为第一次调用 callback 函数时参数 previousValue 的值。若指定了初始值 initialValue,则 currentValue 则将使用数组第一个元素;否则 previousValue 将使用数组第一个元素,而 currentValue 将使用数组第二个元素。
 */
//求数组所有值的和
arr = [354,21,5,46,8,46,9,2,7,46]
reduceArr = arr.reduce((previousValue,currentValue) => previousValue+currentValue)
console.log(reduceArr)  //544
//求对象所有值的和
arr = [354,21,5,46,8,46,9,2,7,46]
arrObj = [{number: 1}, {number: 2}, {number: 3}]
reduceArrObj = arrObj.reduce((pre,cur) => {
    return pre+cur.number
},0)////要累加对象数组中包含的值,必须提供 initialValue,否则会变成字符串拼接,结果就成了[object Object]23
console.log(reduceArrObj)   //6
//将二维数组转化为一维
arr2Dim = [[354,21],[5,46],[8,46],[9,2,7,46]]
arr1Dim = arr2Dim.reduce((pre,cur)=>pre.concat(cur))
console.log(arr1Dim)    //(10) [354, 21, 5, 46, 8, 46, 9, 2, 7, 46]
//计算数组中每个元素出现的次数
arr = [354,27,27,27,26,27,26]
reduceCount = arr.reduce((allNums,num)=>{
    if(num in allNums){
        allNums[num]++
    } else {
        allNums[num] = 1
    }
    return allNums
},{})
console.log(reduceCount)    //{26: 2, 27: 4, 354: 1}

names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']
reduceName = names.reduce((allNames,cur)=>{
    if(cur in allNames){
        allNames[cur]++
    } else {
        allNames[cur] = 1
    }
    return allNames //如果不返回值,则不会有新的参数给到allNames,相当于此处的返回值每次都会给到allNames,便利结束后,才终止
},{})
console.log(reduceName)     //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
//按照岗位分类员工
let people = [
    { name: 'Alice', age: 21, work: "developer" },
    { name: 'Ace', age: 28, work: "developer" },
    { name: 'Max', age: 28, work: "saler" },
    { name: 'James', age: 20, work: "manager" }
];
function reducePeople(objArray,property){ //按照property划分objArray的函数
    //把objArray的每个元素遍历一遍,最后放在target中
    return objArray.reduce((target,objCur)=>{
        //objCur[peoperty]指明按照哪个属性划分
        let key = objCur[property]
        //如果该属性不存在,就新增一个空数组
        if(!target[key]) target[key] = []
        //在新增的空数组中放入当前对应属性的对象
        target[key].push(objCur)
        //最后把汇总的对象数组返回
        return target
    },{})
}
console.log(reducePeople(people,"work"))
// {
//   20: [
//     { name: 'Max', age: 20 },
//     { name: 'Jane', age: 20 }
//   ],
//   21: [{ name: 'Alice', age: 21 }]
// }
//数组去重
let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'];
reduceDuplicate = myArray.reduce((target,curEle)=>{
    if(target.indexOf(curEle) == -1 ) target.push(curEle)
    return target
},[])
console.log(reduceDuplicate)    //(5) ["a", "b", "c", "e", "d"]

判断 isArray()

  • Array.isArray() 用于确定传递的值是否是一个Array
/**Array.isArray() 用于确定传递的值是否是一个 Array
 * 语法:Array.isArray(value)
 * 返回:如果值是 Array,则为 true;否则为 false
 */
//数组
let array = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'];
//类数组
let similarArray = {
    0:2020,
    1:2021,
    2:2022,
    length:3
}
console.log(Array.isArray(array))           //true
console.log(Array.isArray(similarArray))    //false
console.log(Array.prototype)                //true

/**
 *  * instanceof VS isArray
 * 当检测 Array 实例时,Array.isArray 优于 instanceof,因为 Array.isArray 能检测 iframes。
 */
 const iframe = document.createElement('iframe');
 document.body.appendChild(iframe);
 xArray = window.frames[window.frames.length-1].Array;
 const arr = new xArray(1,2,3); // [1,2,3] 
// 正确检查 Array
Array.isArray(arr);  // true
// Considered harmful, because doesn't work through iframes
arr instanceof Array; // false

扁平 flat()

  • flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回
  • flat() 方法会移除数组中的空项
/**flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回
 * 语法:flat()
 *      flat(depth)
 * depth 可选:
 *      指定要提取嵌套数组的结构深度,默认值为 1
 * 返回值:
 *      一个包含将数组与子数组中所有元素的新数组
 */
//扁平化嵌套数组
var arr1 = [1, 2, [3, 4]];
console.log(arr1.flat())    //[1, 2, [3, 4]]
var arr2 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
console.log(arr2.flat(3))   //[1, 2, 3, 4, 5, 6, 7, 8, [9, 10]]
var arr3 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10, [11, 12]]]]]];
console.log(arr3.flat(Infinity))
//(12) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]   
//扁平化并移除数组空项
var arr4 = [1, 2, [3, , [, 6, [7, 8, [9, 10, [11, ]]]]]];
console.log(arr4.flat(Infinity))    
//(9) [1, 2, 3, 6, 7, 8, 9, 10, 11]  

数组去重

方法一:复制到新的空数组

var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];//对该数组去重
//方法一:声明一个空数组,然后一个个地放进元素,在放的时候判断一下是否已经存在
var arrnew = []
for(i=0;i<arr.length;i++){
    //新数组中如果不存在arr[i]元素,则将其赋值给新数组
    if (arrnew.indexOf(arr[i]) == -1) {
        //这里如果使用 arrnew][i] = arr[i],可能会因中间有重复的值时被跳过,导致出现空元素
        arrnew.push(arr[i])
    }
}
console.log(arr,arrnew)
/**
 * (9) [1, 2, 3, 4, 3, 5, 6, 2, 1] 
 * (6) [1, 2, 3, 4, 5, 6]
 */

方法二:数组内置方法splice()

var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];//对该数组去重
//方法二:数组内置方法splice()
for(i=0;i<arr.length-1;i++){
    for(j=i+1;j<arr.length;j++){
        if (arr[i] === arr[j]) {
            var spliceElement = arr.splice(j,1)
            console.log(`重复剔除 -> ${spliceElement}`)
            //剔除了一个重复元素后,需要将剔除的元素的索引一起剔除
            j--;
        }
    }
}
console.log(arr)
/**
 * 重复剔除 -> 1
 * 2 重复剔除 -> 2
 * 2 重复剔除 -> 3
 * 重复剔除 -> 4
 * 2 重复剔除 -> 5
 * (6) [1, 2, 3, 4, 5, 6]
 */
 
 function removeDuplicate(dataArr){
    for (let index = dataArr.length; index > 0; index--) {
        for (let checkIndex = index-1; checkIndex >=0; checkIndex--) {
            if (dataArr[checkIndex] == dataArr[index]) {
                var remo = dataArr.splice(checkIndex,1)                        
                checkIndex++;
            } //else nothing to do...
        }
    }
    return dataArr;
}
 console.log(removeDuplicate([22,15,22,15,22,12]))  //[15, 22, 12]
 console.log(removeDuplicate([2,5,4,8,2,5,8,4,"5"]))  //[2, 8, 4, "5"]

方法三:利用对象key特性

var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];//对该数组去重
//方法三:对象key值重复会被覆盖
var tempObj = new Object()
var arrnew = new Array()
for(i=0;i<arr.length;i++){
    tempObj[arr[i]] = "key is the core"
}
for (const key in tempObj) {
    //字符串-0 会将其隐式转换为number类型
    arrnew.push(key-0)
}
console.log(arr,arrnew)
/**
 * (14) [1, 2, 3, 4, 3, 5, 6, 2, 1, 5, 3, 4, 2, 5]
 * (6) [1, 2, 3, 4, 5, 6]
 */

方法四:利用set特性

var arr = [1,2,3,4,3,5,6,2,1,5,3,4,2,5];//对该数组去重
//方法四:利用set数据结构不允许元素重复的特性,并结合Array.from(set)将set结构转换为数组
//1.将数组作为参数传给set数据结构
var setTemp = new Set(arr);
console.log(setTemp)    
/**
 * Set(6) {1, 2, 3, 4, 5, …}
 * [[Entries]]
 *      0: 1
 *      1: 2
 *      2: 3
 *      3: 4
 *      4: 5
 *      5: 6
 *      size: (...) 
 */
//2.利用Array.from()方法,将类似数组的数据结构转换为数组
var arrnew = Array.from(setTemp);
console.log(arrnew)     //(6) [1, 2, 3, 4, 5, 6]