JavaScript 基础(四):Array

基础系列文章:

JavaScript 基础(一):null 和 undefined

JavaScript 基础(二):String

JavaScript 基础(三):Number

JavaScript 基础(四):Array

JavaScript 基础(五):Object

JavaScript 基础(六):Map、Set

最近一直没有更新了。

原因无非就是工作忙了一点。还有就是对于这个 Array 要写的东西确实比较多。

本篇文章会分多次更新。(更新完成)

在文章中,如果是 ES6 新增的方法,会标红作为提示。

一、Array 构造器

构造器:用于创建一个新的数组。

通常有两种方式:对象字面量构造器方式

下面是代码:

// 对象字面量方式
let arr = [1, 2]
// 构造器方式
let arr1 = Array(1, 2)
console.log(arr === arr1)
console.log('arr:', arr, 'arr1:', arr1)

对于构造器方式,有两点要说明:

1、是使用 new 还是使用函数调用方式,ECMA 给的解释是都一样;一般来说直接函数调用简便。

2、参数,当只有一个参数时,表示创建数组的长度;0 或 2个以上参数,作为数组元素。

var arr1 = Array(4)
console.log('array 1 参数:', arr1)    // array 1 参数: [ <4 empty items> ]
arr1 = Array()
console.log('array 0 参数:', arr1)    // array 0 参数: []
arr1 = Array(1,2,3,4)
console.log('array 2 参数以上:', arr1)    // array 2 参数以上: [ 1, 2, 3, 4 ]

二、ES6 新增的一些构造数组方法

1、Array.of(ES6)

用于将参数依次转化为数组的项。即使只有一个参数也作为数组的项

// 和 Array 区别(一个参数)
let arr2 = Array.of(2)
console.log('arr2 by Array.of:', arr2)    // arr2 by Array.of: [ 2 ]
arr2 = Array.of(4, 5, 6)
console.log('arr2 by Array.of:', arr2)    // arr2 by Array.of: [ 4, 5, 6 ]

2、Array.from(ES6)

用于从一个可迭代对象创建数组。设计初衷是为了快速创建数组。

 * Array.from(arrayLike[,processingFn[,thisAry]])
 * 第一个参数:类似数组的对象,必选;
 * 第二个参数:加工函数,新数组会经过这个函数加工再返回,可选;
 * 第三个参数:this 作用域,表示加工函数执行时 this 的值,可选;

下面展示用法:

// 当使用 function 和箭头函数时,中间打印结果会不一样
let obj = { 0: 'a', 1: 'b', 2: 'c', length: 3 }
let arr3 = Array.from(
  obj,
  function(value, index){
    console.log(value, index, this, arguments.length)
    return value.repeat(3) // 必须指定返回,否则返回的是 undefined
  },
  obj
)
// 打印结果
// a 0 { '0': 'a', '1': 'b', '2': 'c', length: 3 } 2
// b 1 { '0': 'a', '1': 'b', '2': 'c', length: 3 } 2
// c 2 { '0': 'a', '1': 'b', '2': 'c', length: 3 } 2

let obj = { 0: 'a', 1: 'b', 2: 'c', length: 3 }
let arr3 = Array.from(
  obj,
  (value, index) =>{
    console.log(value, index, this, arguments.length)
    return value.repeat(3) // 必须指定返回,否则返回的是 undefined
  },
  obj
)
// 打印结果
// a 0 {} 5
// b 1 {} 5
// c 2 {} 5

对于第一个参数:可迭代对象可以是 string、set、map、arguments

// 可以作为参数的可迭代对象有:string、set、map、arguments
console.log('array from String', Array.from('abc'))    // array from String [ 'a', 'b', 'c' ]
console.log('array from Set', Array.from(new Set(['abc', 'def'])))   // array from Set [ 'abc', 'def' ] 
console.log(
  'array from Map',
  Array.from(
    new Map([
      [1, 'abc'],
      [2, 'def'],
    ])
  )
)    // array from Map [ [ 1, 'abc' ], [ 2, 'def' ] ]
function fn() {
  return Array.from(arguments)
}
console.log('array from arguments:', fn(1, 2, 3))    // array from arguments: [ 1, 2, 3 ]

一个重要使用场景:生成一个从0到n 的数组

// length 就是要生成的数组个数
console.log(
  '0-n array:',
  Array.from({ length: 10 }, (v, i) => i)
) 
// 0-n array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 三、类型检测和原型

1、类型检测

现在检测数组都是用 Array.isArray(ES5 新增),对于以前的检测方法也要了解。

在 ES5 以前,Array 的检测并没有一个统一的标准,一般使用的方法有:

let arr5 = []
// instanceof
console.log('instanceof', arr5 instanceof Array)
// constructor
console.log('constructor:', arr5.constructor === Array)
// Array.prototype.isPrototypeOf
console.log('isPrototypeOf:', Array.prototype.isPrototypeOf(arr5))
// Object.getPrototypeOf
console.log('getPrototypeOf', Object.getPrototypeOf(arr5) === Array.prototype)
// Object.prototype.toString
console.log('prototype.tostring',Object.prototype.toString.apply(arr5) === '[object Array]')

上面的检测一般情况返回都是 true。

针对这些检测方法存在一些问题:

a、如 let arr6 = {__proto__: Array.prototype},对 arr6 使用上面的类型检测返回的都是 true,但 arr6 并不是 Array(可以称为伪造)

b、在多页面系统中,由于每个页面的 Array 引用地址都不一样,在一个页面检测另一个页面的 Array 就会不准确,可以直接在不同页面 

  打印 Object.getOwnPropertyNames(Array),返回的结果不一样
c、最准确的是最后一个 Object.prototype.toString 不会存在上面问题
 
所以一些主流的库对应 Array 的检测实现是这样的:
if (!Array.isArray) {
  Array.isArray = function (arg) {
    return Object.prototype.toString.call(arg) === '[object Array]'
  }
}

看浏览器是否支持 isArray,不支持使用 Object.prototype.toString 实现 isArray 函数。

2、原型
Array 本质上也是 Object,所以 Array 能使用的方法都是来自于 Array.prototype。
和 Object 一样可以通过扩展 Array.prototype 来增加数组的方法。
而且 Array.prototype 也是一个数组
console.log('Array.prototype is Array:', Array.isArray(Array.prototype))    // true
console.log(
  'Array.prototype is Array:',
  Object.prototype.toString.call(Array.prototype) === '[object Array]'
)    // true
console.log('Array property:', Object.getOwnPropertyNames(Array.prototype))    // 返回当前 Array 的方法

四、填鸭辩型

在开始展示 Array 的方法前,先来说一个词:填鸭辩型。

填鸭辩型,在这里的意思是:Array 的方法,由于设计上的巧妙可以用于类数组对象。

下面就用 Array.push 来演示下:(使用填鸭辩型特别注意类数组对象的 length 属性

/**
 * length 正确匹配属性个数,直接添加并 length+1
 * 1、当不存在 length 或者不能转为数值的时候,会替换第一个;length 不存在时会创建;length 为0 同;
 * 2、length 的值小于对象中属性个数;相当于在 length 处插入;
 * 3、length 的值大于等于对象中属性数,在最后添加,索引会跳跃;length 也会增加;
 */

// 当 length 正确匹配属性个数
let obj = { 0: 'football', 1: 'basketball',length :2 }
let objPushInt = Array.prototype.push.call(obj, 'golfball')
console.log('obj pushed:', obj) // obj pushed: { '0': 'football', '1': 'basketball', '2': 'golfball', length: 3 }
console.log('objPushInt:', objPushInt)  // objPushInt: 3

// 情形-1:length 不存在
let obj2 = { 0: 'football', 1: 'basketball' }
objPushInt = Array.prototype.push.call(obj2, 'golfball')
console.log('obj2 pushed:', obj2) // obj2 pushed: { '0': 'golfball', '1': 'basketball', length: 1 }
console.log('objPushInt:', objPushInt)  // objPushInt: 1
// 情形-2:length 小于属性个数
objPushInt = Array.prototype.push.call(obj2, 'volleyball') 
console.log('obj2 pushed(lenght):', obj2) // obj2 pushed(lenght): { '0': 'golfball', '1': 'volleyball', length: 2 }
console.log('objPushInt:', objPushInt)  // objPushInt: 2
// 情形-3:length 大于属性个数
obj2.length = 100
objPushInt = Array.prototype.push.call(obj2, 'football')
console.log('obj2 pushed(lenght==):', obj2) // obj2 pushed(lenght==): { '0': 'golfball', '1': 'volleyball', '100': 'football', length: 101 }
console.log('objPushInt:', objPushInt)  // objPushInt: 101

五、改变数组的9个方法

在 MDN 上面的说法是:修改器方法,就是可以使原数组改变的方法。

1、push

push 向数组尾部添加一个或多个元素,并返回新数组长度;该方法可以模拟栈的入栈操作(配合 pop 实现);可以模拟出入队列(配合 shift 实现);

可以使用“填鸭辩型”

/**
 * 语法:array.push(element1,...,elementN)
 */
let arr8 = ['football', 'basketball', 'volleyball']
let pushInt = arr8.push('golfball')
console.log('arr8 pushed:', arr8)
console.log('pushInt:', pushInt)

// 利用 push 根据 length 插入元素,可以实现数组合并(这里使用 apply 和call 的结果不一样)
let arr8_1 = ['red', 'blue']
pushInt = Array.prototype.push.apply(arr8, arr8_1)
console.log('合并后数组:', arr8)
console.log('合并后长度:', pushInt)

2、pop

pop 用于删除数组的最后一个元素,并返回这个元素;该方法可以模拟栈的出栈(配合 push 实现);也可以模拟队列的出队列(配合 unshift 实现);

pop 和 push 配合使用就可以实现栈数据结构(JS 中没有栈,可以自行实现)

可以使用“填鸭辩型”

/**
 * 语法:array.pop()
 */
let arr7 = ['cat', 'dog', 'cow', 'chicken', 'mouse']
let popItem = arr7.pop()
console.log('arr7 poped:', arr7)
console.log('popItem:', popItem)

3、shift

shift 删除数组的第一个元素,并返回这个元素;可以模拟栈的出栈(配合 unshift 实现);也可以模拟队列的出队列(配合 push 实现);

可以使用“填鸭辩型”

/**
 * array.shift()
 */
let arr10 = [1, 2, 3, 4, 5]
let shiftItem = arr10.shift()
console.log('arr10 shift:', arr10)    // arr10 shift: [ 2, 3, 4, 5 ]
console.log('shiftItme:', shiftItem)    // shiftItme: 1

4、unshift

unshift 向数组头部添加元素,可添加多个,返回新数组长度;可模拟栈的入栈(配合 shift 实现);也可以模拟队列的入队列(配合 pop 实现);

可以使用“填鸭辩型”

/**
 * array.unshift(element1,....,elementN)
 */
let arr11 = [1, 2, 3, 4, 5]
let unshiftInt = arr11.unshift(10, 12)
console.log('arr11 unshift:', arr11, ',unshiftInt:', unshiftInt)

5、fill(ES6)

fill 用一个固定值替换数组中从起始索引到结束索引(不包括结束索引),返回改变后数组引用(即原数组引用),原数组长度不变;

可以使用“填鸭辩型”

/**
 * 语法:array.fill(value[,start[,end=this.length]])
 */

/**
 * 参数:
 * 1、value:要替换为的值;
 * 2、start:可选,起始位置;没有传入,全部替换;
 * 3、end:可选,结束索引,默认到最后;小于 start 不替换;
 */

let arr16 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let arrFill = arr16.fill(100, 2, 5)    // 一般示例
let arrFill = arr16.fill(100)    // 没有起始位置,全部替换
let arrFill = arr16.fill(100, 4)    // 有起始位置,从起始位置到最后
let arrFill = arr16.fill(100, -20, -15)    // 不在范围内,不替换
let arrFill = arr16.fill(100, 4, 2)    // end 小于 start,不替换

6、sort

用于对数组排序,返回排序后的数组;

可以使用“填鸭辩型”

/**
 * 语法:array.sort([comparefn])
 * 可选参数:comparefn
 *  省略时:数组元素将按照各自转换为字符的 Unicode 位点进行排序(如:Boy 排在 apple 前,数字 25 排在 8 前)
 */
let arr12 = ['apple', 'Boy', 'cat', 'dog']
console.log('arr12 sorted:', arr12.sort())
arr12 = [4, 10, 20, 15]
console.log('arr12 number sorted:', arr12.sort())

/**
 * comparefn 有值:
 * 1、若 comparefn(a,b)<0,a 将排在 b 前面
 * 2、若 comparefn(a,b)=0,a 和 b 的相对位置不变
 * 3、若 comparefn(a,b)<0,a 和 b 的位置对调
 */
// 数字的comparefn 可以这么写
function compareNum(a, b) {
  return a - b
}

// 对于字符的,需要使用 localeCompare,排到正确顺序(本地 node 无效,浏览器下可以)
let arr13 = ['互', '联', '网', '改', '变', '世', '界']
let arr13_1 = arr13.sort()
let arr13_2 = ['互', '联', '网', '改', '变', '世', '界']
let arr13_3 = arr13_2.sort(function (a, b) {
  return a.localeCompare(b)
})
console.log('arr13 sort:', arr13_1, '
by localeCompare sort:', arr13_3)

7、reverse

reverse 使数组倒置,返回引用;可以使用“填鸭辩型”

let arr9 = [1, 2, 3, 4, 5]
console.log('arr9:', arr9)
let arr9_1 = arr9.reverse()
console.log('reverse arr9:', arr9_1)

8、splice

splice 改变数组,维持原数组引用,就地删除或者新增;

可以使用“填鸭辩型”

/**;
 * 语法:array.splice(start,deleteCount,[item1[,item2]....])
 */

/**
 * 参数说明:
 * 1、start:开始删除的索引;start 大于 length,则不删除;负值,是从 length+start 位置处删除,相加后还为负值,从0处开始删除;
 * 2、deleteCount:删除的个数;0 不删除,但应该至少添加一个元素;大于 start 后面的元素个数,后面全部删除;
 * 3、itemN:要添加的项;
 * 4、返回值:删除的数组,没有删除,返回空数组;
 */
let arr14 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let spliceArr = arr14.splice(4, 3, 10, 11, 12, 15)
console.log('arr14 spliced:', arr14, '
spliceArr:', spliceArr)

spliceArr = arr14.splice(-8, 1)
console.log('arr14 spliced start=-8:', arr14, '
spliceArr:', spliceArr)
spliceArr = arr14.splice(-15, 1)
console.log('arr14 spliced start=-15:', arr14, '
spliceArr:', spliceArr)

// 删除数组中的某个元素(当不存在时,把最后一个删除了,所以要判断)
arr14.splice(arr14.indexOf(3), 1)
console.log('arr14 delete 3:', arr14)

9、copyWithin(ES6)

copyWithin 用于数组内元素的替换,替换元素和被替换元素都是数组内的;原数组长度不变;

可以使用“填鸭辩型”

/**
 * 语法:copyWithin(target,start,[end=this.length])
 */

/**
 * 参数说明:
 * 1、target:目标元素,将被替换掉元素的索引;
 * 2、start:替换元素在数组中的起始索引;
 * 3、end:可选,默认为数组长度;end 小于 start时,不会替换;start 和 end 决定了替换和被替换的长度;
 * 4、返回:返回改变后数组的引用;
 */
let arr15 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
// let copyWithinArr = arr15.copyWithin(6, 3)
// let copyWithinArr = arr15.copyWithin(6, 3, 1)
let copyWithinArr = arr15.copyWithin(6, 3, 4)
console.log('arr15 copyWinthin:', arr15, '
copyWithinArr:', copyWithinArr)

六、不改变数组的9个方法

这些方法在 MDN 上面是:访问方法。不会改变自身,会返回一个新的数值或一个期望值。

1、join

join 用于使用给定的字符串连接数组的各个项。

可以使用“填鸭辩型”

/**
 * join 用指定的字符连接数组中的所有项,并返回连接后的字符串;
 * 默认不传值,是用【,】连接
 */
let arr18 = ['my', 'name', 'is', 'ZHT']
let arrJoin = arr18.join()
arrJoin = arr18.join(' ')
arrJoin = arr18.join('-')

2、toString

toString 用于连接数组的各个项,并返回这个字符串;

/**
 * 内在操作是:每个项目 toString,后使用 join 连接各个项;所以不能使用“填鸭辩型”,Object 中没有 join(虽然 Array.join 可以使用在 Object 上面,但是 Object 本身没有)
 * 所以是用【,】连接的各个项
*/ let arr20 = [1, 2, 3, 4, 5, 6] let arrToString = arr20.toString() console.log('arr20:', arr20, ' arrToString:', arrToString)

3、toLocaleString

toLocaleString 和上面的 toString 一样,只是在转换的时候本地化

4、toSource

toSource 返回当前数组字面量的字符串。这个一般没有被实现。

5、indexOf

indexOf 用于从数组中查找第一个和给定值相等的索引,不存在返回 -1

/**
 * 使用的是严格相等
 * 语法:array.indexOf(element[,start])
 * start 是开始查找位置,默认 0;
 */
let arr21 = [1, 2, 3, 4, 5, 6, 7, 8]
let arrIndexInt = arr21.indexOf(5)
console.log('arr21:', arr21, '
arrIndexInt:', arrIndexInt)

// 找出数组中某一个值的所有出现位置
function GetArrayAllIndex(arr, value) {
  let indices = []
  let idx = arr.indexOf(value)
  while (idx !== -1) {
    indices.push(idx)
    idx = arr.indexOf(value, idx + 1)
  }
  return indices
}
let allIndex = GetArrayAllIndex(
  ['a', 'b', 'c', 'd', 'e', 'a', 'g', 'a', 'a'],
  'a'
)
console.log('allIndex:', allIndex)    // allIndex: [ 0, 5, 7, 8 ]

6、lastIndexOf

lastIndexOf 从数组中查找给定值的索引,是从数组尾部向前查找。

start 为负值时,从(start + length) 处开始查找

7、includes(ES6)

includes 验证数组中是否包含给定值。

/**
 * includes 用于判断数组中是否存在要查找的值;有 true ,没有 false;
 * 语法:array.includes(value[,start])
 */

let arr22 = [1, 2, 3, 4, 5, 6, 7]
console.log('arr includes 5:', arr22.includes(5))

/**
 * 和 indexOf 有一点区别:
 * includes 可以判断 NaN ,indexOf 不可用
 */

let arr22_1 = [1, 2, 3, 4, 5, NaN, 7]
console.log('arr includes NaN:', arr22_1.includes(NaN)) // true
console.log('arr indexOf NaN:', arr22_1.indexOf(NaN)) // -1

8、concat

concat 用于连接数组、多个数组、数组项等,并返回新的数组;

可以使用“填鸭辩型”

/**
 * concat 用于连接数组、多个数组、值到当前数组尾部,并返回新的数组;原数组不变;会对传入的值扁平化处理(只能处理一级,对于嵌套的不行)。
 * 返回的数组中原数组中的项是--浅复制--过去的;
 */
let arr17 = [{ a: 1 }, 2, 3, 4]
let arrConcat = arr17.concat(5, [6, 7, [8,9]])
console.log('arrConcat:', arrConcat)  // [ { a: 1 }, 2, 3, 4, 5, 6, 7, [ 8, 9 ] ]
console.log('arr17[0] === arrConcat[0]:', arr17[0] === arrConcat[0])  // true

9、slice

slice 用于从原数组浅复制一部分项到新数组;

可以使用“填鸭辩型”

/**
 * slice 用于从原数组【浅复制】一部分项到新数组;返回这个新数组,原数组不变
 */

/**
 * 语法:array.slice([start[,end]])
 * 参数:
 * 1、start:可选,开始索引;负值,表示倒数第几个开始,默认 0;
 * 2、end:可选,结束索引;不包括该项;默认 length;
 */
let arr19 = [0, 1, 2, 3, 4, 5, 6, 7]
let arrSlice = arr19.slice(1, 5)
console.log('arr19:', arr19, '
arrSlice:', arrSlice)

// 当数组中有引用类型存在时,slice 生成的新数组是浅复制的
// 当新数组中的值改变时,原数组也会改变
let arr19_1 = [{ a: 100 }, 101, 102, 103]
let arrSlice_1 = arr19_1.slice(0, 3)
console.log('arr19_1:', arr19_1, '
arrSlice_1:', arrSlice_1)
arrSlice_1[0].a = 200
console.log('arr19_1:', arr19_1, '
arrSlice_1:', arrSlice_1)

七、数组的12个遍历方法

1、forEach

forEach 对数组的每一项执行传入的函数,如果函数操作了项,原数组改变;返回 undefined

/**
 * forEach 对数组中的每个项都执行一次传入的函数;如果函数操作了项,原数组改变;返回值 undefined;
 * 对于删除的、新增的、未初始化的会跳过,不执行;
 * 语法:array.forEach(fn[,thisArg])
 * fn:每一次执行的函数
 * thisArg:可选,传入的要绑定的 this 值
 */

let arr23 = [1, 2, 3, 4, 5, 6, 7]
arr23.forEach((item, index) => {
  console.log('第', index, '个的平方是:', item * item)
})

// 稀疏数组操作(未初始化的会跳过),null 不会跳过,会转为 0(有点坑)
arr23 = [1, 2, 3, null, , 6, 7]
arr23.forEach((item, index) => {
  console.log('第', index, '个的平方是:', item * item)
})

注意:

  即使传入的函数显示返回值,最终 forEach 也是 undefined

let arr23 = [1, 2, 3, 4, 5, 6, 7]
const arr23_for = arr23.forEach((item, index) => {
  return item * item
})
console.log(arr23_for)    // undefined

2、map

map 对数组的每一种执行传入的函数,并根据函数返回值组成一个新的数组(function 默认返回 undefined)

/**
 * map 用于返回一个新数组,是由原数组的每一个项执行一次给定的函数返回的结果;如果函数改变了原数组的项,原数组改变;
 * 对于删除的、新增的、未初始化的会跳过,不执行;null 不会跳过,会转为 0
 */
let arr26 = [1, 2, 3, 4, 5, 6,null, 7, 8]
let arrMap = arr26.map((item) => item * 2)  // 只有一句表达式,没有中括号,默认返回表达式的值
arrMap = arr26.map((item) => {item * 2})  // 返回 undefinde 组成的数组

3、find(ES6)

find 根据传入的函数,查找出第一个符合的项,并返回这个项;不存在返回 undefined

let arr28 = [1, 2, 3, 4, 5, 6, 7, 8]
function getNumber(value) {
  return value === 5
}
console.log('arr28 find 5:', arr28.find(getNumber))    // 5

4、findIndex(ES6)

findIndex 根据传入的函数,查找出第一个符合的项,并返回项索引;不存在返回 -1

let arr28 = [1, 2, 3, 4, 5, 6, 7, 8]
function getNumber(value) {
  return value === 5
}
console.log( 'findIndex 5:',arr28.findIndex(getNumber))    // 4
// 和 indexOf 区别,传入的参数不一样;这里是函数,能做的事情更多,更复杂

5、every

every 用于验证数组内的所有项是否都符合传入函数的验证

/**
 * 只有全部符合才返回 true ,否则 false
 */
let arr24 = [1, 2, 3, 4, 5, 6, 7, 8]
console.log('arr24 all >0:',arr24.every((item) => item > 0)) // true

6、some

some 正好和 every 相反,只有有一个满足传入函数验证,就返回 true。

let arr24 = [1, 2, 3, 4, 5, 6, 7, 8]
console.log('arr24 have >5:',arr24.some((item) => item > 5))  // true

// 数组交集(可对比多个数组)
const intersection = (list, ...args) => {
  console.log(args)
  return list.filter((item) => args.some((list) => list.includes(item)))
}

console.log(intersection([2, 1], [2, 3], [1, 4, 6])) // [2,1]

7、filter

filter 用于筛选数组中满足给定条件的项,并返回这些项的数组。

let arr25 = [1, 2, 3, 4, 5, 6, 7, 8]
console.log('arr25 中大于5的项:',arr25.filter((item) => item > 5))

// 下面是一个场景的使用
// 使用 filter 创建具有非0 id 的json
var arrJson = [
  { id: 15 },
  { id: -1 },
  { id: 0 },
  { id: 3 },
  { id: 12.2 },
  {},
  { id: null },
  { id: NaN },
  { id: 'undefined' },
]
function isNumber(obj) {
  return obj !== undefined && typeof obj === 'number' && !isNaN(obj)
}
function filterById(item) {
  if (isNumber(item.id) && item.id !== 0) {
    return true
  }
  return false
}
let arrJsonNoZeroId = arrJson.filter(filterById)
console.log('arrJsonNoZeroId :', arrJsonNoZeroId)

8、reduce

reduce 接收一个函数,用于汇总数组所有的项,并返回这个汇总结果。

以前一直以为这个是 ES6 的新方法,其实是挺早就有的。IE9 也支持该方法。

/**
 * 语法:array.reduce(callback(accumulator,currentValue[,index[,array]])[,initialValue])
 * 参数:
 *  1、callback:传入的汇总函数
 *    a、accumulator:是 callback 上一次调用返回的值,或者initialValue的值
 *    b、currentValue:当前项
 *    c、当前项的索引
 *    d、array 当前数组
 * 2、initialValue:callback 第一次调用的 accumulator 值
 *    a、未传值时,数组的第一个为该值,上面的回调函数会执行 length-1 次
 *    b、传值,会执行 length 次
 */
let arr27 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(
  'arr27 reduce +:',
  arr27.reduce((pre, cur) => pre + cur)
)
console.log(
  'arr27 reduce + 20:',
  arr27.reduce((pre, cur) => pre + cur, 20)
)

// reduce 示例
// 1、将二维数组转为一维数组(扁平化处理)
let arr27_1 = [
  [1, 2],
  [3, 4],
  [5, 6],
  [7, 8],
]
let arrReduce = arr27_1.reduce(function (a, b) {
  return a.concat(b)
}, [])
console.log('二维数组转一维数组:', arrReduce)

// 2、计算每个元素出现的次数(去重也可以)
let arr27_2 = ['abc', 'bcd', 'def', 'dgf', 'abc', 'def', 'abc']
function getNames(names, name) {
  if (name in names) {
    names[name]++
  } else {
    names[name] = 1
  }
  return names
}
let names = arr27_2.reduce(getNames, {})
console.log(
  '数组元素出现次数:',
  names,
  '
去重后的数组:',
  Object.getOwnPropertyNames(names)
)

// 3、根据属性,对一组对象分类
let arr27_3 = [
  { name: 'abc', age: 20 },
  { name: 'dbg', age: 21 },
  { name: 'edv', age: 20 },
]
function groupBy(objectArr, property) {
  return objectArr.reduce(function (acc, curItem) {
    let key = curItem[property]
    if (acc[key]) {
      acc[key].push(curItem)
    } else {
      acc[key] = [curItem]
    }
    return acc
  }, {})
}
console.log('对象数组分类:', groupBy(arr27_3, 'age'))

9、reduceRight

reduceRight 的使用和 reduce 是一样的,只是在执行的时候是从右侧开始。

10、entries(ES6)

11、keys(ES6)

12、values(ES6)

把上面三个放在一起来说。

共同点:返回的都是一个迭代器

不同点:迭代器内容不同,entries 是项和索引的键值对迭代器;keys 索引的迭代器;values 项的迭代器;

let arr29 = [1, 2, 3, 4, 5, 6, 7, 8]
console.log(
  'arr entries:',
  arr29.entries().next(),    // arr entrie: { value: [ 0, 1 ], done: false } 
  '
keys:',
  arr29.keys().next(),    // key: { value: 0, done: false }
  '
values:',
  arr29.values().next()    // value: { value: 1, done: false }
)

到这里 Array 的已经写完了,后续有新功能会继续更新。

原文地址:https://www.cnblogs.com/zhurong/p/14299245.html