什么是对象?
对象是一组属性的无序集合。每一个属性或者方法都是由一个名称标识来映射一个值。可以把对象想成一张散列表,内容就是一堆的名/值对。
对象的属性
对象的属性可以分为数据属性和访问器属性,使用了一些内部特性来描述属性的特征,通常会用两个中括号括起来,比如:[[Enumerable]]
- 数据属性
- [[Configurable]]: 属性是否可以通过delete删除并重新定义、是否可以修改属性特性以及是否可以改为防问器属性,默认值为true。
- [[Enumerable]]: 表示属性是否可以通过for-in循环返回,默认为true
- [[Writable]]: 表示属性的值是否可以被修改,默认为true
- [[Value]]: 包含属性实际的值,默认为undefined
- 访问器属性
- [[Configurable]]: 属性是否可以通过delete删除并重新定义、是否可以修改属性特性以及是否可以改为防问器属性,默认值为true。
- [[Enumerable]]: 表示属性是否可以通过for-in循环返回,默认为true
- [[Get]]: 获取函数,在读取属性时调用,默认为undefined
- [[Set]]: 设置函数,在写入属性时调用,默认为undefined
定义或修改属性
通常我们要修改属性的默认特性,就会使用Object.defineProperty()。访问器属性是不能直接定义的,必须使用 Object.defineProperty()
// 接收3个参数:要给其添加属性的对象、属性的名称、描述符对象(可以包含configurable、enumerable、writable、value)
let obj = { name : '这是一个对象' }
Object.defineProperty(obj,"name",{
writable: false,
value: "这是被修改后的对象"
})
console.log(obj.name) // 这是被修改后的对象
obj.name = 123
console.log(obj.name) // 这是被修改后的对象,因为writable被设置为false
如果想要定义多个属性,则可以使用Object.defineProperties(),可以通过多个描述符一次性定义多个属性,接受2个参数:添加或修改属性的对象 、 对象的描述符对象。
let obj1 = {};
Object.defineProperties(obj1, {
name: {
value: "obj1",
},
age1: {
value: 18,
configurable: true,
writable: true,
},
age2: {
get() {
return this.age1;
},
set(val) {
this.age1 += val;
},
},
});
obj1.age2 = 1;
console.log(obj1.age2); // 19
console.log(obj1.age1); // 19
读取属性
Object.getOwnPropertyDescriptor()可以取得指定属性的属性描述符。接收两个参数,属性所在的对象和属性名。返回一个对象,包含configurable、enumerable、数据属性则包含writable和value、访问器属性则包含get和set。
Object.getOwnPropertyDescriptors()则会在每个自有属性上调用Object.getOwnPropertyDescriptor(),并在新对象中返回他们。
let obj2 = {
name: "obj2",
age: 22,
};
console.log(Object.getOwnPropertyDescriptor(obj2, "name"));
// {value: 'obj2', writable: true, enumerable: true, configurable: true}
获取对象属性
Object.keys()可以获得对象上所有可枚举的实例属性。
Object.getOwnPropertyNames()可以列出所有实例属性,无论是否可以枚举。
Object.getOwnPropertySymbols()和getOwnPropertyNames类似,但是只针对符号。
对象合并
ES6中提供了一个专门合并对象的方法Object.assign()。接受一个目标对象,和一个或多个源对象作为参数,将每个源对象中可枚举属性和自有属性复制到目标对象。复制时,实际上会使用源对象上的[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值。
dest1 = {
name: "123",
set a(val) {
this.name = val;
},
};
src1 = {
get a() {
console.log("src1 get");
return "foo";
},
};
Object.assign(dest1, src1);
console.log(dest1); // {name:'foo',set a(val){...}}
实际上,assign()对每个源对象执行的是浅复制,如果有多个相同的属性,则后面的属性会覆盖掉前面的那个属性值。如果赋值的过程中出错了,操作就会终止并推出,并抛出一场,之前已经复制的值会存在于目标对象中。
对象迭代
for/in:循环指定对象的每个可枚举属性。对象继承的内置方法是不可枚举的,但是添加给对象的属性默认是可枚举的。
Object.values():返回对象值的数组。
Object.entries():返回对象内容的数组,是一个键/值对的数组。当然,如果有一个键/值对数组,想要转换成对象,则可以使用Object.fromEntries()。
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj); // { foo: "bar", baz: 42 }
console.log(Object.entries(obj)) // [["foo", "bar"],["baz", 42]]
Object.is()
该方法是ES6新增的方法,与全等类似,但是会考虑到一些边界情形。 使用全等:
console.log(-0 === +0) // true
console.log(NaN === NaN) // false
Object.is():
console.log(Object.is(+0,-0)) // false
console.log(Object.is(NaN,NaN)) // true