JavaScript 对象与数组

对象

一、对象基础概念

对象是 JavaScript 的核心数据类型,用于存储键值对集合。可以理解为现实事物的代码映射。

javascript 复制代码
// 一个简单的用户对象
let user = {
    name: "张三",        // 属性:键值对
    age: 25,
    "full name": "张三丰", // 包含空格的属性名需要引号
    sayHi: function() {    // 方法:值为函数的属性
        return "你好";
    }
};
  1. 使用对象字面量创建对象,避免 new Object()

  2. 属性名使用驼峰命名,除非必要不使用引号

  3. 使用解构赋值提高代码可读性

  4. 注意区分浅拷贝和深拷贝

  5. 使用 Object.freeze() 保护配置对象

  6. 遍历对象优先使用 Object.keys() 和 for...of

二、创建对象

1. 对象字面量(最常用)

字面量语法是创建对象最直接的方式,它允许我们在大括号`{}`中直接定义对象的属性和方法。

javascript 复制代码
const person = {
    name: "李四",
    age: 30,
    job: "工程师"
};
2. 使用 new Object()

这种方式在某些特定场景下可能更为适用,例如在需要动态创建对象并根据条件设置属性时。

javascript 复制代码
const person = new Object();
person.name = "王五";
person.age = 28;
3. 使用构造函数
javascript 复制代码
function Person(name, age) {
    this.name = name;
    this.age = age;
    this.greet = function() {
        return `你好,我是${this.name}`;
    };
}

const p1 = new Person("赵六", 22);
4. 使用 Object.create()
javascript 复制代码
const proto = {
    greet() {
        return "你好";
    }
};

const obj = Object.create(proto);
obj.name = "小明";
5. 使用类(ES6+)
javascript 复制代码
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    greet() {
        return `你好,我是${this.name}`;
    }
}

三、属性的增删改查

1. 访问属性
javascript 复制代码
const user = {
    name: "小红",
    age: 20,
    "my-email": "hong@example.com"
};

// 点号语法(最常用)
console.log(user.name);  // "小红"

// 方括号语法(特殊属性名、动态属性名时使用)
console.log(user["age"]);     // 20
console.log(user["my-email"]); // "hong@example.com"

// 动态属性名
const key = "name";
console.log(user[key]);  // "小红"

// 可选链操作符(ES2020)- 避免访问不存在的属性时报错
console.log(user?.address?.city);  // undefined(不会报错)
2. 添加/修改属性

向对象添加新属性非常简单,只需使用赋值语句即可。如果属性不存在,将会自动创建该属性并赋予相应的值。

javascript 复制代码
const car = {};

// 添加属性
car.brand = "特斯拉";
car["model"] = "Model 3";
car.year = 2023;

// 修改属性
car.year = 2024;

// 使用变量作为属性名
const propName = "color";
car[propName] = "红色";

console.log(car);
// { brand: "特斯拉", model: "Model 3", year: 2024, color: "红色" }
3. 删除属性

使用`delete`运算符可以删除对象的属性。删除后,该属性将不再属于对象,访问时会返回`undefined`。需要注意的是,`delete`操作符仅删除对象自身的属性,如果属性是继承而来,则不会被删除。

javascript 复制代码
const student = {
    name: "小明",
    age: 18,
    score: 95,
    password: "123456"
};

// 删除单个属性
delete student.password;

// 删除成功返回 true,属性不存在也返回 true
console.log(delete student.age);  // true
console.log(student);  // { name: "小明", score: 95 }

四、属性遍历和检查

1. 检查属性是否存在
javascript 复制代码
const obj = { name: "测试", age: 25 };

// 使用 in 运算符
console.log("name" in obj);    // true
console.log("job" in obj);     // false

// 使用 hasOwnProperty(检查自身属性,不包括原型链)
console.log(obj.hasOwnProperty("name"));  // true
console.log(obj.hasOwnProperty("toString")); // false(原型链上的方法)

// 直接判断是否为 undefined(不严谨,因为值可能确实是 undefined)
console.log(obj.name !== undefined);  // true
console.log(obj.job !== undefined);   // false
2. 遍历对象属性
javascript 复制代码
const person = {
    name: "张三",
    age: 30,
    job: "工程师"
};

// 1. for...in 循环(包括原型链上的可枚举属性)
for (let key in person) {
    console.log(key, person[key]);
}

// 2. Object.keys() - 返回自身属性的键数组
console.log(Object.keys(person));    // ["name", "age", "job"]

// 3. Object.values() - 返回自身属性的值数组
console.log(Object.values(person));  // ["张三", 30, "工程师"]

// 4. Object.entries() - 返回键值对数组
console.log(Object.entries(person)); 
// [["name", "张三"], ["age", 30], ["job", "工程师"]]

// 5. 遍历 entries
Object.entries(person).forEach(([key, value]) => {
    console.log(`${key}: ${value}`);
});

五、对象方法

对象的方法是存储在对象中的函数,用于定义对象的行为。

1. 定义方法

在对象中定义方法时,方法名后跟一个函数表达式。这样,对象就拥有了执行特定任务的能力。通过在对象中封装方法,将数据和操作数据的函数紧密结合在一起,体现了面向对象编程的思想。

javascript 复制代码
let calculator = {

    add: function(a, b) {
        return a + b;
    },

    subtract: function(a, b) {
        return a - b;
    }
};


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

使用箭头函数定义方法

javascript 复制代码
let mathUtils = {

    multiply: (a, b) => a * b,

    divide: (a, b) => a / b

};


console.log(mathUtils.multiply(4, 3)); // 输出 12
console.log(mathUtils.divide(10, 2)); // 输出 5

箭头函数提供了一种更为简洁的方法定义方式。在对象的方法中使用箭头函数时,需要注意`this`的指向问题。箭头函数中的`this`会继承其所在上下文的`this`值,这与传统函数的`this`行为有所不同。因此,在需要正确引用对象自身属性的情况下,需要谨慎使用箭头函数。

2. 常用对象方法
javascript 复制代码
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };

// Object.assign() - 合并对象
const merged = Object.assign({}, obj1, obj2);
console.log(merged);  // { a: 1, b: 2, c: 3, d: 4 }

// Object.freeze() - 冻结对象(不能修改、添加、删除)
const frozen = Object.freeze({ name: "固定值" });
frozen.name = "新值";    // 严格模式下会报错
console.log(frozen.name); // "固定值"(没变)

// Object.seal() - 密封对象(不能添加删除,但可以修改)
const sealed = Object.seal({ count: 10 });
sealed.count = 20;       // 可以修改
delete sealed.count;     // 不能删除
sealed.newProp = "新";   // 不能添加
3. 属性描述符
javascript 复制代码
const user = { name: "张三" };

// 获取属性描述符
const descriptor = Object.getOwnPropertyDescriptor(user, "name");
console.log(descriptor);
// {
//   value: "张三",
//   writable: true,   // 是否可修改
//   enumerable: true, // 是否可枚举
//   configurable: true // 是否可配置(删除、修改属性特性)
// }

// 定义属性的元数据
Object.defineProperty(user, "age", {
    value: 25,
    writable: false,   // 只读
    enumerable: false, // 不可枚举
    configurable: false // 不可配置
});

console.log(user.age);  // 25
user.age = 30;          // 修改无效(严格模式报错)
console.log(user.age);  // 25
console.log(Object.keys(user)); // ["name"](age不可枚举)

六、原型和继承

javascript 复制代码
// 原型链继承
const parent = {
    greet() {
        return "你好";
    }
};

const child = Object.create(parent);
child.name = "孩子";

console.log(child.name);      // "孩子"(自身属性)
console.log(child.greet());   // "你好"(原型上的方法)
console.log(child.toString()); // 原型链上的方法

// 获取原型
console.log(Object.getPrototypeOf(child)); // parent对象

// 检查是否是自身属性
console.log(child.hasOwnProperty("name"));   // true
console.log(child.hasOwnProperty("greet"));  // false

七、对象操作

1. 拷贝对象
javascript 复制代码
const original = { a: 1, b: { c: 2 } };

// 浅拷贝
const shallowCopy1 = Object.assign({}, original);
const shallowCopy2 = { ...original };

// 深拷贝
const deepCopy = JSON.parse(JSON.stringify(original));
// 注意:这种方法会丢失函数、undefined、Symbol等

// 简单深拷贝函数
function deepClone(obj) {
    if (obj === null || typeof obj !== "object") return obj;
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof Array) return obj.map(item => deepClone(item));
    
    const cloned = {};
    Object.keys(obj).forEach(key => {
        cloned[key] = deepClone(obj[key]);
    });
    return cloned;
}
2. 计算属性名
javascript 复制代码
const key = "dynamicKey";
const value = "动态值";

const obj = {
    [key]: value,           // 计算属性名
    [`${key}2`]: "另一个值",
    [1 + 2]: "三"           // 属性名可以是表达式
};

console.log(obj);  // { dynamicKey: "动态值", dynamicKey2: "另一个值", 3: "三" }
3. 方法简写
javascript 复制代码
const oldStyle = {
    name: "老方式",
    sayHi: function() {
        return "Hi";
    }
};

const newStyle = {
    name: "新方式",
    sayHi() {          // 方法简写
        return "Hi";
    },
    // 生成器函数简写
    *generator() {
        yield 1;
        yield 2;
    }
};

数组

一、数组基础概念

数组是 JavaScript 中用于存储有序集合的特殊对象。可以包含任意类型的元素。

二、创建数组

1. 数组字面量(最常用)
javascript 复制代码
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [];  // 空数组
const arr3 = [1, "two", true, null, undefined, { name: "对象" }];
2. 使用 Array 构造函数
javascript 复制代码
const arr1 = new Array();  // []
const arr2 = new Array(5); // [empty × 5] 长度为5的空数组
const arr3 = new Array(1, 2, 3); // [1, 2, 3]

// 注意陷阱
const arr4 = new Array(3);     // [empty × 3] - 长度为3的空数组
const arr5 = [3];              // [3] - 包含一个元素3的数组
3. 使用 Array.of() (ES6)
javascript 复制代码
const arr1 = Array.of(5);      // [5] - 解决了构造函数的歧义
const arr2 = Array.of(1, 2, 3); // [1, 2, 3]
4. 使用 Array.from() (ES6)
javascript 复制代码
// 从类数组对象创建
const str = "hello";
const arr1 = Array.from(str);  // ["h", "e", "l", "l", "o"]

// 从 Set 创建
const set = new Set([1, 2, 3]);
const arr2 = Array.from(set);  // [1, 2, 3]

// 使用映射函数
const arr3 = Array.from([1, 2, 3], x => x * 2); // [2, 4, 6]

// 创建指定范围的数组
const range = Array.from({ length: 5 }, (_, i) => i + 1); // [1, 2, 3, 4, 5]

三、数组的基本操作

1. 访问和修改元素
javascript 复制代码
const arr = ["a", "b", "c", "d"];

// 访问元素
console.log(arr[0]);     // "a" - 第一个元素
console.log(arr[arr.length - 1]); // "d" - 最后一个元素
console.log(arr[10]);    // undefined - 索引不存在

// 修改元素
arr[1] = "x";
console.log(arr);  // ["a", "x", "c", "d"]

// 添加元素到任意位置
arr[4] = "e";
console.log(arr);  // ["a", "x", "c", "d", "e"]
arr[10] = "z";
console.log(arr);  // ["a", "x", "c", "d", "e", empty × 5, "z"]
2. length 属性
javascript 复制代码
const arr = [1, 2, 3, 4];

console.log(arr.length);  // 4

// 截断数组
arr.length = 2;
console.log(arr);  // [1, 2]

// 清空数组
arr.length = 0;
console.log(arr);  // []

// 增加长度(会创建空位)
arr.length = 5;
console.log(arr);  // [empty × 5]

四、数组的遍历方法

1. 传统 for 循环
javascript 复制代码
const arr = ["a", "b", "c"];

// 基本for循环
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}

// 反向遍历
for (let i = arr.length - 1; i >= 0; i--) {
    console.log(arr[i]);
}
2. for...of 循环 (ES6)
javascript 复制代码
const arr = ["a", "b", "c"];

for (const item of arr) {
    console.log(item);
}

// 同时获取索引和值
for (const [index, value] of arr.entries()) {
    console.log(index, value);
}
3. for...in 循环(不推荐用于数组)
javascript 复制代码
const arr = ["a", "b", "c"];

// 遍历索引(包括自定义属性)
for (const index in arr) {
    console.log(index, arr[index]);
}
4. 数组内置遍历方法
javascript 复制代码
const arr = [1, 2, 3, 4, 5];

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

// map - 映射为新数组
const doubled = arr.map(item => item * 2);
console.log(doubled);  // [2, 4, 6, 8, 10]

// filter - 过滤元素
const evens = arr.filter(item => item % 2 === 0);
console.log(evens);  // [2, 4]

// reduce - 累积计算
const sum = arr.reduce((acc, item) => acc + item, 0);
console.log(sum);  // 15

// some - 是否有元素满足条件
const hasEven = arr.some(item => item % 2 === 0);
console.log(hasEven);  // true

// every - 是否所有元素满足条件
const allPositive = arr.every(item => item > 0);
console.log(allPositive);  // true

// find - 查找第一个满足条件的元素
const found = arr.find(item => item > 3);
console.log(found);  // 4

// findIndex - 查找第一个满足条件的索引
const foundIndex = arr.findIndex(item => item > 3);
console.log(foundIndex);  // 3

五、数组的增删改操作

1. 添加元素
javascript 复制代码
const arr = [1, 2, 3];

// push - 末尾添加(返回新长度)
arr.push(4, 5);
console.log(arr);  // [1, 2, 3, 4, 5]

// unshift - 开头添加(返回新长度)
arr.unshift(-1, 0);
console.log(arr);  // [-1, 0, 1, 2, 3, 4, 5]

// splice - 任意位置添加
arr.splice(3, 0, "a", "b");  // 在索引3处插入"a","b"
console.log(arr);  // [-1, 0, 1, "a", "b", 2, 3, 4, 5]
2. 删除元素
javascript 复制代码
const arr = [1, 2, 3, 4, 5];

// pop - 删除末尾元素(返回删除的元素)
const last = arr.pop();
console.log(last, arr);  // 5, [1, 2, 3, 4]

// shift - 删除开头元素(返回删除的元素)
const first = arr.shift();
console.log(first, arr);  // 1, [2, 3, 4]

// splice - 删除任意位置元素
const removed = arr.splice(1, 2);  // 从索引1开始删除2个
console.log(removed, arr);  // [3, 4], [2]

// 清空数组的几种方式
let arr2 = [1, 2, 3];
arr2.length = 0;      // 方式1
arr2 = [];            // 方式2
arr2.splice(0);       // 方式3
while(arr2.length) {  // 方式4
    arr2.pop();
}
3. 修改元素
javascript 复制代码
const arr = [1, 2, 3, 4, 5];

// 直接通过索引修改
arr[2] = 30;

// splice - 替换元素
arr.splice(1, 2, 20, 30);  // 从索引1开始删除2个,插入20,30
console.log(arr);  // [1, 20, 30, 4, 5]

// fill - 填充元素(ES6)
const filled = new Array(5).fill(0);  // [0, 0, 0, 0, 0]
arr.fill(9, 1, 3);  // 从索引1到3填充9
console.log(arr);  // [1, 9, 9, 4, 5]

六、数组的常用方法

1. 合并与切割
javascript 复制代码
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [5, 6];

// concat - 合并数组(返回新数组)
const merged = arr1.concat(arr2, arr3);
console.log(merged);  // [1, 2, 3, 4, 5, 6]

// slice - 切割数组(返回新数组)
const sliced = merged.slice(1, 4);  // [2, 3, 4]

// join - 连接为字符串
const str = merged.join("-");  // "1-2-3-4-5-6"

// split - 字符串分割为数组(字符串方法)
const arr = "a,b,c".split(",");  // ["a", "b", "c"]
2. 查找与判断
javascript 复制代码
const arr = [1, 2, 3, 2, 1];

// indexOf - 查找元素第一次出现的索引
console.log(arr.indexOf(2));     // 1
console.log(arr.indexOf(5));     // -1

// lastIndexOf - 查找元素最后一次出现的索引
console.log(arr.lastIndexOf(2)); // 3

// includes - 是否包含元素(ES6)
console.log(arr.includes(3));    // true
console.log(arr.includes(5));    // false

// find - 查找满足条件的第一个元素
const found = arr.find(item => item > 2);  // 3

// findIndex - 查找满足条件的第一个索引
const foundIndex = arr.findIndex(item => item > 2);  // 2
3. 排序与反转
javascript 复制代码
const arr = [3, 1, 4, 1, 5, 9, 2, 6];

// sort - 排序(修改原数组)
arr.sort();
console.log(arr);  // [1, 1, 2, 3, 4, 5, 6, 9](默认按字符串排序)

// 自定义排序
const numbers = [3, 1, 4, 1, 5, 9];
numbers.sort((a, b) => a - b);  // 升序
console.log(numbers);  // [1, 1, 3, 4, 5, 9]

numbers.sort((a, b) => b - a);  // 降序
console.log(numbers);  // [9, 5, 4, 3, 1, 1]

// reverse - 反转数组
const arr2 = [1, 2, 3, 4];
arr2.reverse();
console.log(arr2);  // [4, 3, 2, 1]
4. 数组转换
javascript 复制代码
const arr = [1, 2, 3, 4, 5];

// map - 映射
const doubled = arr.map(x => x * 2);

// filter - 过滤
const evens = arr.filter(x => x % 2 === 0);

// reduce - 归约
const sum = arr.reduce((acc, x) => acc + x, 0);

// flat - 扁平化数组(ES2019)
const nested = [1, [2, 3], [4, [5, 6]]];
const flat1 = nested.flat();        // [1, 2, 3, 4, [5, 6]]
const flat2 = nested.flat(2);       // [1, 2, 3, 4, 5, 6]

// flatMap - 映射后扁平化
const result = [1, 2, 3].flatMap(x => [x, x * 2]);
console.log(result);  // [1, 2, 2, 4, 3, 6]

七、多维数组

javascript 复制代码
// 创建二维数组
const matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

// 访问元素
console.log(matrix[1][2]);  // 6

// 遍历二维数组
for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < matrix[i].length; j++) {
        console.log(`matrix[${i}][${j}] = ${matrix[i][j]}`);
    }
}

// 创建指定大小的二维数组
function createMatrix(rows, cols, initial = 0) {
    return Array.from({ length: rows }, () => 
        Array.from({ length: cols }, () => initial)
    );
}

const matrix2 = createMatrix(3, 3, 0);
console.log(matrix2);  // [[0,0,0], [0,0,0], [0,0,0]]

八、数组的高级技巧

1. 数组去重
javascript 复制代码
const arr = [1, 2, 2, 3, 3, 3, 4, 4, 5];

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

// 使用 filter 和 indexOf
const unique2 = arr.filter((item, index) => arr.indexOf(item) === index);

// 使用 reduce
const unique3 = arr.reduce((acc, item) => {
    if (!acc.includes(item)) acc.push(item);
    return acc;
}, []);
2. 数组交集、并集、差集
javascript 复制代码
const arr1 = [1, 2, 3, 4];
const arr2 = [3, 4, 5, 6];

// 并集
const union = [...new Set([...arr1, ...arr2])];
console.log(union);  // [1, 2, 3, 4, 5, 6]

// 交集
const intersection = arr1.filter(item => arr2.includes(item));
console.log(intersection);  // [3, 4]

// 差集(arr1中有但arr2中没有)
const difference = arr1.filter(item => !arr2.includes(item));
console.log(difference);  // [1, 2]
3. 数组分组
javascript 复制代码
const arr = [
    { name: "张三", age: 25, department: "技术部" },
    { name: "李四", age: 30, department: "市场部" },
    { name: "王五", age: 28, department: "技术部" },
    { name: "赵六", age: 35, department: "市场部" }
];

// 按部门分组
const grouped = arr.reduce((acc, person) => {
    const dept = person.department;
    if (!acc[dept]) {
        acc[dept] = [];
    }
    acc[dept].push(person);
    return acc;
}, {});

console.log(grouped);
// {
//   技术部: [{ name: "张三", ... }, { name: "王五", ... }],
//   市场部: [{ name: "李四", ... }, { name: "赵六", ... }]
// }
4. 数组打平与链式调用
javascript 复制代码
const data = [
    { category: "水果", items: ["苹果", "香蕉"] },
    { category: "蔬菜", items: ["白菜", "萝卜"] }
];

// 获取所有物品
const allItems = data
    .flatMap(category => category.items)
    .map(item => item.toUpperCase())
    .filter(item => item.length > 2)
    .sort();

console.log(allItems);  // ["苹果", "香蕉", "白菜", "萝卜"](按拼音排序)

九、类数组对象

javascript 复制代码
// 常见的类数组对象
function example() {
    console.log(arguments);  // 类数组对象
    console.log(Array.isArray(arguments));  // false
    
    // 转换为真实数组
    const args1 = Array.from(arguments);
    const args2 = [...arguments];
    const args3 = Array.prototype.slice.call(arguments);
}

example(1, 2, 3);

// DOM 元素集合
const divs = document.querySelectorAll('div');
const divArray = Array.from(divs);  // 转换为数组
相关推荐
你怎么知道我是队长1 小时前
前端学习---HTML---文本标签
前端·学习·html
一次旅行1 小时前
XSS总结
前端·xss
摸鱼的春哥1 小时前
春哥的Agent通关秘籍10:本地RAG实战(上)
前端·javascript·后端
Hx_Ma161 小时前
回显逻辑详解
java
彭于晏Yan2 小时前
LangChain4j实战二:集成到Springboot
java·spring boot·后端·langchain
fengtangjiang2 小时前
nacos服务之间相互调用
android·java·开发语言
石牌桥网管2 小时前
正则表达式:匹配不包含指定字符串的文本
java·javascript·python·正则表达式·go·php
Hello.Reader2 小时前
Nuxt 4.2 + Tauri 2 接入指南把 Vue 元框架“静态化”后装进桌面/移动端
前端·javascript·vue.js
独隅2 小时前
macOS 查看与安装 Java JDK 全面指南(2026年版)
java·开发语言·macos