前言
在js中,对象类型是非常重要的类型之一,也是项目中数据处理常用的类型之一,虽然这种类型我们经常使用,但是它的方法却不怎么用的到或者很少用到,不知不觉js的对象方法已经来到了29个了,今天就来看看这29个方法的使用。
还有当typeof Array
的时候,会发现数组是一个对象类型,也可以说数组是一个特殊的对象,所以大多对象的方法都可以对数组使用,而且有些效果可能会使你大跌眼镜。
正文
1. ⭐defineProperty 劫持对象属性
了解vue的应该都比较熟悉这个方法了,因为在vue2中,底层的响应拦截就是使用的Object.defineProperty
加观察者模式实现的。首先说这个是因为描述对象在后面一些方法中都会使用到。
Object.defineProperty
方法接收三个参数:
第一个为目标对象,
第二个是要添加或修改的属性,
三个为描述对象,返回传入的对象。
描述对象的属性:
configurable
是否可配置的:默认为false,当为false的时候,该属性无法被配置。
enumerable
是否可枚举:默认false,当为false的时候,该属性无法被枚举,也是就是使用in操作符,或者for in 的时候无法被找到。
writable
是否可写:默认false,当为false的时候,该属性无法被修改。
value
属性值:默认undefined,可以是js中的任何类型任何值
get
方法:返回值默认undefined。当访问该属性的时候会调用此方法,访问时,所得到的属性值是该方法的返回值。
set
方法:默认值为undefined。当修改该属性时会调用此方法。
注意:当配置对象中存在value和writable属性时,不应该存在get和set方法,反之亦然。当value属性和get方法同时存在时,会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13
| const obj = { name: "iceCode" };
const newObj = Object.defineProperty(obj, "age", { value: "12", configurable: true, writable: true, enumerable: true, }); console.log(newObj, obj, newObj === obj);
|
如果非要在添加value的时候添加get和set方法则会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const obj = { name: "iceCode" };
const newObj = Object.defineProperty(obj, "age", { value: "12", configurable: true, writable: true, enumerable: true, get() { return this.value; }, set(val) { this.value = val; }, });
|

使用get
和set
来改变对象属性,有些情况可能我们在vue的项目中见到过,当打印当前数据的时候发现控制台中显示的数据并不是最新的,但是展开之后,反而却显示最新的数据了。
这是因为 JavaScript 中的对象是动态的,随时可能发生变化。当你使用 console.log 打印对象时,实际上是获取了这个对象在打印时的状态,而不是实时状态。而当你展开一个对象时,实际上是重新获取了这个对象的所有属性值,因此你看到的是最新的状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const obj = { name: "iceCode" }; let value = 12; const newObj = Object.defineProperty(obj, "age", { enumerable: true, configurable: true, get() { return value; }, set(val) { value = val; }, });
console.log(newObj, obj, newObj === obj);
|

对象说完了,但是当目标对象是一个数组的时候呢,结果又是如何的。
1 2 3 4 5 6 7 8 9
| const arr: string[] = []; const newObj = Object.defineProperty(arr, "age", { value: "iceCode", writable: true, enumerable: true, configurable: true, });
console.log(newObj);
|
猜到结果了吗?见过这种类型没。

那么这种类型是数组还是对象,访问它里面的值得时候又该如何访问呢
1 2 3 4 5
| console.log(newObj); console.log(Array.isArray(newObj)); console.log(newObj[0]); console.log(newObj.age);
|

这时再看这个数组它能使用那种方法遍历呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const arr: string[] = ["222"]; const newObj = Object.defineProperty(arr, "age", { value: "iceCode", writable: true, enumerable: true, configurable: true, });
for (const value of newObj) { console.log(value, "for of"); } newObj.forEach((item) => { console.log(item, "forEach"); }); for (const key in newObj) { console.log(key, newObj[key], "for in"); }
|

使用Object.defineProperty
往数组中添加一个对象可以得到一个令人大跌眼镜得效果,但这个添加得对象也属于是身在曹营心在汉了,即便是在数组中,但想要访问它或者遍历它必须要使用对象得方式才能得到它。
那如果对这个数组添加元素或者修改元素,又或者使用属性名修改元素又是怎么样的呢?
1 2 3 4 5 6
| newObj.push("123"); newObj.name = "不存在的"; newObj.age = 18; newObj[2] = "以存在的"; console.log(newObj);
|
最后的结果是相互不影响的,属性值可被更改,没有的属性可以被添加,使用push添加的元素已索引的形式单独存在。

2. ⭐defineProperties 劫持对象
这个方法可以说是**Object.defineProperty
的强化版**,相比较于Object.defineProperty
每次只能劫持一个属性对其配置,这个方法可以一次性对多个属性进行劫持。
Object.defineProperties
接收两个参数,
第一个目标对象,
第二个是一个对象,对象的每一个key就是要劫持属性值,对象的value是该属性值的配置对象。返回目标对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const obj = { name: "iceCode" }; const newObj = Object.defineProperties(obj, {
name: { value: "icedCode", writable: true, enumerable: true, }, age: { value: 18, writable: true, enumerable: true, }, }); console.log(newObj);
|
假如目标对象是一个数组的话和Object.defineProperty
是一样的,获取修改和添加值也都是相同的操作。

3. ⭐assign 对对象的浅拷贝
Object.assign
接收两个参数或以上的参数,
第一个参数是目标对象,
第二个或多是被拷贝的对象,返回目标对象本身。
这里的浅拷贝是将被拷贝对象的属性赋值到目标对象上
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const obj = { name: "iceCode" }; const age = { age: 18 }; const my= { interest: ["唱", "跳", "rap", "篮球"] } const newObj = Object.assign(obj, age, my);
console.log(newObj, newObj === obj);
obj.age = 24; obj.interest.push("足球"); console.log(obj, age, my);
|
假如目标对象是一个数组… 对,还是身在曹营心在汉的场景,并且类型是Array

4. create 以现有的对象作为原型创建一个新的对象
Object.create
创建一个新的对象,接收两个参数,创建新对象的原型对象,第二个参数同
Object.defineProperties
第二个参数相同,里面的值作为创建新对象本身的属性和属性值
1 2 3 4 5 6 7 8 9 10 11
| const obj = { name: "iceCode" };
const newObj = Object.create(obj, { age: { value: 12, writable: true, enumerable: true, configurable: true, }, }); console.log(newObj);
|

如果第一个参数是数组,那么这个新对象的原型就是数组
1 2 3 4 5 6 7 8 9 10 11
| const obj = { name: "iceCode" }; const interest = ["唱", "跳", "rap", "篮球"]; const newObj = Object.create(interest, { age: { value: 12, writable: true, enumerable: true, }, });
console.log(newObj, Array.isArray(newObj), Object.prototype.toString.call(newObj));
|

5. freeze 冻结对象
Object.freeze
方法冻结对象,使目标对象不可配置(使用Object.defineProperty
等方法修改对象的操作权限),不可扩展,不可删除,不可修改(包括改变原型)。接收一个要被冻结的对象,返回这个对象。
注意:一旦这个对象被冻结,那么这个操作就不可逆,该对象无法被解冻
1 2 3 4 5 6 7 8 9
| const obj = { name: "iceCode" }; const newObj = Object.freeze(obj);
newObj.name = "我是队长阿威呀"; delete newObj.name; newObj.age = 18; newObj.__proto__ = { age: 18 };
newObj.__proto__.age = 18;
|
6. isFrozen 判断对象是否被冻结
Object.isFrozen
判断一个对象是否被冻结,如果被冻结返回true,否则返回false。
- 一个空的不可扩展对象会认为是冻结对象
- 如果一个不可扩展的对象里有属性,且被删除了属性,会认为是冻结对象
- 如果一个不可扩展的对象里有属性,且被配置为不可配置并且不可写(
configurable: false,writable: false,
),会认为是冻结对象
- 如果一个不可扩展的对象里有一个访问器属性,且被配置为不可配置并且不可写(
configurable: false,writable: false,
),会认为是冻结对象
- 使用
Object.freeze
冻结一个对象会被认为是冻结对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const newObj1 = Object.preventExtensions({}); const newObj2 = Object.preventExtensions({ name: "iced" }); delete newObj2.name; const newObj3 = Object.preventExtensions({ name: "iced" }); Object.defineProperty(newObj3, "name", { configurable: false, writable: false, }); const newObj4 = Object.preventExtensions({ get name() { return "iceCode"; }, }); Object.defineProperty(newObj4, "name", { configurable: false, writable: false, });
const newObj = Object.freeze(obj); console.log(Object.isFrozen(newObj)); console.log(Object.isFrozen(newObj1)); console.log(Object.isFrozen(newObj2)); console.log(Object.isFrozen(newObj3)); console.log(Object.isFrozen(newObj4));
|
7. seal 密封一个对象
Object.seal
密封一个对象,与Object.freeze
相比可以更改对象中现有的属性,一样不可删除,不可添加等操作
1 2 3 4 5
| const obj = { name: "iceCode" }; const newObj = Object.seal(obj);
delete newObj.name; newObj.age = 18;
|
8. isSealed 判断一个对象是否被密封
Object.isSealed
判断一个对象是否被密封,如果被冻结返回true,否则返回false。判断基本如上,并且冻结的一定是密封的
- 一个空的不可扩展对象会认为是密封对象
- 如果一个不可扩展的对象里有属性,且被删除了属性,会认为是密封对象
- 如果一个不可扩展的对象里有属性,且被配置为不可配置(
configurable: false
),会认为是密封对象
- 如果一个不可扩展的对象里有一个访问器属性,且被配置为不可配置(
configurable: false
),会认为是密封对象
- 使用
Object.seal
密封一个对象会被认为是密封对象
- 使用
Object.freeze
冻结一个对象会被认为是密封对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const newObj1 = Object.preventExtensions({}); const newObj2 = Object.preventExtensions({ name: "iced" }); delete newObj2.name; const newObj3 = Object.preventExtensions({ name: "iced" }); Object.defineProperty(newObj3, "name", { configurable: false, }); const newObj4 = Object.preventExtensions({ get name() { return "iceCode"; }, }); Object.defineProperty(newObj4, "name", { configurable: false, }); const newObj5 = Object.seal(obj); const newObj = Object.freeze(obj); console.log(Object.isSealed(newObj)); console.log(Object.isSealed(newObj1)); console.log(Object.isSealed(newObj2)); console.log(Object.isSealed(newObj3)); console.log(Object.isSealed(newObj4)); console.log(Object.isSealed(newObj5));
|
9. preventExtensions 禁止对象扩展
Object.preventExtensions
禁止对象的扩展操作,除了不可以添加新的属性以外其他的都可以,原型一样不可以被重新指定
1 2 3 4 5
| const obj = { name: "iceCode" }; const newObj = Object.preventExtensions(obj);
newObj.name = "iced"; delete newObj.name;
|
10. isExtensible 判断一个对象是否可扩展
Object.isExtensible
判断一个对象是否可扩展,返回要给布尔值。默认的普通对象是可扩展的所以为true,不可扩展的对象为false。
- 当对象被冻结时是不可扩展的
- 当对象被密封时是不可扩展的
- 当对象被标记为不可扩展时是不可扩展的
1 2 3 4 5 6 7 8 9
| const obj = { name: "iceCode" }; const newObj = Object.preventExtensions(obj); const newObj1 = Object.seal(obj); const newObj2 = Object.freeze(obj);
console.log(Object.isExtensible({})); console.log(Object.isExtensible(newObj)); console.log(Object.isExtensible(newObj1)); console.log(Object.isExtensible(newObj2));
|
另外:冻结、密封、不可扩展方法对数组都是可用的,同样无法添加新的属性或修改等一些操作,这里就不演示了
11. ⭐fromEntries 将键值对列表转成对象
Object.fromEntries
会将键值对格式的列表转化成对象,比如Map类型数据,或二维数组数据
1 2 3 4 5 6 7 8 9 10
| const arr: any[] = [ ["2", "4"], ["name", "iceCdoe"], ["age", 18], ]; const map = new Map(arr); const newObj = Object.fromEntries(map); const newObj1 = Object.fromEntries(arr); console.log(newObj); console.log(newObj1);
|
12. ⭐hasOwnProperty 查找对象本身是否存在这个属性
Object.prototype.hasOwnProperty
是一个原型方法,实例对象调用此方法会查找到自身是否存在这个属性,返回一个布尔值,如果自身存在返回true,如果不存在或或者原型上存在返回false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const obj = { name: "iceCode" }; obj.__proto__ = { age: 18 };
console.log(obj.hasOwnProperty("name")); console.log(obj.hasOwnProperty("age")); console.log("age" in obj);
const obj = { name: "iceCode", hasOwnProperty() { return false; }, };
console.log(obj.hasOwnProperty("name")); console.log(obj.hasOwnProperty("age"));
|
13. ⭐hasOwn 检查对象本身是否存在这个属性
此方法兼容性一般,如果需要使用,生产环境需谨慎 Node 16.9+ chrome 93+
Object.hasOwn
是Object.prototype.hasOwnProperty
替代版,用来代替Object.prototype.hasOwnProperty
。
接收两个参数,
第一个为目标对象,
第二个是要查找的属性值。
返回一个布尔值,结果与Object.prototype.hasOwnProperty
相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const obj = { name: "iceCode", hasOwnProperty() { return false; }, }; obj.__proto__ = { age: 18 }; console.log(Object.hasOwn(obj, "name")); console.log(Object.hasOwn(obj, "age")); console.log(Object.hasOwn(obj, "hasOwnProperty")); console.log("age" in obj);
const arr = [1, 2, 3, 4, 5];
console.log(Object.hasOwn(arr, 0)); console.log(Object.hasOwn(arr, 10));
|
14. getOwnPropertyNames 返回一个包含自身属性(包括不可枚举)的数组
Object.getOwnPropertyNames
接收一个目标对象,返回一个数组,包含自身不可枚举的属性,但是不包括Symbol
属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const f = Symbol("f"); const obj = { name: "iceCode", age: 12, [f]: 1336444, }; Object.defineProperty(obj, "age", { enumerable: false, }); const objArr = Object.getOwnPropertyNames(obj);
console.log(objArr);
const arr = [1, 2, 3, 4, 5]; const objArr = Object.getOwnPropertyNames(arr); console.log(objArr);
|
15. getOwnPropertySymbols 返回一个包含自身Symbol属性的数组
Object.getOwnPropertySymbols
接收一个目标对象,返回一个数组,只包括目标对象的Symbol
属性
1 2 3 4 5 6 7 8 9 10 11
| const f = Symbol("f"); const obj = { name: "iceCode", age: 12, [f]: 1336444, }; Object.defineProperty(obj, "age", { enumerable: false, }); const objArr = Object.getOwnPropertySymbols(obj); console.log(objArr);
|
16. getPrototypeOf 获取对象的原型
Object.getPrototypeOf
获取目标对象的原型,接收一个参数,返回该目标对象上的原型对象
1 2 3 4
| const obj = { name: "iceCode", age: 12 }; const objs = Object.create(obj); const newObj = Object.getPrototypeOf(objs); console.log(obj === newObj);
|
17. setPrototypeOf 修改对象的原型
Object.setPrototypeOf
修改目标对象的原型,
接收两个参数,
第一个为目标对象,
第二个为要设置的原型对象。
此方法不建议使用,使用该方法修改原型的速度非常慢,建议使用Object.create创建一个新的对象
1 2 3 4 5
| const obj = { name: "iceCode", age: 12 }; const a = { sex: "男" }; const newObj = Object.setPrototypeOf(obj, a);
console.log(a === newObj.__proto__);
|
18. isPrototypeOf 判断一个对象是否存在于另一个对象原型上
Object.prototype.isPrototypeOf
作为Object
的原型方法,一般用在构造函数上。正常对象也可以使用,同样该方法也是不被保护的。接收一个目标函数,返回一个布尔值。
1 2 3 4 5
| const obj = { name: "iceCode", age: 12 }; const a = { sex: "男" }; const newObj = Object.setPrototypeOf(obj, a);
console.log(a.isPrototypeOf(newObj));
|
19. ⭐getOwnPropertyDescriptor 获取自身属性值的描述对象
Object.getOwnPropertyDescriptor
方法将会获取对象自身属性的描述对象,
接收两个参数,
第一个为目标对象,
第二个属性名,返回一个对该属性的描述对象。
描述对象上面有介绍的,修改返回描述对象的值不会改变原属性的值
1 2 3 4 5 6 7 8 9 10 11 12
| const obj = { name: "iceCode", age: 12 }; const newObj = Object.getOwnPropertyDescriptor(obj, "name"); newObj.value = "队长阿威"; console.log(newObj, obj);
|
20. ⭐getOwnPropertyDescriptors 获取自身所有属性值的描述对象
Object.getOwnPropertyDescriptors
方法将会获取对象自身所有属性的描述对象,接收一个目标对象,返回一个该对象所有属性描述对象。描述对象上面有介绍的,修改返回描述对象的值不会改变原属性的值
1 2 3 4 5 6 7
| const obj = { name: "iceCode", age: 12 }; const newObj = Object.getOwnPropertyDescriptors(obj); console.log(newObj);
|
21. propertyIsEnumerable 表明指定属性是否可枚举
Object.prototype.propertyIsEnumerable
原型方法,表明指定属性是否可枚举,接收一个指定属性的参数,返回要给布尔值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const obj = { name: "iceCode", age: 12 }; Object.defineProperty(obj, "age", { enumerable: false, }); const newObj = obj.propertyIsEnumerable("name"); const newObj1 = obj.propertyIsEnumerable("age"); console.log(newObj); console.log(newObj1);
const arr = [1, 2, 3, 4, 5]; const newArr = arr.propertyIsEnumerable(0); const newArr1 = arr.propertyIsEnumerable("length"); console.log(newArr); console.log(newArr1);
|
22. ⭐keys 返回自身可枚举属性的数组
Object.keys
接收一个目标对象,返回一个包含自身可枚举属性的数组。就是如果属性被配置为不可枚举或者是Symbol属性都无法找到。
1 2 3 4 5 6 7 8 9 10 11 12 13
| const f = Symbol("f"); const obj = { name: "iceCode", age: 12, [f]: "666" }; Object.defineProperty(obj, "age", { enumerable: false, }); const keys = Object.keys(obj);
console.log(keys);
const arr = [1, 2, 3, 4, 5]; const values = Object.keys(arr); console.log(values);
|
23. ⭐values 返回自身可枚举属性值的数组
Object.values
接收一个目标对象,返回一个包含自身可枚举属性值的数组。就是如果属性被配置为不可枚举或者是Symbol属性都无法获取到这个属性值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const arr = [1, 2, 3, 4, 5]; const f = Symbol("f"); const obj = { name: "iceCode", age: 12, [f]: "666" }; Object.defineProperty(obj, "age", { enumerable: false, }); const keys = Object.values(obj); console.log(keys);
const arr = [{ f: "666" }, 2, 3, 4, 5]; const values = Object.values(arr); values[0].f = "777"; values[1] = 666; console.log(values, arr);
|
24. ⭐entries 返回自身可枚举属性和属性值的数组
Object.entries
接收一个目标对象,返回一个包含自身可枚举属性和属性值的二维数组。就是如果属性被配置为不可枚举或者是Symbol属性都无法获取到这个属性值。
1 2 3 4 5 6 7 8 9 10 11 12
| const f = Symbol("f"); const obj = { name: "iceCode", age: 12, [f]: "666" }; Object.defineProperty(obj, "age", { enumerable: false, }); const entr = Object.entries(obj); console.log(entr);
const arr = [1, 2, 3, 4, 5]; const entr = Object.entries(arr); console.log(entr);
|
25. ⭐is 对比两个值是否相同
Object.is
会对比两个值是否相同,对比比较操作符更准确,
接收两个参数,
第一个对比的参数,第二个对比的参数。
返回一个布尔值,为对比结果。
1 2 3 4 5 6 7 8 9 10 11
| console.log(Object.is(+0, -0), +0 === -0); console.log(Object.is(0, +0), 0 === +0); console.log(Object.is(0, -0), 0 === -0); console.log(Object.is("iceCode", "iceCode"), "iceCode" === "iceCode"); console.log(Object.is(undefined, undefined), undefined === undefined); console.log(Object.is(NaN, NaN), NaN === NaN);
console.log(Object.is({}, {}), {} === {}); console.log(Object.is([], []), [] === []); console.log(Object.is(NaN, 0 / 0), NaN === 0 / 0);
|
26. valueOf toString toLocaleString
这三个方法都是Object原型上的方法,本身在Object对象上基本没有任何意义。valueOf方法旨在被派生对象重写,以实现自定义类型转换逻辑。toSting
方法旨在重写(自定义)派生类对象的类型转换的逻辑。toLocaleString
方法旨在由派生对象重写,以达到其特定于语言环境的目的。
因为js中所有类型(不包括null),都继承Object的原型,所有类型都有这三个派生方法,但它们的这三种方法都已经被重写,有着自身不同的效果。这里就不写了,只要知道在Object上这三个方法基本没有特殊效果就可以了。
27. groupBy 给定可迭代对象中的元素进行分组
注意:此方法兼容性极差,作为了解即可,目前为止node完全不支持,chrome需要117版本以上
Object.groupBy
方法接收两个参数,第一个是元素分组可迭代的对象(Array等),第二个是一个回调函数,传递两个参数,第一个参数为当前迭代的元素,第二个为当前迭代的索引。返回的对象具有每个组的单独属性,其中包含组中的元素的数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
const f = [ { name: 'i', sex: '男', age: 12 }, { name: 'ie', sex: '男', age: 12 }, { name: 'iq', sex: '女', age: 12 }, { name: 'iw', sex: '男', age: 12 }, { name: 'ie', sex: '女', age: 12 }, { name: 'ir', sex: '男', age: 12 }, { name: 'it', sex: '女', age: 12 }, ]
const newObj = Object.groupBy(arr, (v) => v.sex); console.log(newObj);
|
Object.groupBy
方法的第二个回调返回的属性是什么,就以什么相同属性值分组,例如如果返回是v.name,得到的结果如下:

最后
js中对象的方法虽然有众多,但是业务项目中几乎很少能够用到,如果是比较,感觉还是对比运算符会比Object.is
更方便一些。如果是对对象进行冻结、密封、禁止扩展的场景更是少之又少,访问原型链在业务需求里也是比较少的。访问对象中是否存在这个属性使用in
操作符更方便些,但也不乏会有极少数场景可以使用到Object.hasOwn
等方法
感觉使用Object.groupBy
对数据进行分组是比较好的方法,但是兼容性可谓是一言难尽。目前使用最多的就是Object.keys来对对象进行判断或者遍历
链接:https://juejin.cn/post/7294072846129004585