数组高级应用

数组解构

解构赋值可以从数组中提取值并赋给变量。

基本解构

const arr = [1, 2, 3];

// 基本解构
const [a, b, c] = arr;
console.log(a, b, c);  // 1 2 3

// 跳过元素
const [first, , third] = arr;
console.log(first, third);  // 1 3

// 剩余元素
const [head, ...tail] = arr;
console.log(head);  // 1
console.log(tail);  // [2, 3]

默认值

const arr = [1, 2];

// 默认值
const [a, b, c = 3] = arr;
console.log(a, b, c);  // 1 2 3

// undefined时使用默认值
const [x = 10, y = 20] = [undefined, 2];
console.log(x, y);  // 10 2

交换变量

let a = 1;
let b = 2;

// 交换值
[a, b] = [b, a];
console.log(a, b);  // 2 1

嵌套解构

const nested = [1, [2, 3], 4];

const [a, [b, c], d] = nested;
console.log(a, b, c, d);  // 1 2 3 4

函数返回值解构

function getCoords() {
    return [10, 20];
}

const [x, y] = getCoords();
console.log(x, y);  // 10 20

展开运算符

展开运算符(...)可以展开数组。

基本用法

const arr = [1, 2, 3];

// 展开数组
console.log(...arr);  // 1 2 3

// 复制数组
const copy = [...arr];
console.log(copy);  // [1, 2, 3]

// 合并数组
const arr2 = [4, 5, 6];
const merged = [...arr, ...arr2];
console.log(merged);  // [1, 2, 3, 4, 5, 6]

函数参数

const numbers = [1, 2, 3, 4, 5];

// 展开作为参数
console.log(Math.max(...numbers));  // 5
console.log(Math.min(...numbers));  // 1

// 等价于
Math.max.apply(null, numbers);

数组操作

const arr = [2, 3, 4];

// 开头添加
const newArr1 = [1, ...arr];  // [1, 2, 3, 4]

// 末尾添加
const newArr2 = [...arr, 5];  // [2, 3, 4, 5]

// 中间插入
const newArr3 = [...arr.slice(0, 2), 2.5, ...arr.slice(2)];
// [2, 3, 2.5, 4]

将类数组转为数组

// NodeList转数组
const divs = document.querySelectorAll("div");
const divArray = [...divs];

// arguments转数组
function foo() {
    const args = [...arguments];
}

// 字符串转数组
const chars = [..."东巴文"];  // ["东", "巴", "文"]

数组拷贝

浅拷贝

const arr = [1, 2, { name: "东巴文" }];

// slice
const copy1 = arr.slice();

// 展开运算符
const copy2 = [...arr];

// Array.from
const copy3 = Array.from(arr);

// concat
const copy4 = arr.concat();

// 注意:都是浅拷贝
copy1[2].name = "JavaScript";
console.log(arr[2].name);  // "JavaScript"

深拷贝

const arr = [1, 2, { name: "东巴文", nested: [3, 4] }];

// JSON方法(有局限性)
const deepCopy1 = JSON.parse(JSON.stringify(arr));

// structuredClone(现代浏览器)
const deepCopy2 = structuredClone(arr);

// 递归实现
function deepClone(obj) {
    if (obj === null || typeof obj !== "object") {
        return obj;
    }
    
    if (Array.isArray(obj)) {
        return obj.map(item => deepClone(item));
    }
    
    const cloned = {};
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            cloned[key] = deepClone(obj[key]);
        }
    }
    return cloned;
}

拷贝方法对比

方法 深度 函数/undefined 循环引用 东巴文建议
slice/spread 保留 支持 简单场景
JSON 丢失 报错 纯数据
structuredClone 报错 支持 现代环境

数组扁平化

将多维数组转为一维数组。

flat方法

const nested = [1, [2, 3], [4, [5, 6, [7, 8]]]];

nested.flat();           // [1, 2, 3, 4, [5, 6, [7, 8]]]
nested.flat(2);          // [1, 2, 3, 4, 5, 6, [7, 8]]
nested.flat(Infinity);   // [1, 2, 3, 4, 5, 6, 7, 8]

递归实现

function flatten(arr) {
    const result = [];
    
    for (const item of arr) {
        if (Array.isArray(item)) {
            result.push(...flatten(item));
        } else {
            result.push(item);
        }
    }
    
    return result;
}

flatten([1, [2, [3, [4]]]]);  // [1, 2, 3, 4]

reduce实现

function flatten(arr) {
    return arr.reduce((acc, cur) => {
        return acc.concat(Array.isArray(cur) ? flatten(cur) : cur);
    }, []);
}

指定深度

function flattenDepth(arr, depth = 1) {
    if (depth === 0) return arr;
    
    return arr.reduce((acc, cur) => {
        return acc.concat(
            Array.isArray(cur) ? flattenDepth(cur, depth - 1) : cur
        );
    }, []);
}

flattenDepth([1, [2, [3, [4]]]], 1);  // [1, 2, [3, [4]]]
flattenDepth([1, [2, [3, [4]]]], 2);  // [1, 2, 3, [4]]

数组去重

Set方法

const arr = [1, 2, 2, 3, 3, 3, 4];

// 使用Set
const unique = [...new Set(arr)];
console.log(unique);  // [1, 2, 3, 4]

// 或使用Array.from
const unique2 = Array.from(new Set(arr));

filter方法

const arr = [1, 2, 2, 3, 3, 3, 4];

const unique = arr.filter((item, index) => {
    return arr.indexOf(item) === index;
});

reduce方法

const arr = [1, 2, 2, 3, 3, 3, 4];

const unique = arr.reduce((acc, cur) => {
    if (!acc.includes(cur)) {
        acc.push(cur);
    }
    return acc;
}, []);

对象数组去重

const users = [
    { id: 1, name: "东巴文" },
    { id: 2, name: "张三" },
    { id: 1, name: "东巴文" }
];

// 按id去重
const unique = users.filter((user, index, arr) => {
    return arr.findIndex(u => u.id === user.id) === index;
});

// 使用Map
const unique2 = [...new Map(users.map(u => [u.id, u])).values()];

数组排序算法

冒泡排序

function bubbleSort(arr) {
    const result = [...arr];
    const len = result.length;
    
    for (let i = 0; i < len - 1; i++) {
        for (let j = 0; j < len - 1 - i; j++) {
            if (result[j] > result[j + 1]) {
                [result[j], result[j + 1]] = [result[j + 1], result[j]];
            }
        }
    }
    
    return result;
}

快速排序

function quickSort(arr) {
    if (arr.length <= 1) return arr;
    
    const pivot = arr[Math.floor(arr.length / 2)];
    const left = arr.filter(x => x < pivot);
    const middle = arr.filter(x => x === pivot);
    const right = arr.filter(x => x > pivot);
    
    return [...quickSort(left), ...middle, ...quickSort(right)];
}

归并排序

function mergeSort(arr) {
    if (arr.length <= 1) return arr;
    
    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));
    
    return merge(left, right);
}

function merge(left, right) {
    const result = [];
    let i = 0, j = 0;
    
    while (i < left.length && j < right.length) {
        if (left[i] <= right[j]) {
            result.push(left[i++]);
        } else {
            result.push(right[j++]);
        }
    }
    
    return result.concat(left.slice(i)).concat(right.slice(j));
}

排序算法对比

算法 时间复杂度 空间复杂度 稳定性 东巴文建议
冒泡排序 O(n²) O(1) 稳定 学习用
快速排序 O(n log n) O(log n) 不稳定 实际使用
归并排序 O(n log n) O(n) 稳定 需要稳定时
原生sort O(n log n) O(1) 取决于引擎 首选

TypedArray

TypedArray是处理二进制数据的数组类型。

类型列表

类型 说明 字节数
Int8Array 8位有符号整数 1
Uint8Array 8位无符号整数 1
Int16Array 16位有符号整数 2
Uint16Array 16位无符号整数 2
Int32Array 32位有符号整数 4
Uint32Array 32位无符号整数 4
Float32Array 32位浮点数 4
Float64Array 64位浮点数 8

创建TypedArray

// 从长度创建
const int8 = new Int8Array(5);  // [0, 0, 0, 0, 0]

// 从数组创建
const int16 = new Int16Array([1, 2, 3, 4, 5]);

// 从ArrayBuffer创建
const buffer = new ArrayBuffer(8);
const int32 = new Int32Array(buffer);

// 从另一个TypedArray创建
const copy = new Int8Array(int8);

操作TypedArray

const arr = new Int32Array([1, 2, 3, 4, 5]);

// 访问
arr[0];  // 1
arr.at(-1);  // 5

// 修改
arr[0] = 10;

// 方法
arr.length;  // 5
arr.set([6, 7], 3);  // 从索引3开始设置
arr.subarray(1, 3);  // 返回视图

// 遍历
for (const num of arr) {
    console.log(num);
}

ArrayBuffer

// 创建缓冲区
const buffer = new ArrayBuffer(16);  // 16字节

// 创建视图
const int32 = new Int32Array(buffer);  // 4个32位整数
const int8 = new Int8Array(buffer);    // 16个8位整数

// 共享同一块内存
int32[0] = 0x12345678;
console.log(int8[0]);  // 0x78(小端序)

DataView

const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);

// 设置值
view.setInt8(0, 127);
view.setInt16(1, 32767, true);  // true表示小端序
view.setFloat64(4, 3.14159);

// 读取值
view.getInt8(0);
view.getInt16(1, true);
view.getFloat64(4);

应用场景

// 处理二进制文件
const file = input.files[0];
const buffer = await file.arrayBuffer();
const bytes = new Uint8Array(buffer);

// WebSocket二进制数据
const ws = new WebSocket(url);
ws.binaryType = "arraybuffer";
ws.onmessage = (event) => {
    const data = new Uint8Array(event.data);
};

// Canvas像素操作
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const imageData = ctx.getImageData(0, 0, 100, 100);
const pixels = new Uint8ClampedArray(imageData.data);

数组实用技巧

随机打乱数组

function shuffle(arr) {
    const result = [...arr];
    for (let i = result.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [result[i], result[j]] = [result[j], result[i]];
    }
    return result;
}

shuffle([1, 2, 3, 4, 5]);

数组分块

function chunk(arr, size) {
    const result = [];
    for (let i = 0; i < arr.length; i += size) {
        result.push(arr.slice(i, i + size));
    }
    return result;
}

chunk([1, 2, 3, 4, 5, 6], 2);  // [[1, 2], [3, 4], [5, 6]]

数组差集

function difference(arr1, arr2) {
    return arr1.filter(x => !arr2.includes(x));
}

difference([1, 2, 3, 4], [2, 4]);  // [1, 3]

数组交集

function intersection(arr1, arr2) {
    return arr1.filter(x => arr2.includes(x));
}

intersection([1, 2, 3, 4], [2, 3, 5]);  // [2, 3]

数组并集

function union(arr1, arr2) {
    return [...new Set([...arr1, ...arr2])];
}

union([1, 2, 3], [2, 3, 4]);  // [1, 2, 3, 4]

下一步

掌握了数组高级应用后,让我们继续学习:

  1. 对象属性 - 深入对象属性
  2. 对象创建与继承 - 学习继承
  3. ES6类 - 学习ES6类语法

东巴文(db-w.cn) - 让编程学习更简单

🚀 东巴文寄语:数组高级应用是提升代码质量的关键,掌握解构、展开运算符、深浅拷贝、去重、排序等技巧,能让你写出更优雅、更高效的代码。在 db-w.cn,我们帮你进阶!