前言

温故知新,顺便做下知识总结。把别人的好文拿来看一遍,顺便做下笔记,瞧一瞧加深记忆。

在 js 中,对象类型是非常重要的类型之一,也是项目中数据处理常用的类型之一,虽然这种类型我们经常使用,但是它的方法却不怎么用的到或者很少用到,不知不觉 js 的对象方法已经来到了 29 个了,今天就来看看这 29 个方法的使用。
还有当 typeof Array 的时候,会发现数组是一个对象类型,也可以说数组是一个特殊的对象,所以大多对象的方法都可以对数组使用,而且有些效果可能会使你大跌眼镜。

正文

Object.defineProperty 劫持对象属性

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/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
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const obj = { name: 'iceCode' }
//这里接不接收返回值都可以,因为返回也是传入的对象。
//age属性值不存在obj对象里,这里就会新增。
const newObj = Object.defineProperty(obj, 'age', {
value: '12',
configurable: true,
writable: true,
enumerable: true,
})
console.log(newObj, obj, newObj === obj)
//{ name: 'iceCode', age: '12' }
//{ name: 'iceCode', age: '12' }
//true
// 如果非要在添加value的时候添加get和set方法则会报错。
const obj = { name: 'iceCode' }
//这里接不接收返回值都可以,因为返回也是传入的对象。
//age属性值不存在obj对象里,这里就会新增。
const newObj = Object.defineProperty(obj, 'age', {
value: '12',
configurable: true,
writable: true,
enumerable: true,
get() {
return this.value
},
set(val) {
this.value = val
},
})

Object.defineProperties 劫持对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties

这个方法可以说是 Object.defineProperty 的强化版,相比较于 Object.defineProperty 每次只能劫持一个属性对其配置,这个方法可以一次性对多个属性进行劫持。

Object.defineProperties 接收两个参数,第一个目标对象,第二个是一个对象,对象的每一个 key 就是要劫持属性值,对象的 value 是该属性值的配置对象。返回目标对象。

Object.assign 对对象的浅拷贝

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Object.assign 接收两个参数或以上的参数,第一个参数是目标对象,第二个或多是被拷贝的对象,返回目标对象本身。 这里的浅拷贝是将被拷贝对象的属性赋值到目标对象上

1
2
3
4
5
6
7
const obj = { name: 'iceCode' }
const age = { age: 18 }
const my = { interest: ['唱', '跳', 'rap', '篮球'] }
const newObj = Object.assign(obj, age, my)
//这里返回值和目标对象是同一个对象
console.log(newObj, newObj === obj)
//{name: 'iceCode', age: 18, interest: ["唱", "跳", "rap", "篮球"]} true

Object.create 以现有的对象作为原型创建一个新的对象

1
2
3
4
5
6
7
8
9
10
11
const obj = { name: 'iceCode' }
//obj作为原型对象,第二个参数作为新对象本身的参数
const newObj = Object.create(obj, {
age: {
value: 12,
writable: true,
enumerable: true,
configurable: true,
},
})
console.log(newObj)

Object.freeze 冻结对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/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

Object.isFrozen 判断对象是否被冻结

  1. 一个空的不可扩展对象会认为是冻结对象
  2. 如果一个不可扩展的对象里有属性,且被删除了属性,会认为是冻结对象
  3. 如果一个不可扩展的对象里有属性,且被配置为不可配置并且不可写( configurable: false,writable: false,),会认为是冻结对象
  4. 如果一个不可扩展的对象里有一个访问器属性,且被配置为不可配置并且不可写( configurable: false,writable: false,),会认为是冻结对象
  5. 使用 Object.freeze 冻结一个对象会被认为是冻结对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen

Object.seal 密封一个对象

Object.seal 密封一个对象,与 Object.freeze 相比可以更改对象中现有的属性,一样不可删除,不可添加等操作

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/seal

1
2
3
4
5
6
7
8
9
10
11
12
const object1 = {
property1: 42,
}

Object.seal(object1)
object1.property1 = 33
console.log(object1.property1)
// Expected output: 33

delete object1.property1 // Cannot delete when sealed
console.log(object1.property1)
// Expected output: 33

Object.isSealed 判断一个对象是否被密封

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed

Object.preventExtensions 禁止对象扩展

可以防止新属性被添加到对象中(即防止该对象被扩展)。它还可以防止对象的原型被重新指定。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions

Object.isExtensible 判断一个对象是否可扩展

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible

Object.fromEntries 将键值对列表转成对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries

Object.fromEntries 会将键值对格式的列表转化成对象,比如 Map 类型数据,或二维数组数据

1
2
3
4
5
6
7
8
9
10
11
12
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)
//{2: '4', name: 'iceCdoe', age: 18}
console.log(newObj1)
//{2: '4', name: 'iceCdoe', age: 18}

Object.prototype.hasOwnProperty 查找对象本身是否存在这个属性

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/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 }
//此方法与in 操作的差别就是in可以找到原型的属性,但是hasOwnProperty不会
console.log(obj.hasOwnProperty('name')) //true
console.log(obj.hasOwnProperty('age')) //false
console.log('age' in obj) //true

//另外 因为对象有方法这个概念,所以这个hasOwnProperty不受保护的
const obj = {
name: 'iceCode',
hasOwnProperty() {
return false
},
}
//这个时候不论如何访问这个方法,返回的都是false
console.log(obj.hasOwnProperty('name')) //false
console.log(obj.hasOwnProperty('age')) //false

Object.hasOwn 检查对象本身是否存在这个属性

用法同上,替代版,但有兼容性要求

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn

如果指定的对象自身有指定的属性,则静态方法 Object.hasOwn() 返回 true。如果属性是继承的或者不存在,该方法返回 false。

Object.getOwnPropertyNames 返回一个包含自身属性(包括不可枚举)的数组

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames

注:本身不包含对象的 Symbol 属性,只包含字符串属性。

Object.getOwnPropertyNames 接收一个目标对象,返回一个数组,包含自身不可枚举的属性,但是不包括 Symbol 属性

Object.getOwnPropertySymbols 返回一个包含自身 Symbol 属性的数组

Object.getOwnPropertySymbols 接收一个目标对象,返回一个数组,只包括目标对象的 Symbol 属性

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols

Object.getPrototypeOf 获取对象的原型

Object.getPrototypeOf 获取目标对象的原型,接收一个参数,返回该目标对象上的原型对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/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) //true

Object.setPrototypeOf 修改对象的原型

Object.setPrototypeOf 修改目标对象的原型,接收两个参数,第一个为目标对象,第二个为要设置的原型对象。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf

Object.prototype.isPrototypeOf 判断一个对象是否存在于另一个对象原型上

Object.prototype.isPrototypeOf 作为 Object 的原型方法,一般用在构造函数上。正常对象也可以使用,同样该方法也是不被保护的。接收一个目标函数,返回一个布尔值。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf

Object.getOwnPropertyDescriptor 获取自身属性值的描述对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor

1
2
3
4
5
6
7
8
9
10
11
const object1 = {
property1: 42,
}

const descriptor1 = Object.getOwnPropertyDescriptor(object1, 'property1')

console.log(descriptor1.configurable)
// Expected output: true

console.log(descriptor1.value)
// Expected output: 42

Object.getOwnPropertyDescriptor() 静态方法返回一个对象,该对象描述给定对象上特定属性(即直接存在于对象上而不在对象的原型链中的属性)的配置。返回的对象是可变的,但对其进行更改不会影响原始属性的配置。

Object.getOwnPropertyDescriptors 获取自身所有属性值的描述对象

同上,不过传的是整个对象

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors

Object.prototype.propertyIsEnumerable 表明指定属性是否可枚举

Object.prototype.propertyIsEnumerable 原型方法,表明指定属性是否可枚举,接收一个指定属性的参数,返回要给布尔值

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable

Object.keys 返回自身可枚举属性的数组

Object.keys 接收一个目标对象,返回一个包含自身可枚举属性的数组。就是如果属性被配置为不可枚举或者是 Symbol 属性都无法找到。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

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)
//这里只能找到name属性
console.log(keys) //['name']

//如果传入的目标对象是一个数组,那么会返回它们的索引
const arr = [1, 2, 3, 4, 5]
const values = Object.keys(arr)
console.log(values) //['0', '1', '2', '3', '4']

Object.values 返回自身可枚举属性值的数组

Object.values 接收一个目标对象,返回一个包含自身可枚举属性值的数组。就是如果属性被配置为不可枚举或者是 Symbol 属性都无法获取到这个属性值。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/values

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) //['iceCode']

//如果目标对象是一个数组,那么会返回一个浅拷贝的新数组
const arr = [{ f: '666' }, 2, 3, 4, 5]
const values = Object.values(arr)
values[0].f = '777'
values[1] = 666
console.log(values, arr) //[{ f: "777" }, 666, 3, 4, 5] [{ f: "777" }, 2, 3, 4, 5]

Object.entries 返回自身可枚举属性和属性值的数组

Object.entries 接收一个目标对象,返回一个包含自身可枚举属性和属性值的二维数组。就是如果属性被配置为不可枚举或者是 Symbol 属性都无法获取到这个属性值。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/entries

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) //[['name','iceCode']]

//如果目标对象是一个数组,那么会返回包含索引和value的二维数组
const arr = [1, 2, 3, 4, 5]
const entr = Object.entries(arr)
console.log(entr) //[['0',1],['1',2],['2',3],['3',4],['4',5]]

Object.is 对比两个值是否相同

Object.is 会对比两个值是否相同,对比比较操作符更准确,接收两个参数,第一个对比的参数,第二个对比的参数。返回一个布尔值,为对比结果。

Object.prototype.valueOf

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf

Object.prototype.toString

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/toString

Date.prototype.toLocaleString

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString

这三个方法都是 Object 原型上的方法,本身在 Object 对象上基本没有任何意义。valueOf 方法旨在被派生对象重写,以实现自定义类型转换逻辑。toSting 方法旨在重写(自定义)派生类对象的类型转换的逻辑。toLocaleString 方法旨在由派生对象重写,以达到其特定于语言环境的目的。
因为 js 中所有类型(不包括 null),都继承 Object 的原型,所有类型都有这三个派生方法,但它们的这三种方法都已经被重写,有着自身不同的效果。这里就不写了,只要知道在 Object 上这三个方法基本没有特殊效果就可以了。

Object.groupBy ES2024 新增

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy

给定可迭代对象中的元素进行分组

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 },
]
//这个时候有一个需求,将性别为要求,性别相同的分到一组,并且以对象的格式显示他们
//正常情况下我们会创建一个对象,然后遍历数组,在使用判断条件进行分组
//但是使用Object.groupBy只需要一行代码
//第二个回调需要返回要分组的属性
const newObj = Object.groupBy(arr, v => v.sex)
console.log(newObj) //这里打印一下看看
//{
// 女: [
// { name: "iq", sex: "女", age: 12 },
// { name: "ie", sex: "女", age: 12 },
// { name: "it", sex: "女", age: 12 },
// ],
// 男: [
// { name: "i", sex: "男", age: 12 },
// { name: "ie", sex: "男", age: 12 },
// { name: "iw", sex: "男", age: 12 },
// { name: "ir", sex: "男", age: 12 },
// ],
//}

最后

js 中对象的方法虽然有众多,但是业务项目中几乎很少能够用到,如果是比较,感觉还是对比运算符会比 Object.is 更方便一些。如果是对对象进行冻结、密封、禁止扩展的场景更是少之又少,访问原型链在业务需求里也是比较少的。访问对象中是否存在这个属性使用 in 操作符更方便些,但也不乏会有极少数场景可以使用到 Object.hasOwn 等方法

感觉使用 Object.groupBy 对数据进行分组是比较好的方法,但是兼容性可谓是一言难尽。目前使用最多的就是 Object.keys 来对对象进行判断或者遍历