Day-6-数组与对象

Day 6: 数组与对象

🎯 学习目标

  • 深入理解数组的操作和常用方法
  • 掌握对象的创建、访问和操作
  • 学会使用数组和对象的高级特性
  • 理解引用类型的工作原理

💡 核心概念

什么是数组?

数组是有序的数据集合,可以存储多个值。想象成一辆火车,每节车厢(索引)装着一个乘客(值)。

// 创建数组
let fruits = ["苹果", "香蕉", "橙子"];
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "你好", true, null];

什么是对象?

对象是无序的键值对集合。想象成一个档案柜,每个文件夹(属性)都贴着标签(键)。

// 创建对象
let person = {
    name: "张三",
    age: 25,
    city: "北京"
};

📚 数组详解

1. 创建数组

// 字面量方式(推荐)
let arr1 = [1, 2, 3];

// 构造函数
let arr2 = new Array(1, 2, 3);

// 创建空数组
let arr3 = [];
let arr4 = new Array();

// 创建指定长度的数组
let arr5 = new Array(5);  // [empty × 5]

2. 访问和修改数组元素

let colors = ["红", "绿", "蓝"];

// 访问元素(索引从0开始)
console.log(colors[0]);  // "红"
console.log(colors[1]);  // "绿"

// 修改元素
colors[0] = "橙色";
console.log(colors);  // ["橙色", "绿", "蓝"]

// 获取数组长度
console.log(colors.length);  // 3

3. 数组常用方法

添加元素

let arr = [1, 2, 3];

// push() - 在末尾添加
arr.push(4);           // [1, 2, 3, 4]
arr.push(5, 6);        // [1, 2, 3, 4, 5, 6]

// unshift() - 在开头添加
arr.unshift(0);        // [0, 1, 2, 3, 4, 5, 6]

// splice() - 在指定位置添加
arr.splice(3, 0, 3.5); // [0, 1, 2, 3.5, 3, 4, 5, 6]

删除元素

let arr = [1, 2, 3, 4, 5];

// pop() - 删除末尾
let last = arr.pop();   // 返回 5, arr = [1, 2, 3, 4]

// shift() - 删除开头
let first = arr.shift(); // 返回 1, arr = [2, 3, 4]

// splice() - 删除指定位置
arr.splice(1, 1);       // 删除索引1的1个元素, arr = [2, 4]

查找元素

let fruits = ["苹果", "香蕉", "橙子", "苹果"];

// indexOf() - 查找索引
console.log(fruits.indexOf("香蕉"));  // 1
console.log(fruits.indexOf("葡萄"));  // -1(未找到)

// lastIndexOf() - 最后一次出现的位置
console.log(fruits.lastIndexOf("苹果")); // 3

// includes() - 是否包含
console.log(fruits.includes("橙子")); // true
console.log(fruits.includes("葡萄")); // false

// find() - 查找符合条件的元素
let numbers = [1, 2, 3, 4, 5];
let found = numbers.find(num => num > 3); // 4

// findIndex() - 查找符合条件的索引
let index = numbers.findIndex(num => num > 3); // 3

遍历数组

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

// forEach() - 遍历每个元素
numbers.forEach((num, index) => {
    console.log(`索引${index}: ${num}`);
});

// map() - 转换数组,返回新数组
let doubled = numbers.map(num => num * 2); // [2, 4, 6, 8, 10]

// filter() - 过滤数组,返回新数组
let evens = numbers.filter(num => num % 2 === 0); // [2, 4]

// reduce() - 累计计算
let sum = numbers.reduce((total, num) => total + num, 0); // 15

// some() - 是否有符合条件的元素
let hasEven = numbers.some(num => num % 2 === 0); // true

// every() - 是否所有元素都符合条件
let allPositive = numbers.every(num => num > 0); // true

排序

let arr = [3, 1, 4, 1, 5, 9];

// sort() - 排序(默认按字符串排序)
arr.sort();  // [1, 1, 3, 4, 5, 9]

// 数字排序
arr.sort((a, b) => a - b);  // 升序
arr.sort((a, b) => b - a);  // 降序

// reverse() - 反转
arr.reverse(); // [9, 5, 4, 3, 1, 1]

其他方法

// join() - 转为字符串
let arr = [1, 2, 3];
console.log(arr.join("-"));  // "1-2-3"
console.log(arr.join());     // "1,2,3"

// slice() - 截取数组(不修改原数组)
let arr = [1, 2, 3, 4, 5];
let sliced = arr.slice(1, 3); // [2, 3]

// concat() - 合并数组
let arr1 = [1, 2];
let arr2 = [3, 4];
let merged = arr1.concat(arr2); // [1, 2, 3, 4]

// spread(...) - 展开数组
let arr = [1, 2, 3];
let newArr = [...arr, 4, 5]; // [1, 2, 3, 4, 5]

🗂️ 对象详解

1. 创建对象

// 字面量方式(推荐)
let person = {
    name: "张三",
    age: 25
};

// 构造函数
let obj = new Object();
obj.name = "李四";
obj.age = 30;

// Object.create()
let proto = { greet: function() { return "Hello"; } };
let obj = Object.create(proto);

2. 访问对象属性

let person = {
    name: "张三",
    age: 25,
    "full name": "张 三"  // 带空格的属性名
};

// 点表示法
console.log(person.name);  // "张三"

// 方括号表示法
console.log(person["age"]);  // 25
console.log(person["full name"]); // "张 三"(必须用方括号)

// 动态访问
let key = "name";
console.log(person[key]);  // "张三"

3. 添加和修改属性

let person = { name: "张三" };

// 添加属性
person.age = 25;
person.city = "北京";

// 修改属性
person.name = "李四";

// 方括号方式
person["email"] = "test@example.com";

4. 删除属性

let person = {
    name: "张三",
    age: 25,
    city: "北京"
};

delete person.age;
delete person["city"];
console.log(person); // {name: "张三"}

5. 对象方法

let calculator = {
    add: function(a, b) {
        return a + b;
    },
    
    subtract(a, b) {
        return a - b;
    }
};

console.log(calculator.add(5, 3));      // 8
console.log(calculator.subtract(5, 3)); // 2

6. 遍历对象

let person = {
    name: "张三",
    age: 25,
    city: "北京"
};

// for...in 遍历键
for (let key in person) {
    console.log(`${key}: ${person[key]}`);
}

// Object.keys() 获取所有键
let keys = Object.keys(person); // ["name", "age", "city"]

// Object.values() 获取所有值
let values = Object.values(person); // ["张三", 25, "北京"]

// Object.entries() 获取键值对数组
let entries = Object.entries(person);
// [["name", "张三"], ["age", 25], ["city", "北京"]]

🔄 引用类型

数组和对象是引用类型

// 赋值是引用,不是复制
let arr1 = [1, 2, 3];
let arr2 = arr1;
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4](也被修改了!)

// 对象同理
let obj1 = { name: "张三" };
let obj2 = obj1;
obj2.name = "李四";
console.log(obj1.name); // "李四"

复制数组和对象

// 数组复制
let arr = [1, 2, 3];
let copy1 = arr.slice();           // 方式1
let copy2 = [...arr];               // 方式2(spread)
let copy3 = Array.from(arr);        // 方式3

// 对象浅复制
let obj = { name: "张三", age: 25 };
let copy1 = Object.assign({}, obj); // 方式1
let copy2 = { ...obj };              // 方式2(spread)

// 深拷贝(嵌套对象)
let deepObj = {
    person: { name: "张三", age: 25 }
};
let deepCopy = JSON.parse(JSON.stringify(deepObj));

🎮 实战示例

示例1:购物车管理

class ShoppingCart {
    constructor() {
        this.items = [];
    }
    
    addItem(product, price, quantity = 1) {
        this.items.push({ product, price, quantity });
    }
    
    removeItem(index) {
        this.items.splice(index, 1);
    }
    
    getTotal() {
        return this.items.reduce((total, item) => {
            return total + item.price * item.quantity;
        }, 0);
    }
    
    getReceipt() {
        return this.items.map(item => 
            `${item.product}: ¥${item.price} × ${item.quantity}`
        ).join('\n');
    }
}

// 使用
let cart = new ShoppingCart();
cart.addItem("苹果", 5.5, 2);
cart.addItem("香蕉", 3.0, 3);
console.log(cart.getReceipt());
console.log(`总计: ¥${cart.getTotal()}`);

示例2:学生成绩管理

let students = [
    { id: 1, name: "张三", scores: { math: 90, english: 85 } },
    { id: 2, name: "李四", scores: { math: 78, english: 92 } },
    { id: 3, name: "王五", scores: { math: 88, english: 76 } }
];

// 找出数学平均分
let avgMath = students.reduce((sum, student) => 
    sum + student.scores.math, 0
) / students.length;

// 找出英语最高分的学生
let topEnglish = students.reduce((top, student) => 
    student.scores.english > top.scores.english ? student : top
);

// 筛选数学及格的学生
let mathPassed = students.filter(s => s.scores.math >= 80);

示例3:待办事项应用

class TodoApp {
    constructor() {
        this.todos = [];
    }
    
    add(title) {
        this.todos.push({
            id: Date.now(),
            title: title,
            completed: false,
            createdAt: new Date()
        });
    }
    
    toggle(id) {
        let todo = this.todos.find(t => t.id === id);
        if (todo) {
            todo.completed = !todo.completed;
        }
    }
    
    delete(id) {
        this.todos = this.todos.filter(t => t.id !== id);
    }
    
    list(filter = 'all') {
        switch (filter) {
            case 'active':
                return this.todos.filter(t => !t.completed);
            case 'completed':
                return this.todos.filter(t => t.completed);
            default:
                return this.todos;
        }
    }
}

✍️ 练习任务

练习1:数组去重

function unique(arr) {
    return [...new Set(arr)];
}

// 或使用 filter
function unique2(arr) {
    return arr.filter((item, index) => arr.indexOf(item) === index);
}

练习2:数组扁平化

function flatten(arr) {
    return arr.flat();
}

// 扁平化任意深度
function flattenDeep(arr) {
    return arr.flat(Infinity);
}

// 手动实现
function flattenManual(arr) {
    let result = [];
    arr.forEach(item => {
        if (Array.isArray(item)) {
            result = result.concat(flattenManual(item));
        } else {
            result.push(item);
        }
    });
    return result;
}

练习3:对象合并

function mergeObjects(...objs) {
    return Object.assign({}, ...objs);
}

// 或使用 spread
function mergeObjects2(...objs) {
    return { ...objs };
}

🎓 今日挑战

项目:图书管理系统

class Library {
    constructor() {
        this.books = [
            { id: 1, title: "JavaScript高级程序设计", author: "Nicholas", available: true },
            { id: 2, title: "深入浅出Node.js", author: "朴灵", available: true },
            { id: 3, title: "CSS揭秘", author: "Lea Verou", available: false }
        ];
        this.borrowed = {};  // { userId: [bookIds] }
    }
    
    // 添加图书
    addBook(title, author) {
        let id = this.books.length + 1;
        this.books.push({ id, title, author, available: true });
        return id;
    }
    
    // 借书
    borrowBook(userId, bookId) {
        let book = this.books.find(b => b.id === bookId);
        if (!book || !book.available) {
            return false;
        }
        book.available = false;
        if (!this.borrowed[userId]) {
            this.borrowed[userId] = [];
        }
        this.borrowed[userId].push(bookId);
        return true;
    }
    
    // 还书
    returnBook(userId, bookId) {
        let book = this.books.find(b => b.id === bookId);
        if (!book) return false;
        
        book.available = true;
        this.borrowed[userId] = this.borrowed[userId]
            .filter(id => id !== bookId);
        return true;
    }
    
    // 查找图书
    searchBooks(keyword) {
        return this.books.filter(book => 
            book.title.includes(keyword) || 
            book.author.includes(keyword)
        );
    }
    
    // 列出可借图书
    listAvailable() {
        return this.books.filter(book => book.available);
    }
}

💡 最佳实践

1. 使用 const 声明数组

const arr = [1, 2, 3];
arr.push(4);  // ✅ 可以修改内容
// arr = [];  // ❌ 不能重新赋值

2. 使用解构

// 数组解构
let [first, second, ...rest] = [1, 2, 3, 4, 5];

// 对象解构
let { name, age } = person;

3. 使用可选链

let city = person?.address?.city;  // 安全访问

4. 避免直接修改原数组

// ❌ 修改原数组
arr.sort();

// ✅ 创建新数组
let sorted = [...arr].sort();

学习时间: 2026-03-07 16:30
难度: ⭐⭐⭐⭐☆
预计用时: 3-4 小时
关键词: 数组, 对象, 方法, 引用类型
相关标签: #02-变量与数据