基本特点

  • 是一组由 <属性名-属性值> 组成的无序集合
  • 包含相关数据和方法的集合(通常由一些变量和函数组成,称之为对象里面的属性和方法)
  • 对象的 <属性名> 只能是字符串或者 symbol 类型(其他的类型会自动转换为字符串),对象的 <值> 可以是任意类型
  • 有特别的顺序:先对自然数属性升序排列,然后字符串属性按照创建时间排序,最后 symbol 属性按创建时间排序

分类

  • 用户自定义对象:程序员自己定义
  • 内建对象:内置在 JavaScript 语言中定义的对象
  • 宿主对象:浏览器等提供的对象

1. 创建

构造函数语法

let obj = new Object()

对象字面量语法

let user = {
  name: 'xx',
  age: 20,
  tags: ['HTML5', 'CSS3', 'JavaScript'],
};

2. 访问

点运算符

user.name:点运算要求 key 是有效的 变量标识符,注意这里的 name 是一个字符串

方括号运算符

user['name']:如果 key 不是有效的 变量标识符,可以用这种(数字可以不加引号,默认转为字符串)

方括号运算符还可以动态取值:

let obj = {
  a: 1
}
let name = 'a'
 
obj[name] // 1

Tip

对象默认有个名为 __proto__ 的属性,不能将它设置为一个非对象的值。要设置一个这样的属性,可以用 Map

3. 属性

自有属性

对象本身的属性,而不是继承来的属性。
判断方法:Object.hasOwn(obj, prop) 返回 true

可枚举属性

内部 可枚举 标志设置为 true 的属性
判断方法:obj.propertyIsEnumerable(prop) 返回 true


计算属性ES6

可以在对象属性定义中使用方括号动态创建属性,也可以动态访问属性,方括号里可以是任意表达式

let a = 'name'
 
// 创建
let user = {
  [a]: 'xx',
}
 
// 访问
user[a] // 'xx'

属性删除

delete 可以删除不是继承来的属性:delete user.name


属性简写ES6

对象属性名和值一样,直接写一个即可

let name = 'xx'
let age = 20
 
let user = {
  name,
  age,
}

函数属性

作为对象属性的函数

let user = {
  name: 'xx',
  getName: function() {
    return this.age
  }
}

方法ES6

省略 “function”

let user = {
  name: 'xx',
  getName() {
    return this.age
  }
}

方法 和 函数属性 略有区别:


判断属性是否在对象里

  • in:判断某个属性在不在对象里,包括 自有属性原型继承属性 ,也包括 可枚举属性不可枚举属性
  • hasOwnProperty(prop):判断 自有属性
  • Object.hasOwn( obj, prop ):判断 自有属性,但是为静态方法,没有原型的对象也能调用(一般用这个)

属性描述符

对象中的属性描述符分为两类:数据描述符访问器描述符,每个属性内部都有属性描述符来描述这个属性的特性

valuewritableconfigurableenumerablegetset
数据描述符
访问器描述符

一个属性要么是数据属性(有 value/writable 特性),要么是访问器属性(有 get/set 方法)

  • value:属性值
  • writable:true 表示能被赋值运算符改变,false 表示只读
  • configurable:false 表示属性不可删除,描述符不可修改(除了能将 writable 由 true false)
  • enumerable:true 表示可枚举(引入目的是让某些属性规避掉 for…in 操作,不然内部属性和方法都会遍历)
  • get:访问属性时,会调用此函数(但是不能在外部直接当作函数一样调用它,只能访问它)
  • set:属性值被修改时,会调用此函数,接受一个参数(也就是被赋予的新值)

使用 Object.defineProperty() 进行设置时,没有提供的描述符默认为 false


遍历

  • for...in:遍历所有 可枚举属性 属性,包括继承来的(非 symbol)
  • Object.keys( obj ):返回 自有属性 可枚举属性 的属性名组成的数组(非 symbol)
  • Object.values( obj ):返回 自有属性 可枚举属性 的属性值组成的数组(非 symbol)
  • Object.entries( obj ):返回 自有属性 可枚举属性 组成的键值对数组(非 symbol)
  • Object.getOwnPropertyNames( obj ):返回 自有属性 的所有(非 Symbol)的属性名数组
  • Object.getOwnPropertySymbols( obj ):返回 自有属性 的所有 Symbol 属性名的数组
  • Reflect.ownKeys( obj ):包含 自有属性 的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举

以上遍历方法都遵循下面规则:

  1. 首先遍历所有数值键,按照数值升序排列
  2. 其次遍历所有字符串键,按照加入时间升序排列
  3. 最后遍历所有 Symbol 键,按照加入时间升序排列

对象复制

浅拷贝

深拷贝

  • JSON.parse( JSON.stringify(obj) ):对于不可序列化的属性会失效
  • _.cloneDeep(obj):lodash 库函数
  • structuredClone()

全局对象

可在任何地方使用的变量和函数

  • 浏览器 中:window
  • Node.js 中:global
  • 所有环境通用:globalThisES2020

SymbolES6

带有可选描述的“原始唯一值”,目前 symbol 类型值的唯一目的就是作为对象不重复的属性名。

  • 创建:let id = Symbol()
  • 添加描述(name):let id = Symbol(id)
  • 对象字面量中的 symbol:需要用方括号括起来:let obj = { [id]: 123 }
  • 全局 symbol:用于相同的描述取到相同的 symbol,如:let x = Symbol.for('id')let y = Symbol.for('id'),则 x===y

特性:

  • 不参与 for...in 循环
  • Object.keys()Object.values()Object.entries() 也会忽略它们
  • Object.assign() 会同时复制字符串和 symbol 属性
  • 展开语法 { ...obj } 会同时复制字符串和 symbol 属性

常用方法:

创建

  • Object.assign( target, ...sources ):将所有 可枚举自有 属性(包括 symbol),从一个或多个源对象复制到目标对象,返回修改后的对象,如果被拷贝的属性名已经存在,那么它会被覆盖。

只能拷贝源对象的 可枚举自有 属性,同时无法拷贝属性的描述符,而且访问器属性会被转换成数据属性,也无法拷贝源对象的原型

  • 要拷贝属性及原型:
let cloneObj = Object.assign(
  Object.create( Object.getPrototypeOf(obj) ),
  obj
)
  • 要拷贝属性及描述符:
let cloneObj = Object.defineProperties(
  {},
  Object.getOwnPropertyDescriptors(obj)
)

这种可以完整拷贝:

let cloneObj = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
)
  • Object.create( prototype [, propertiesObject] ):返回根据指定的原型对象和属性描述创建的新对象

  • Object.fromEntries( iterable ):从生成键值对的可迭代对象中创建新对象

判断

  • hasOwnProperty( prop ):判断一个对象的属性是否是 自有 属性
  • Object.hasOwn ( instance, prop ):同上,但是为静态方法,没有原型的对象也能调用判断ES2020
  • Object.is ( value1, value2 ):判断两个值是否为同一个值ES6
  • Object.isExtensible( obj ):判断对象是否可扩展(可以在它上面添加新的属性)
  • Object.isSealed( obj ):判断是否 自有 属性 都是 不可扩展,不可配置
  • Object.isFrozen( obj ):判断是否 自有 属性 都是 不可扩展,不可配置,且所有数据属性都是不可写的
  • propertyIsEnumerable( prop ):判断指定的属性名是否是 可枚举自有 属性
  • isPrototypeOf( obj ):判断一个对象是否存在于 obj 的原型链上

获取

  • Object.getOwnPropertyDescriptor( obj, prop ):返回 obj 对象中 自有 属性 prop 对应的属性描述符
  • Object.getOwnPropertyDescriptors( obj ):返回所有 自有 属性的描述符
  • Object.getOwnPropertyNames( obj ):返回 自有 的所有(非 Symbol)的属性名数组
  • Object.getOwnPropertySymbols( obj ):返回 自有 的所有 Symbol 属性名的数组
  • Object.getPrototypeOf( obj ):返回指定对象的原型
  • Object.keys( obj ):返回 可枚举自有 属性名组成的数组
  • Object.values( obj ):返回 可枚举自有 属性值组成的数组
  • Object.entries( obj ):返回 可枚举自有 属性组成的键值对数组

设置

  • Object.defineProperty( obj, prop, descriptor ):在对象上定义新属性,或修改现有属性
  • Object.defineProperties( obj, props ):同上,批量
  • Object.preventExtensions( obj ):禁止 添加 属性
  • Object.seal( obj ):禁止 添加/删除 属性

为所有自身现有的属性设置 configurable: false

  • Object.freeze( obj ):禁止 添加/删除/更改 属性

为所有自身现有的属性设置 configurable: false, writable: false

  • Object.setPrototypeOf( obj, prototype ):重新设置 obj 对象的原型

深入理解JavaScript——Object.create - 知乎 (zhihu.com)
javascript - Difference between freeze and seal - Stack Overflow