创建对象
//使用对象字面量、构造函数或者Object.create()方法来创建对象
// 对象字面量
const person = {
name: 'John',
age: 30,
hobbies: ['reading', 'swimming']
}
// 构造函数
function Car(make, model) {
this.make = make;
this.model = model;
}
const myCar = new Car('Toyota', 'Corolla');
// Object.create()
const animal = {
speak() {
console.log(`${this.name} makes a noise.`);
}
};
const dog = Object.create(animal);
dog.name = 'Buddy';
//Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它会返回目标对象。这个方法常用于合并对象或为对象添加多个属性。
const car = {
make: "Honda",
model: "Civic"
}
const newCar = Object.assign({}, car, {
year: 2024,
color: "blue"
})
console.log(car); // 原对象未被修改
console.log(newCar); // 新对象包含了所有属性
访问属性
//访问对象属性有两种方式,分别是点号表示法和方括号表示法
//使用点号表示法 如属性名包含空格、特殊字符,或是一个变量,点表示法无法使用console.log(person.name); // 'John'
//使用方括号表示法 方括号表示法允许使用字符串或变量作为属性名
console.log(person['age']); // 30
// 动态访问
const key = 'hobbies';
console.log(person[key]); // ['reading', 'swimming']
修改和添加属性
//可以直接通过赋值来修改或添加对象的属性
person.age = 31 // 修改现有属性
person.city = 'New York' // 用点表示法添加新属性
person['isStudent'] = false // 用方括号表示法添加属性
//结合方括号表示法,使用变量作为属性名
const person = {
name: "Charlie"
}
const propName = "age" // 属性名存储在变量中
const propValue = 35 // 属性值存储在变量中
// 使用变量作为属性名来添加属性
person[propName] = propValue
console.log(person)
// { name: 'Charlie', age: 35 }
// 使用变量作为属性名用于修改
person[propName] = 36
console.log(person.age) // 输出: 36
// ES6 计算属性名,在创建对象时就需要动态生成属性名的场景下非常有用
const dynamicKey = "email"
const domain = "example.com"
const user = {
name: "David",
// 计算属性名:属性名由表达式 dynamicKey 的结果决定
[dynamicKey]: "david@" + domain,
// 更复杂的表达式
["user-" + Math.random().toString(36).substr(2, 9)]: "some-value"
}
console.log(user)
// { name: 'David', email: 'david@example.com', user-xxxxxx: 'some-value' }
// Object.assign () 方法
const car = {
make: "Toyota",
model: "Camry"
}
// 使用 Object.assign() 添加多个属性
Object.assign(car, {
year: 2023,
color: "silver",
// 可以覆盖现有属性
model: "Corolla"
})
console.log(car)
// { make: 'Toyota', model: 'Corolla', year: 2023, color: 'silver' }
const newCar = Object.assign({}, car, {
year: 2024,
color: "blue"
})
console.log(car); // 原对象未被修改
console.log(newCar); // 新对象包含了所有属性
删除属性
//使用delete操作符能够删除对象的属性。
delete person.hobbies; // 删除hobbies属性
console.log(person.hobbies); // undefined
检查属性是否存在
//in操作符和hasOwnProperty()方法可以检查对象是否拥有某个属性
console.log('name' in person); // true
console.log(person.hasOwnProperty('city')); // true
遍历对象
//使用for...in循环、Object.keys()、Object.values()和Object.entries()来遍历对象
// for...in循环
for (const key in person) {
console.log(key, person[key]);
}
// Object.keys() - 获取所有键
console.log(Object.keys(person)); // ['name', 'age', 'city', 'isStudent']
// Object.values() - 获取所有值
console.log(Object.values(person)); // ['John', 31, 'New York', false]
// Object.entries() - 获取所有键值对
console.log(Object.entries(person));
// [['name', 'John'], ['age', 31], ['city', 'New York'], ['isStudent', false]]
冻结对象
//Object.freeze()可以阻止修改对象的属性
const frozen = Object.freeze({ x: 5 });
frozen.x = 10; // 操作被忽略(严格模式下会报错)
console.log(frozen.x); // 5
密封对象
//Object.seal()允许修改现有属性,但不能添加或删除属性。
const sealed = Object.seal({ y: 10 });
sealed.y = 20; // 可以修改
sealed.z = 30; // 无效(严格模式下会报错)
delete sealed.y; // 无效
嵌套对象
//对于嵌套对象,可以链式访问和修改属性
const user = {
profile: {
address: {
city: 'London'
}
}
};
console.log(user.profile.address.city); // 'London'
user.profile.address.zip = 'SW1A 1AA'; // 添加嵌套属性
对象方法
//对象的属性值也可以是函数,这样的属性被称为方法
const calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
}
};
console.log(calculator.add(5, 3)); // 8
this关键字
//在对象方法内部,this指向调用该方法的对象
const person = {
name: 'Alice',
greet() {
console.log(`Hello, ${this.name}!`);
}
};
person.greet(); // 'Hello, Alice!'
对象的键与值
let obj = { "id": "1", "name": "millia", "color": "red"}
/* Object.keys() */
/* 返回一个包含 javascript 对象键的数组
如果键是数字,Object.keys() 函数将按排序顺序返回数字键的数组,键值仍将保持与原始 javascript 对象相同的值映射*/
console.log(Object.keys(obj))
//["id", "name", "color"]
/* Object.entries() */
/* 将整个对象拆分为小数组。每个数组由 [key, value] 形式的键值对组成*/
console.log(Object.entries(obj))
//0: (2) ['id', '1']
//1: (2) ['name', 'millia']
//2: (2) ['color', 'red']
console.log(JSON.stringify(Object.entries(obj)))
//[["id","1"],["name","millia"],["color","red"]]
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`);
}
//id: 1
//name: millia
//color: red
/*for 循环*/
for(let i in obj) {
console.log(i) // 键
console.log(obj[i]) //值
}
// id
// 1
// name
// millia
// color
// red
/* Object.values() */
/* 静态方法返回一个给定对象的自有可枚举字符串键属性值组成的数组 */
console.log(Object.values(obj))
//['1', 'millia', 'red']
对象的合并
/* Object.assign(target,...sources) */
/* target 需要应用源对象属性的目标对象,修改后将作为返回值
sources 一个或多个包含要应用的属性的源对象
注意target对象有值还是空值{} */
let target = { a: 1, b: 2 }
let source = { b: 4, c: 5 }
console.log(Object.assign({},target, source),target,source)
//{a: 1, b: 4, c: 5} {a: 1, b: 2} {b: 4, c: 5}
console.log(target,Object.assign(target, source))
//{a: 1, b: 4, c: 5} {a: 1, b: 4, c: 5}
/* 展开运算符... 解构赋值 */
/* 可以在函数调用/数组构造时,将数组表达式或者 string 在语法层面展开;还可以在构造字面量对象时,将对象表达式按 key-value 的方式展开。(注: 字面量一般指 [1, 2, 3] 或者 {name: "mdn"} 这种简洁的构造方式)
函数调用:myFunction(...iterableObj)
字面量数组构造或字符串:[...iterableObj, '4', ...'hello', 6]
构造字面量对象,进行克隆或者属性拷贝:let objClone = { ...obj }
*/
const obj1 = { a: 1, b: 2 }
const obj2 = { b: 3, c: 4 }
const obj3 = { ...obj1, ...obj2 }
console.log(obj3)
// { a: 1, b: 3, c: 4 }
对象的去重
//基于单个属性去重使用Array.reduce()或者Map来实现
const people = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Charlie' } // 重复id
];
// 使用Map
const uniqueById = [...new Map(people.map(person => [person.id, person])).values()];
console.log(uniqueById); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
// 等效方法(使用reduce)
const uniqueById2 = people.reduce((acc, current) => {
const x = acc.find(item => item.id === current.id);
if (!x) {
return acc.concat([current]);
} else {
return acc;
}
}, []);
//深度比较对象去重,当需要比较对象的所有属性是否都相同时,可以使用JSON.stringify()或者自定义比较函数。不过要注意,JSON.stringify()有局限性,比如它无法处理函数、undefined或者循环引用的情况。
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ name: 'Alice', id: 1 } // 属性顺序不同,但内容相同
];
// 使用JSON.stringify()(简单但有局限性)
const uniqueByStringify = [...new Set(users.map(user => JSON.stringify(user)))]
.map(str => JSON.parse(str));
console.log(uniqueByStringify); // 去重成功
// 自定义深度比较函数
function isEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (!obj2.hasOwnProperty(key) || !isEqual(obj1[key], obj2[key])) return false;
}
return true;
}
const uniqueByDeepCompare = users.filter((current, index, self) =>
self.findIndex(item => isEqual(item, current)) === index
);
//处理嵌套对象,如果对象包含嵌套结构,去重会更复杂一些,需要递归比较所有层级的属性。
const items = [
{ id: 1, details: { age: 30 } },
{ id: 2, details: { age: 25 } },
{ id: 1, details: { age: 30 } } // 完全重复
];
// 自定义深度比较(支持嵌套对象)
function deepCompare(obj1, obj2) {
if (obj1 === obj2) return true;
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) return false;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (obj1[key] instanceof Date && obj2[key] instanceof Date) {
if (obj1[key].getTime() !== obj2[key].getTime()) return false;
} else if (!deepCompare(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
const uniqueItems = items.filter((current, index, self) =>
self.findIndex(item => deepCompare(item, current)) === index
);
//基于多个属性去重,当需要根据对象的多个属性组合来判断是否重复时,可以这样实现。
const products = [
{ name: 'Apple', category: 'Fruit' },
{ name: 'Banana', category: 'Fruit' },
{ name: 'Apple', category: 'Fruit' } // 重复组合
];
const uniqueProducts = products.reduce((acc, current) => {
const key = `${current.name}-${current.category}`;
const isDuplicate = acc.some(item => `${item.name}-${item.category}` === key);
if (!isDuplicate) {
acc.push(current);
}
return acc;
}, []);
去重方法选择建议
- 基于单个属性去重:推荐使用Map,因为它的时间复杂度是 O (n),效率较高。
- 深度比较去重:如果对象结构简单,可以使用JSON.stringify();如果对象结构复杂,建议使用自定义比较函数或者 Lodash。
- 性能考虑:对于大型数组,使用
Map或者Set的方法性能更好,因为它们的查找效率比Array.find()更高。