(十四) js中的数组相关方法
类数组对象
类数组定义
- 拥有length属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理)
- 不具有数组所具有的方法;
常见的类数组有:
arguments
对象- DOM方法的返回结果。比如
document.getElementsByTagName()
- 字符串
-
arguments对象
function fn() { console.log(arguments); [...arguments].forEach(element => { console.log(element); }); } fn(1, 2, 3, 4, 5)
-
字符串
var str = '1234' console.log(str.length); // 4 // 对于字符串长度的改变是不允许的, 但是可以利用字符串拼接的方式 str.push('5') // str.push is not a function Array.prototype.push.call(str, '5') //Cannot assign to read only property 'length' of object console.log(str); Array.prototype.forEach.call(str, item => console.log(item)) // 1 2 3 4
1. 字符串和数组转换
类数组 -> 数组
toString() 、 join()和split()
toString() 是一个所有对象都有的方法, 它把一个变量隐式转换成字符串
console.log(Array.prototype.toString.call([[[1, [[2], 3]], [4, 5], 6, 7]])); //1,2,3,4,5,6,7
console.log([[[1, [[2], 3]], [4, 5], 6, 7]].join()); // 1,2,3,4,5,6,7
console.log([[1, [[2], 3]], [4, 5], 6, 7].join('-')); // 1,2,3-4,5-6-7
toString全部转换, 而join只能分割一层
split用于把字符串转换成数组
var str = "123"
str.slpit() // ['123']
str.split('') // ['1','2','3']
[slice()方法的特殊操作](#5. 删改)
... (Array.from的语法糖)
var str = "12345"
console.log([...str]); // ["1", "2", "3", "4", "5"]
console.log(Array.from(str)); // ["1", "2", "3", "4", "5"]
2. 堆栈方法
push pop unshift shift
方法名 | push | pop | unshift | shift |
---|---|---|---|---|
操作 | 追加 | 尾删 | 头加 | 头删 |
返回值 | 数组长度 | 删除的值 | 数组长度 | 删除的值 |
3. 排序
reverse sort 随机排序
方法名 | reverse | sort |
---|---|---|
操作 | 数组元素反序 | 排序 |
返回值 | 改变后的原数组 | 改变后的原数组 |
// arr.reverse()
var arr = ['hello', 'world', 123] // [123, "world", "hello"]
console.log(arr, arr.reverse()); // [123, "world", "hello"]
sort()
方法用于对数组的元素进行排序,并返回数组。默认排序顺序是根据ASCII码升序。
sort语法:arrayObject.sort(sortby);参数sortby可选。规定排序顺序。必须是函数(函数名)。
注:如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。
如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:
- 若 a 小于 b,则返回一个小于 0 的值。即: 升序
- 若 a 等于 b,则返回 0。相等位置不变1
- 若 a 大于 b,则返回一个大于 0 的值。即: 降序
var arr = [1, 2, 3, 4]
console.log(arr, arr.sort((a, b) => {
return b - a
})); // [4,3,2,1] [4,3,2,1]
利用sort实现一个随机排序
function ramSort() {
return Math.random() - 0.5
}
console.log(arr.sort(ramSort));
4. 拼接
concat ES6的扩展运算符 ...
方法名 | concat | ... |
---|---|---|
返回值 | 新数组 | 新数组 |
对原数组修改 | 没有 | 没有 |
可拼接几层 | 一层 | 一层 |
var arr = [1, 2, 3]
// concat
console.log(arr, arr.concat(4, 5)); // [1,2,3,4,5] [1,2,3,4,5]
console.log(arr, arr.concat([4, 5])); // [1,2,3,4,5] [1,2,3,4,5]
console.log(arr, arr.concat([4, [5]])); // [1,2,3,4,5] [1,2,3,4,[5]]
// ...
console.log(arr, [...arr, 4, 5]); //[1,2,3,4,5] [1,2,3,4,5]
console.log(arr, [...arr, [4, 5]]); //[1,2,3,4,5] [1,2,3,[4,5]]
console.log(arr, [...arr, [4, [5]]]); //[1,2,3,4,5] [1,2,3,[4,[5]]
5. 删改
slice splice
方法 | slice | splice |
---|---|---|
返回值 | 删除后的数组 | 删除后的数组 |
原数组是否被修改 | 否 | 是 |
对于slice和splice的删除操作, 都是左闭右开区间
slice():
需要两个参数: start 删除的起始下标, end 删除的结束下标 => start必须小于end
var arr = [1, 2, 3, 4, 5]
console.log(arr, arr.slice()); // [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
console.log(arr, arr.slice(3)); // [1, 2, 3, 4, 5] [4, 5]
console.log(arr, arr.slice(2, 4)); // [1, 2, 3, 4, 5] [3, 4]
console.log(arr, arr.slice(-2)); // [1, 2, 3, 4, 5] [4, 5]
console.log(arr, arr.slice(-3, -1)); // [1, 2, 3, 4, 5] [3, 4]
// slice()的特殊操作
var str = "12345"
console.log(str, Array.prototype.slice.call(str)); // "12345" ["1", "2", "3", "4", "5"]
function fn() {
console.log(Array.prototype.slice.call(arguments)); // [1, 2, 3, 4, 5]
}
fn(1, 2, 3, 4, 5)
splice():
需要两个参数: start: 删除的起始下标, count: 需要删除的个数
- 当count < 0 时, 不进行操作
- 当count = 0时, 进行添加操作, 此时需要指定添加的元素
- 当count > 0时, 表示要删除的个数
var arr = [1, 2, 3, 4, 5]
console.log(arr, arr.splice()); // [1, 2, 3, 4, 5] []
console.log(arr, arr.splice(4)); // [1, 2, 3, 4] [5]
console.log(arr, arr.splice(-2)); // [1, 2, 3] [4, 5]
console.log(arr, arr.splice(0, 3)); // [4, 5] [1, 2, 3]
console.log(arr, arr.splice(0)); // [] []
// 添加元素
console.log(arr, arr.splice(0, 0, 'a', 12)); // ["a", 12, 1, 2, 3, 4, 5] []
6. 元素下标
indexOf lastIndexOf
方法名 | indexOf | lastIndexOf |
---|---|---|
返回值 | 找到: 元素下标 / 找不到: -1 | 找到: 元素下标 / 找不到: -1 |
var arr = [1, 2, 3, 4, 5, 3]
console.log(arr.indexOf(3)); // 2
console.log(arr.indexOf(6)); // -1
console.log(arr.lastIndexOf(3)); // 5
console.log(arr.indexOf(6)); // -1
7. 数组迭代
map forEach filter some every
1. forEach
作用像for一样
forEach没有返回值
forEach的第一个参数是一个回调函数, 可以接收三个参数: item index arr
var arr = [1, 2, 3, 4, 5]
// 基本使用
arr.forEach(function (item, index, arr) {
console.log(item, index, arr);
})
// 类数组(对象)的调用
function fn() {
[...arguments].forEach(item => console.log(item))
}
fn(1, 2, 3, 4, 5)
// 返回值 => 没有返回值
console.log(arr.forEach(function (item) {
return 1 // undefined
}));
// 与for的对比 对于稀疏数组的遍历
var arr = [1, , 3]
arr.forEach(function (item) {
console.log(item); // 1 3
})
for (let i = 0, l = arr.length; i < l; i++) {
console.log(arr[i]); // 1 undefined 3
}
// break continue 无法使用
var arr = [1, 2, 3, 4]
arr.forEach(function (item) {
if (item === 2) {
//break // SyntaxError: Illegal break statement
continue // SyntaxError: Illegal continue statement: no surrounding iteration statement
}
})
foreach还可以接收第二个参数, this指向
var arr = [1, 2, 3, 4, 5]
var newArr = []
arr.forEach(function (item) {
this.push(item)
}, newArr)
console.log(newArr); // [1, 2, 3, 4, 5]
var obj = {
items: [1, 2, 3],
print: function () {
console.log(this); // obj
this.items.forEach(function (item) {
console.log(this); // window
})
}
}
obj.print()
// 指定this指向
var obj = {
items: [1, 2, 3],
print: function () {
console.log(this); // obj
this.items.forEach(function (item) {
console.log(this); // obj
},this)
}
}
obj.print()
2. map
返回一个和原数组有映射关系的新数组 => 有返回值
返回一个新数组
map()
不会对空数组进行检测map()
不会改变原始数组
var arr = [1, 2, 3, 4, 5] var newArr = arr.map(function (item) { return item * 2 }) console.log(arr, newArr); // [1, 2, 3, 4, 5] [2, 4, 6, 8, 10]
练习:
// 需求: 返回一个新数组: 数组中的元素为每个数组中的最大值 function fn(arr) { return arr.map(function (item) { // apply和call的参数区别 return Math.max.apply(Math, item) }) } fn([ [4, 5, 6, 7], [12, 33, 45, 63], [123, 432, 423, 234], [1234, 5435, 4564, 7567] ]);
// apply和call的参数区别
call(thisObj, arg1, arg2, arg3, arg4);
apply(thisObj, [args]);
- thisObj:call和apply第一个参数是一样的,该参数将替代Function类里面的this对象。
- arg1,arg2....:是一个个的参数,
- args:一个数组或类数组,是一个参数列表。
3. filter
根据每一次返回值的真假来过滤数组 => 返回值必须是一个布尔值
返回一个新数组
简单理解: 只有满足 return 后面表达式的数组元素会被保留
var arr = [1, 2, 3, 4, 5]
console.log(arr.filter(item => {
return item > 2
})); // 3, 4, 5
练习:
// 需求: 过滤掉数组中的 undefined 和 null
var arr = [1, 2, 3, undefined, 4, null, 5]
var newArr = arr.filter(item => { return item != undefined })
console.log(newArr); // [1, 2, 3, 4, 5]
// undefined == null
4. some和every
两个方法都返回一个布尔值
var arr = [1, 2, 3, 4, 5]
var res1 = arr.some((item, index, arr) => {
// 只要有一个元素满足return后的表达式, 就返回true
return item % 2 === 0
})
var res2 = arr.every((item, index, arr) => {
// 只有数组中的每个元素都满足return后的表达式, 才返回true
return item % 2 === 0
})
console.log(res1, res2); // true false
8. reduce()
reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数,将其结果汇总为单个返回值。
返回一个值 (不像上面局限于数组)
callback中第一个参数是一个累加器 (accumulator)
几种使用场景
-
数组求和 基本使用
// 1. 数组求和 var arr = [0, 1, 2, 3, 4] var sum = arr.reduce((acc, item) => { return acc + item }) console.log(sum);
计算过程: 赋予累加器
acc
为数组第一个元素的值, 然后从数组第二个元素1
开始进行累加, 最后将最终的计算结果返回
事实上, 还可以为reduce指定第二个参数, 指定的第二个参数将作为callback
中accumulator
的初始值
-
数组元素是对象, 对元素对象的value求和
// 2. 数组元素是对象, 对元素对象的value求和 var arr = [ { a: 1 }, { b: 2 }, { c: 3 } ] var sum = arr.reduce((acc, item) => { var k = Object.keys(item)[0] return acc + item[k] }, 0) // 第二个参数 console.log(sum); // 6
-
二维数组转换成一维数组
var arr = [[1, 2], [3, 4], [5, 6]] var newArr = arr.reduce((acc, item) => { return acc.concat(item) }, []) console.log(newArr); // [1,2,3,4,5,6]
-
计算数组中每个元素出现的次数, 结果返回一个对象
var arr = ['a', 'b', 'a', 'c', 'd', 'c'] var res = arr.reduce(function (acc, item) { if (item in acc) { acc[item]++ } else { acc[item] = 1 } return acc }, {}) console.log(res); // {a: 2, b: 1, c: 2, d: 1}
-
按照某个属性对object进行分类
// 比如说: 按照年龄这个属性进行分类 // 结果为一个对象: key: 年龄的value值, value: 以数组存储, 数组元素是原始数据中的数据 // { // 20: [ // { name: 'Max', age: 20 }, // { name: 'Jane', age: 20 } // ], // 21: [{ name: 'Alice', age: 21 }] // } var people = [ { name: 'Alice', age: 21 }, { name: 'Max', age: 20 }, { name: 'Jane', age: 20 } ]; // 第一次答案 function groupBy(objectArr, basis) { return objectArr.reduce((newArr, item) => { var k = Object.keys(newArr); var indexK = k.indexOf(item[basis]) if (!(item[basis] in newArr)) { newArr[item[basis]] = [] } if (indexK) { newArr[item[basis]].push(item) } return newArr }, {}) } var newArr = groupBy(people, 'age') console.log(newArr); // MDN给的解法 两个思路相同, 只不过我个人绕了远路 function groupBy(objectArr, basis) { return objectArr.reduce((newArr, item) => { var k = item[basis]; if (!newArr[k]) { newArr[k] = [] } newArr[k].push(item) return newArr }, {}) } var newArr = groupBy(people, 'age') console.log(newArr); // 隔一些天后再写的解法 function classify(arr, tag) { return arr.reduce((acc, item) => { if (acc.hasOwnProperty(item[tag])) { acc[item[tag]].push(item) } else { acc[item[tag]] = [item] } return acc }, {}) }
-
数组去重
let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4]; var res = arr.reduce(function (acc, item) { if (acc.indexOf(item) === -1) { acc.push(item) } return acc }, []) console.log(res);
-
使用reduce实现map
// if (!Array.prototype.mapUsingReduce) { Array.prototype.mapUsingReduce = function(callback, thisArg) { return this.reduce(function(mappedArray, currentValue, index, array) { mappedArray[index] = callback.call(thisArg, currentValue, index, array) return mappedArray }, []) } } [1, 2, , 3].mapUsingReduce( (currentValue, index, array) => currentValue + index + array.length ) // [5, 7, , 10]