ECMAScript 的对象是一个无序的属性的集合,对象中的属性之间没有排列顺序。
在 ECMAScript 中,对象是一种基本的数据类型,即对象就是数据类型 Object ,数据类型 Object 就是对象。
由于对象本身就是一种数据类型,所以对象可以像其它数据类型一样,随时被创建、被修改。对象具有极大的动态性,并不是面向对象编程中被严格定义的对象实体。
严格来说,ECMAScript 的对象的行为不一定合适 JavaScript 中的其它对象,比如浏览器环境中的 BOM 对象和 DOM 对象,是由宿主环境定义和提供的宿主对象。而宿主对象不受 ECMAScript 约束,所以宿主对象不一定会继承 Object 。
属性
属性是对象存储的数据,是对象的组成部分。
属性 是一个键值对。
键为属性名,值为属性值。
属性都由一个名称作为键来标识,这个名称被称作 属性名 ,属性名会映射到一个值,这个值被称作 属性值 。
方法
方法 是属性值为函数对象的属性。
当属性的值为函数对象时,该属性被称为方法。
示例:
- 在对象字面量中定义方法。
const obj = { func: function() { return '2035' } // 属性值为函数 } console.log(obj.func()) // 输出: // 2035
当属性值是一个匿名函数可以简写方法,即省略冒号操作符 .
和关键字 function 。
示例:
- 对象字面量的方法简写。
const obj = { func() { return '2049' } // 方法简写 } console.log(obj.func()) // 输出: // 2049
属性的类型
属性有两种类型:数据属性和访问器属性。
数据属性
数据属性有 4 个内部特性,用于描述属性的行为:
-
[[Configurable]] ,表示属性是否是可配置的,默认值为 true 。
属性是可配置的,指的是可以对属性进行以下的行为:
- 可以重新定义属性。
- 可以通过关键字 delete 删除属性。
- 可以修改属性的内部特性。
- 可以把属性修改为访问器属性。
-
[[Enumerable]] ,表示属性是否是可枚举的,默认值为 true 。
属性是可枚举的,指的是属性可以通过 for-in 循环返回。
-
[[Writable]] ,表示属性的值是否可以被修改,默认值为 true 。
-
[[Value]] ,表示属性的值,默认值为 undefined 。
访问器属性
访问器属性有 4 个内部特性,用于描述属性的行为:
-
[[Configurable]] ,表示属性是否是可配置的,默认值为 true 。
属性是可配置的,指的是可以对属性进行以下的行为:
- 可以重新定义属性。
- 可以通过关键字 delete 删除属性。
- 可以修改属性的内部特性。
- 可以把属性修改为数据属性。
-
[[Enumerable]] ,表示属性是否是可枚举的,默认值为 true 。
属性是可枚举的,指的是属性可以通过 for-in 循环返回。
-
[[Get]] ,表示属性的获取函数,默认值为 undefined 。
获取函数 ,在读取属性的值时被调用。
- 返回值:
任意值,向读取操作返回的值。
- 返回值:
-
[[Set]] ,表示属性的设置函数,默认值为 undefined 。
设置函数 ,在写入属性的值时被调用。
- 提供一个参数:
newValue,任意值,写入时提供的值。
- 提供一个参数:
可以使用字面量语法来定义访问器属性。
在属性名前面使用关键字 get 、set ,且属性值为函数对象。
访问器属性通常用于访问其他属性,比如对象自身的数据属性。
通常在属性名的前面或后面使用下划线
_
,表示不希望被对象外部直接访问的属性。
示例:
- 使用字面量语法来定义访问器属性。
const obj = { _attr: '2021', get accessorAttr() { console.log('get accessor attribute') return this._attr }, // 定义属性 accessorAttr 的获取函数 set accessorAttr(newValue) { console.log('set accessor attribute') this._attr = newValue + '-set' } // 定义属性 accessorAttr 的设置函数 } console.log('obj.accessorAttr:', obj.accessorAttr) obj.accessorAttr = '2035' console.log('obj.accessorAttr:', obj.accessorAttr) // 输出: // get accessor attribute // obj.accessorAttr: 2021 // set accessor attribute // get accessor attribute // obj.accessorAttr: 2035-set
相关 API
定义属性
ECMAScript 提供一些 API 来动态定义对象的属性。
前置知识:
- 用于描述属性的、属性的内部特性,被称为 描述符 。
Object.defineProperty()
方法 Object.defineProperty() :
-
功能:
通过设置属性的内部属性,动态地为指定的对象定义一个新属性,或重新定义一个已有的属性。 -
接收三个参数:
-
对象
需要定义属性的对象。 -
字符串 | 符号
需要定义的属性的属性名。 -
对象
需要定义的属性的描述符对象。
-
-
返回值:
对象,定义属性后的原对象。
属性的描述符对象:
-
数据属性的描述符对象
const dataDescriptor = { configurable: true, enumerable: true, writable: true, value: 'value' }
-
访问器属性的描述符对象
const accessorDescriptor = { configurable: true, enumerable: true, get() {}, set(newValue) {} }
在使用方法 Object.defineProperty() 定义属性时,如果不设置属性的内部特性 [[Configurable]]、[[Enumerable]]、[[Writable]] ,默认将这些内部特性设置为 false 。
示例:
-
为对象定义一个数据属性。
const obj = {} Object.defineProperty( obj, 'attr', { configurable: true, enumerable: true, writable: true, value: '1948' } // 数据属性的描述符对象 ) // 为对象定义一个数据属性 console.log('obj.attr:', obj.attr) // 输出: // obj.attr: 1948
-
为对象定义一个访问器属性。
let obj = { _attr: '2091' } obj = Object.defineProperty( obj, 'attr', { configurable: true, enumerable: true, get() { return this._attr }, set(value) { this._attr = value + '-set' } } // 访问器属性的描述符对象 ) // 为对象定义一个访问器属性 console.log('obj.attr:', obj.attr) obj.attr = '2108' console.log('obj.attr:', obj.attr) // 输出: // obj.attr: 2091 // obj.attr: 2108-get
Object.defineProperties()
方法 Object.defineProperties() :
-
功能:
通过设置属性的内部属性,动态地为指定的对象(重新)定义多个属性。 -
接收两个参数:
-
对象
需要定义属性的对象。 -
对象
属性定义对象。
-
-
返回值:
对象,定义属性后的原对象。
属性定义对象的属性的结构:
-
属性名:
需要定义的属性的属性名 -
属性值:
需要定义的属性的描述符对象。
属性定义对象:
const attributeDefining = {
dataAttr: {
configurable: true,
enumerable: true,
writable: true,
value: 'dataAttr'
},
accessorAttr: {
configurable: true,
enumerable: true,
get() {},
set(newValue) {}
}
}
在使用方法 Object.defineProperties() 定义属性时,如果不设置属性的内部特性 [[Configurable]]、[[Enumerable]]、[[Writable]] ,默认将这些内部特性设置为 false 。
示例:
- 为对象定义多个属性。
let obj = { _innerAttr: '2035' } Object.defineProperties( obj, { attr_01: { configurable: true, enumerable: true, writable: true, value: '2017' }, // 定义数据属性 attr_02: { configurable: true, enumerable: true, writable: true, value: '2021' }, outerAttr: { configurable: true, enumerable: true, get() { return this._innerAttr }, set(value) { this._innerAttr = value + '-set' } }, } // 属性定义对象 ) // 定义多个属性 console.log('obj.attr_01:', obj.attr_01) console.log('obj.attr_02:', obj.attr_02) console.log('obj.outerAttr:', obj.outerAttr) // 输出: // obj.attr_01: 2017 // obj.attr_02: 2021 // obj.outerAttr: 2035
获取属性的内部特性
ECMAScript 提供一些 API 来获取属性的内部特性。
Object.getOwnPropertyDescriptor()
方法 Object.getOwnPropertyDescriptor() :
-
功能:
获取指定对象的某个属性的内部特性。 -
接收两个参数:
-
对象
目标对象。 -
字符串 | 符号
属性名。
-
-
返回值:
对象,属性的描述符对象。
示例:
- 获取指定对象的某个属性的内部特性。
const obj = { attr: '1948' } const descriptor = Object.getOwnPropertyDescriptor(obj, 'attr') console.log(descriptor) // 输出: // {value: '1948', writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptors()
方法 Object.getOwnPropertyDescriptors() :
-
功能:
获取指定对象所有属性的内部特性。 -
接收一个参数:
对象,目标对象。 -
返回值:
对象,包含目标对象所有的属性的描述符对象的对象。
示例:
- 获取指定对象的某个属性的内部特性。
const descriptors = { attr_01: '2017', _innerAttr: '2021', get accessorAttr() { return this._innerAttr }, set accessorAttr(value) { this._innerAttr = value } } const obj = Object.getOwnPropertyDescriptors(descriptors) console.log(obj) console.log(obj.attr_01) console.log(obj._innerAttr) console.log(obj.accessorAttr) // 输出: // {attr_01: {…}, _innerAttr: {…}, accessorAttr: {…}} // {value: '2017', writable: true, enumerable: true, configurable: true} // {value: '2021', writable: true, enumerable: true, configurable: true} // {enumerable: true, configurable: true, get: ƒ, set: ƒ}
获取属性名
ECMAScript 提供一些 API 来获取属性的属性名。
Object.keys()
方法 Object.keys() :
-
功能:
获取指定对象的所有可枚举的、属性名为字符串的属性的键(属性名)。 -
接收一个参数:
对象,目标对象。 -
返回值:
数组,包含目标对象的所有可枚举的、属性名为字符串的属性的键(属性名)的字符串数组。
示例:
- 使用方法 Object.keys() ,获取指定对象的所有可枚举的、属性名为字符串的属性的键(属性名)。
const obj = { attrEnumerable_01: '1948', attrEnumerable_02: '2017' } Object.defineProperty( obj, 'attrNotEnumerable', { configurable: true, enumerable: false, writable: true, value: '2019' } ) const keys = Object.keys(obj) // 获取指定对象的所有可枚举的、属性名为字符串的属性的键(属性名) console.log('obj.attrEnumerable_01:', obj.attrEnumerable_01) console.log('obj.attrEnumerable_02:', obj.attrEnumerable_02) console.log('obj.attrNotEnumerable:', obj.attrNotEnumerable) console.log('# --- --- ---') console.log('keys:', keys) console.log('keys[0]:', keys[0]) console.log('keys[1]:', keys[1]) // 输出: // obj.attrEnumerable_01: 1948 // obj.attrEnumerable_02: 2017 // obj.attrNotEnumerable: 2019 // # --- --- --- // keys: (2) ['attrEnumerable_01', 'attrEnumerable_02'] // keys[0]: attrEnumerable_01 // keys[1]: attrEnumerable_02
Object.getOwnPropertyNames()
方法 Object.getOwnPropertyNames() :
-
功能:
获取指定对象的所有属性名为字符串的属性的键(属性名)。 -
接收一个参数:
对象,目标对象。 -
返回值:
数组,包含目标对象的所有属性名为字符串的属性的键(属性名)的字符串数组。
示例:
- 使用方法 Object.getOwnPropertyNames() ,获取指定对象的所有属性名为字符串的属性的键(属性名)。
const obj = { attrEnumerable_01: '2021', attrEnumerable_02: '2034' } Object.defineProperty( obj, 'attrNotEnumerable', { configurable: true, enumerable: false, writable: true, value: '2035' } ) const names = Object.getOwnPropertyNames(obj) // 获取指定对象的所有属性名为字符串的属性的键(属性名) console.log('obj.attrEnumerable_01:', obj.attrEnumerable_01) console.log('obj.attrEnumerable_02:', obj.attrEnumerable_02) console.log('obj.attrNotEnumerable:', obj.attrNotEnumerable) console.log('# --- --- ---') console.log('names:', names) console.log('names[0]:', names[0]) console.log('names[1]:', names[1]) console.log('names[2]:', names[2]) // 输出: // obj.attrEnumerable_01: 2021 // obj.attrEnumerable_02: 2034 // obj.attrNotEnumerable: 2035 // # --- --- --- // names: (3) ['attrEnumerable_01', 'attrEnumerable_02', 'attrNotEnumerable'] // names[0]: attrEnumerable_01 // names[1]: attrEnumerable_02 // names[2]: attrNotEnumerable
Object.getOwnPropertySymbols()
方法 Object.getOwnPropertySymbols() :
-
功能:
获取指定对象的所有属性名为符号的属性的键(属性名)。 -
接收一个参数:
对象,目标对象。 -
返回值:
数组,包含目标对象的所有属性名为符号的属性的键(属性名)的符号数组。
示例:
- 使用方法 Object.getOwnPropertySymbols() ,获取指定对象的所有属性名为符号的属性的键(属性名)。
const symbolEnumerable = Symbol('symbolEnumerable'), symbolNotEnumerable = Symbol('symbolNotEnumerable'), obj = { attrEnumerable: '2036', [symbolEnumerable]: '2045' } Object.defineProperty( obj, symbolNotEnumerable, { configurable: true, enumerable: false, writable: true, value: '2049' } ) const symbols = Object.getOwnPropertySymbols(obj) // 获取指定对象的所有属性名为符号的属性的键(属性名) console.log('obj.attrEnumerable:', obj.attrEnumerable) console.log('obj[symbolEnumerable]:', obj[symbolEnumerable]) console.log('obj[symbolNotEnumerable]:', obj[symbolNotEnumerable]) console.log('# --- --- ---') console.log('symbols:', symbols) console.log('symbols[0]:', symbols[0]) console.log('symbols[1]:', symbols[1]) // 输出: // obj.attrEnumerable_01: 2021 // obj.attrEnumerable_02: 2034 // obj.attrNotEnumerable: 2035 // # --- --- --- // symbols: (2) [Symbol(symbolEnumerable), Symbol(symbolNotEnumerable)] // symbols[0]: Symbol(symbolEnumerable) // symbols[1]: Symbol(symbolNotEnumerable)
主要参考资料:
- 《JavaScript 高级程序设计(第4版)》- P232(257/931)
合并对象
JavaScript 开发者发现合并两个对象很有用,即把源对象所有的属性复制到目标对象中。
合并(merge)对象有时也被称为混入(mixin)对象。
ECMAScript 提供方法 Object.assign() 来合并对象(对象间的赋值)。
Object.assign()
方法 Object.assign() :
-
功能:
把源对象所有的属性复制到目标对象中,执行的复制是浅复制。 -
接收任意个数的参数:
-
对象
目标对象。 -
更多参数:
对象,源对象。
-
-
返回值:
对象,合并后的目标对象。
如果在方法 Object.assign() 执行期间出错,就会中止并退出方法,并抛出错误。在出错前执行的合并对象的操作不会被撤销,即不会“回滚”。
示例:
- 合并对象。
let targetObj = { key: 'targetObj' } const sourceObj_01 = { attr_01: 'sourceObj_01 attr' } const sourceObj_02 = { attr_02: 'sourceObj_02 attr' } Object.assign(targetObj, sourceObj_01, sourceObj_02) // 合并对象 console.log('targetObj:', targetObj) // 输出: // targetObj: {key: 'targetObj', attr_01: 'sourceObj_01 attr', attr_02: 'sourceObj_02 attr'}
相等判定
在一些情况下使用操作符 ===
进行相等判定,可能得不到“预期”的结果。
示例:
console.log('+0 === -0 :', +0 === -0)
console.log('0 === -0 :', 0 === -0)
console.log('0 === +0 :', 0 === +0)
console.log('NaN === NaN :', NaN === NaN)
// 输出:
// +0 === -0 : true
// 0 === -0 : true
// 0 === +0 : true
// NaN === NaN : false
针对这些情况,ES6 新增方法 Object.is() 来提供与操作符 ===
相同的功能,即判定两个值是否相等,但在一些情况下能得到与操作符 ===
不同的、“预期”的结果。
Object.is()
方法 Object.is() :
-
功能:
判定两个值是否相等。 -
接收两个参数:
- 任意值,将要进行判定的值。
- 任意值,将要进行判定的值。
-
返回值:
布尔值,两个值是否相等的判定结果。
示例:
- 使用方法 Object.is() 进行相等判定。
console.log('is +0 equal to -0 ? :', Object.is(+0, -0)) // 进行相等判定 console.log('is 0 equal to -0 ? :', Object.is(0, -0)) console.log('is 0 equal to +0 ? :', Object.is(0, +0)) console.log('is NaN equal to NaN ? :', Object.is(NaN, NaN)) // 输出: // is +0 equal to -0 ? : false // is 0 equal to -0 ? : false // is 0 equal to +0 ? : true // is NaN equal to NaN ? : true