实现一些 Array 方法
- arrayLike
- forEach
- filter
- map
- reduce
- flat
- flatMap
类数组
js
// 类数组转化为数组
let arrayLike = {
0: 'tom',
1: '65',
2: '男',
3: ['jane', 'john', 'Mary'],
length: 4
}
// 1. [].slice
console.log([].slice.call(arrayLike))
// 2. Array.from
console.log(Array.from(arrayLike))
// 3. Array.apply
console.log(Array.apply(null, arrayLike))
// 4. [].concat
// 这个有点问题
console.log([].concat.apply([], arrayLike))
// 5. [...arr]
console.log([...arrayLike])
如何把一个数组随机打乱
js
// 如何把一个数组随机打乱
// 方案一
// 使用原生实现,Math.rondom() - 0.5 有时大于 0,有时小于 0 会达成这样的效果
;[1, 2, 3, 4].sort((x, y) => Math.random() - 0.5)
// 方案二
// 借用 lodash 可更方便
_.shuffle([1, 2, 3, 4])
//-> [3, 2, 4, 1]
创建一个数组,并填充值
比如创建一个数组大小为 100,每个值都为 0 的数组
点我查看详细
js
// 方法一:
Array(100).fill(0)
// 方法二:
// 注: 如果直接使用 map,会出现稀疏数组
Array.from(Array(100), (x) => 0)
// 方法二变体:
Array.from({ length: 100 }, (x) => 0)
如果要求填充自然数呢?
点我查看详细
js
Array.from({ length: 5 }, (x, i) => i)
实现数组的方法
手写数组 forEach 方法
手写之前,分析一位同学的如下写法,一直拿不到接口数据,是为什么?
js
const data = []
const urls = ['api1', 'api2', 'api3']
urls.forEach(async (url) => {
const res = await request(url)
data.push(res)
})
// 一直是空数组
console.log(data)
手写 forEach 方法,从 forEach 原理再看上面问题的所在?
js
Array.prototype.myForEach = function (fn, thisValue = []) {
for (let i = 0; i < this.length; i++) {
fn(this[i], i, this)
}
}
forEach 的问题
- 无法中断
- 无法进行异步排队
手写数组 filter 方法
js
Array.prototype.myFilter = function (fn, thisValue = []) {
let res = []
for (let i = 0; i < this.length; i++) {
if (fn(this[i], i, this)) {
res.push(this[i])
}
}
return res
}
手写数组 map 方法
js
Array.prototype.myMap = function (fn, thisValue = []) {
let res = []
let arr = this
for (let i in arr) {
res.push(fn(arr[i]))
}
return res
}
手写数组 reduce 方法
js
function myReduce(arr, fn, initialValue) {
var num = initValue === undefined ? (num = arr[0]) : initValue
var i = initValue === undefined ? 1 : 0
for (i; i < arr.length; i++) {
num = fn(num, arr[i], i)
}
return num
}
如何把一个数组 Array 转化为迭代器 Iterable
js
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const it = list[Symbol.iterator]()
it.next()
实现扁平化 flatten
实现 flatten 模拟 Array.prototype.flat,默认展开一层,可传递参数用以展开多层
数组降维
js
// ES2019 之前,可通过 reduce + concat 实现
// Array.prototype.concat 既可以连接数组又可以连接单项,十分巧妙
// 简单
// 方案一
const flatten = (list) => list.reduce((a, b) => a.concat(b), [])
// 方案二
const flatten = (list) => [].concat(...list)
// 深层数组打平
function flatten(list, depth = 1) {
if (depth === 0) return list
return list.reduce(
(a, b) => a.concat(Array.isArray(b) ? flatten(b, depth - 1) : b),
[]
)
}
// 使用迭代器实现
const flatten = function (target, depth = 1) {
const copy = [...target]
for (let i = 0; i < depth; ++i) {
const iter = copy[Symbol.iterator]()
let item = null
for (item = iter.next(); !item.done; ) {
// 注意:迭代器并不与可迭代对象某个时刻的快照绑定,而仅仅是用游标来记录遍历可迭代对象的历程,
// 如果可迭代对象在迭代期间被修改了,那么迭代器也会反映相应的变化
if (Array.isArray(item.value)) {
const temp = [...item.value]
let size = temp.length
for (let j = 0; j < size; ++j) {
item = iter.next()
}
copy.splice(copy.indexOf(item.value), 1, ...temp)
} else {
item = iter.next()
}
}
}
return copy
}
// 基于递归实现,不用 Array.concat
Array.prototype.myFlat = function (this: any[], depth: number = 1) {
const myFlat = (
arr: any[],
flatLength = 1,
resultArray = [] as any[],
forEachCount = 0
) => {
arr.forEach((d: any) => {
if (
Array.isArray(d) &&
(flatLength === -1 || forEachCount < flatLength)
) {
myFlat(d, flatLength, resultArray, forEachCount + 1);
} else {
resultArray.push(d);
}
});
return resultArray;
};
return myFlat(this, depth);
};
ES6 flat
js
let arr = [1, 2, [3, 4, [5, [6]]]]
console.log(arr.flat(Infinity))
// flat 参数为指定要提取嵌套数组的结构深度,默认值为 1
使用 reduce
实现
常见的递归版本
js
// 没控制深度,相当于 Infinity
const myFlat = function flat(arr) {
return arr.reduce((prev, cur) => {
return prev.concat(Array.isArray(cur) ? flat(cur) : cur)
}, [])
}
let arr = [1, 2, [3, 4, [5, [6]]]]
console.log(myFlat(arr, Infinity))
console.log([1, 2].concat(3, [4, 5]))
concat(valueN)
方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
concat 的参数 valueN
为数组和/或值,将被合并到一个新的数组中。如果省略了所有 valueN
参数,则 concat 会返回调用此方法的现存数组的一个浅拷贝。
扩展思考
能用迭代的思路去实现吗?
点我查看详细
js
function flatter(arr) {
if (!arr.length) return arr
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr)
}
return arr
}