对象操作
访问对象属性
在 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.keys
和Object.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
,实现了真正的深拷贝。实际项目中推荐使用成熟的第三方库如lodash
的cloneDeep
方法来实现深拷贝,而不是自己实现。
工厂函数创建对象
可以用工厂函数批量创建结构类似的对象:
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.defineProperty
或Object.defineProperties
方法可以定义这些属性特性。