跳到主要内容

对象操作

访问对象属性

在 JavaScript 中,可以通过点语法或方括号语法来访问对象的属性:

let person = {
age: 18,
'full name': '张三',
};

console.log(person.age); // 18
console.log(person['full name']); // 张三

当属性名中包含特殊字符如空格时,必须使用方括号语法。使用点语法访问这样的属性会导致语法错误。

遍历对象属性

可以使用for...in循环遍历对象的所有可枚举属性:

let book = {
title: 'JavaScript高级程序设计',
author: 'Nicholas C. Zakas',
year: 2012,
};

for (let key in book) {
console.log(key + ': ' + book[key]);
}

for...in循环会遍历对象自身的和继承的可枚举属性。如果只想遍历对象自身的属性,可以使用hasOwnProperty()方法进行过滤:

for (let key in book) {
if (book.hasOwnProperty(key)) {
console.log(key + ': ' + book[key]);
}
}

扩展对象

在 ES6 中,可以使用扩展运算符...方便地给对象添加新属性。

给数组添加元素:

let numbers = [1, 2, 3];
let newNumbers = [...numbers, 4, 5];
console.log(newNumbers); // [1, 2, 3, 4, 5]

给对象添加属性:

let person = {
name: 'Alice',
age: 20,
};
let student = {
...person,
major: 'Computer Science',
};
console.log(student); // { name: 'Alice', age: 20, major: 'Computer Science' }

对象属性覆盖

当把两个对象合并时,如果存在同名属性,后面对象的属性值会覆盖前面的。利用这个特性可以很方便地实现参数默认值:

function uploadAvatar(options) {
let defaultOptions = {
type: 'jpg',
size: 100,
};

options = { ...defaultOptions, ...options };
console.log(options);
}

uploadAvatar({ size: 200 }); // { type: "jpg", size: 200 }

这里defaultOptions对象定义了默认的参数值,通过对象扩展语法和覆盖特性,就可以非常简洁地实现参数默认值和用户参数合并。

解构赋值设置默认值

在使用解构赋值语法从对象提取属性时,也可以方便地指定默认值:

function createChart({ width = 600, height = 400, backgroundColor = 'white' } = {}) {
console.log(width, height, backgroundColor);
}

createChart(); // 600 400 white
createChart({ width: 800 }); // 800 400 white

注意函数参数后面的= {}不能省略,否则在调用时不传参会报错,因为此时解构赋值的源是undefined

检测对象属性

判断对象是否包含某个属性,可以用in操作符或hasOwnProperty方法:

let car = {
brand: 'BMW',
};
let myCar = {
color: 'blue',
};

Object.setPrototypeOf(myCar, car);

console.log('color' in myCar); // true
console.log(myCar.hasOwnProperty('color')); // true

console.log('brand' in myCar); // true
console.log(myCar.hasOwnProperty('brand')); // false

in操作符会检查对象自身和原型链上是否包含某属性,而hasOwnProperty只检查对象自身。一般推荐使用hasOwnProperty,除非需要特意检查原型链。

数组对象转对象

有时我们需要将一个由对象组成的数组转换为对象。可以用reduce方法实现:

const courses = [
{
title: '媒体查询响应式布局',
category: 'CSS',
},
{
title: 'FLEX 弹性盒模型',
category: 'CSS',
},
{
title: 'MySQL 多表联查',
category: 'MySQL',
},
];

let courseMap = courses.reduce((map, course, index) => {
map[`${course.category}-${index + 1}`] = course;
return map;
}, {});

console.log(JSON.stringify(courseMap, null, 2));

结果为:

{
"CSS-1": {
"title": "媒体查询响应式布局",
"category": "CSS"
},
"CSS-2": {
"title": "FLEX 弹性盒模型",
"category": "CSS"
},
"MySQL-3": {
"title": "MySQL 多表联查",
"category": "MySQL"
}
}

合并对象

Object.assign方法可以将一个或多个源对象的可枚举属性复制到目标对象中:

const defaultSettings = {
theme: 'light',
fontSize: 16,
};

const userSettings = {
fontSize: 18,
};

Object.assign(defaultSettings, userSettings);

console.log(defaultSettings); // { theme: 'light', fontSize: 18 }

注意Object.assign会修改第一个参数对象。如果不想修改现有对象,可以传入一个空对象作为目标:

let mergedSettings = Object.assign({}, defaultSettings, userSettings);

获取对象的键和值

Object.keysObject.values方法分别用于获取一个对象的所有键和值:

const person = {
name: 'Alice',
age: 18,
};

console.log(Object.keys(person)); // ['name', 'age']
console.log(Object.values(person)); // ['Alice', 18]

对象的浅拷贝

有几种常见方式可以实现对象的浅拷贝:

使用Object.assign:

const origin = {
a: 1,
b: 2,
};

const copy = Object.assign({}, origin);
console.log(copy); // { a: 1, b: 2 }

使用扩展运算符:

const copy2 = { ...origin };
console.log(copy2); // { a: 1, b: 2 }

这两种方式都只能实现对象的浅拷贝,如果属性值是引用类型,拷贝的只是引用地址。

对象的深拷贝

实现对象的深拷贝需要遍历对象的所有属性,如果属性值是引用类型则递归拷贝。一个简单的实现:

function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}

const copy = Array.isArray(obj) ? [] : {};

for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepClone(obj[key]);
}
}

return copy;
}

使用这个deepClone函数:

const origin = {
a: 1,
b: {
c: 2,
},
};

const copy = deepClone(origin);
console.log(copy); // { a: 1, b: { c: 2 } }

copy.b.c = 3;
console.log(origin.b.c); // 2

修改copy不会影响origin,实现了真正的深拷贝。实际项目中推荐使用成熟的第三方库如lodashcloneDeep方法来实现深拷贝,而不是自己实现。

工厂函数创建对象

可以用工厂函数批量创建结构类似的对象:

function createPerson(name, age) {
return {
name,
age,
sayHi() {
console.log(`Hi, I'm ${this.name}`);
},
};
}

const alice = createPerson('Alice', 18);
alice.sayHi(); // Hi, I'm Alice

const bob = createPerson('Bob', 20);
bob.sayHi(); // Hi, I'm Bob

查看对象属性描述符

Object.getOwnPropertyDescriptor方法可以获取对象某个属性的描述符对象。Object.getOwnPropertyDescriptors方法可以获取对象所有属性的描述符对象:

const person = {
name: 'Alice',
age: 18,
};

console.log(Object.getOwnPropertyDescriptor(person, 'name'));
// { value: 'Alice', writable: true, enumerable: true, configurable: true }

console.log(JSON.stringify(Object.getOwnPropertyDescriptors(person), null, 2));
/*
{
"name": {
"value": "Alice",
"writable": true,
"enumerable": true,
"configurable": true
},
"age": {
"value": 18,
"writable": true,
"enumerable": true,
"configurable": true
}
}
*/

属性描述符对象包含 4 个属性:

  • value:属性值
  • writable:是否可修改
  • enumerable:是否可枚举
  • configurable:是否可配置

通过Object.definePropertyObject.defineProperties方法可以定义这些属性特性。