前言
在 JavaScript 中,几乎所有的对象都是 Object
类型的实例,它们都会从 Object.prototype
继承属性和方法,虽然大部分属性都会被覆盖(shadowed)或者说被重写了(overridden)。 除此之外,Object
还可以被故意的创建,但是这个对象并不是一个“真正的对象”(例如:通过 Object.create(null)
),或者通过一些手段改变对象,使其不再是一个“真正的对象”(比如说:Object.setPrototypeOf
)。
静态方法
Object.create
创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)。
let obj = Object.create(null)
let o1 = {
name: 'abc',
say() {
console.log(this.name)
}
}
let o2 = Object.create(o1)
o2.name = 'ABC'
o2.say() // ABC
Object.getPrototypeOf()
返回指定对象的原型(内部[[Prototype]]
属性的值)。
function Foo() {}
let foo = new Foo()
Object.getPrototypeOf(foo) === Foo.prototype // true
Object.setPrototypeOf()
设置一个指定的对象的原型(即,内部 [[Prototype]]
属性)到另一个对象或null
const obj = {}
Object.setPrototypeOf(obj, null)
Object.getOwnPropertyDescriptors()
获取一个对象的所有自身属性的描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
const obj = {
age: 18,
name: 'A'
}
console.log(Object.getOwnPropertyDescriptors(obj))
/**
{
age: { value: 18, writable: true, enumerable: true, configurable: true },
name: { value: 'A', writable: true, enumerable: true, configurable: true }
}
**/
Object.getOwnPropertyDescriptor
获取对象上一个自有属性对应的属性描述符。
const obj = {
age: 18,
name: 'A'
}
console.log(Object.getOwnPropertyDescriptor(obj, 'age'))
// { value: 18, writable: true, enumerable: true, configurable: true }
Object.getOwnPropertyNames()
获取指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。
const obj = {
age: 18,
name: 'A',
[Symbol('address')]: 'GZ'
}
console.log(Object.getOwnPropertyNames(obj)) // [ 'age', 'name' ]
Object.getOwnPropertySymbols()
获取指定对象自身的所有 Symbol 属性的数组。
const obj = {
age: 18,
name: 'A',
[Symbol('address')]: 'GZ'
}
console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol(address) ]
Object.hasOwn()
如果指定的对象自身有指定的属性,返回 true
。如果属性是继承的或者不存在,返回 false
。
Object.assign()
将所有可枚举的自有属性从一个或多个源对象复制到目标对象,返回修改后的对象。
如果目标对象与源对象具有相同的 key,则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的属性。
Object.fromEntries()
将键值对列表转换为一个对象
const entries = new Map([
['name', 'abc'],
['age', 20]
])
const obj = Object.fromEntries(entries)
console.log(obj) // { name: 'abc', age: 20 }
Object.is()
Object.is()
方法判断两个值是否为同一个值,如果满足以下任意条件则两个值相等:
- 都是
undefined
- 都是
null
- 都是
true
或都是false
- 都是相同长度、相同字符、按相同顺序排列的字符串
- 都是相同对象(意味着都是同一个对象的值引用)
- 都是数字且
- 都是
+0
- 都是
-0
- 都是
NaN
- 都是同一个值,非零且都不是
NaN
- 都是
Object.is()
与 ==
不同。==
运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将 "" == false
判断为 true
),而 Object.is
不会强制转换两边的值。
Object.is()
与 ===
也不相同。差别是它们对待有符号的零和 NaN 不同,例如,===
运算符(也包括 ==
运算符)将数字 -0
和 +0
视为相等,而将 Number.NaN
与 NaN
视为不相等。
Object.freeze()
冻结一个对象。
不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值,对象的原型也不能被修改。
这个方法返回原对象,而不是创建一个被冻结的副本。
如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它也是个冻结对象。数组作为一种对象,被冻结,其元素不能被修改。没有数组元素可以被添加或移除。
const obj = {
age: 18,
name: 'A',
address: ['GX', 'DS']
}
console.log(Object.freeze(obj) === obj)
// obj.age = 20 // 报错
// delete obj.age // 报错
obj.address.push('SS') // 可以添加
obj.address.splice(0, 1) // 可以删除
// obj.address = [] // 报错
// 冻结一个数组
const arr = [1, 2, 3]
Object.freeze(arr)
arr.push(4) // 不能添加,当然也不能做其他修改
Object.isFrozen()
判断一个对象是否被冻结
Object.seal()
密封一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
const obj = {
age: 18,
name: 'A'
}
Object.seal(obj)
delete obj.age // 不能删除属性,会报错
// obj.age = 20 // 添加属性,会报错
Object.isSealed()
判断一个对象是否被密封。
注意冻结对象也是一个密封对象。
Object.preventExtensions()
让一个对象变的不可扩展,也就是永远不能再添加新的属性。
const obj = {
age: 18,
name: 'A'
}
Object.preventExtensions(obj)
delete obj.age // 可以删除属性
obj.age = 20 // 添加属性则会报错
Object.isExtensible()
判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。\n
注意冻结对象也是一个不可扩展的对象。一个不可扩展的空对象同时也是一个冻结对象。
const obj = {
age: 18,
name: 'A'
}
Object.seal(obj)
console.log(Object.isSealed(obj)) // true
console.log(Object.isExtensible(obj)) // false密封属性同时也是不可以拓展的
const obj1 = {}
Object.preventExtensions(obj1)
console.log(Object.isFrozen(obj1)) // true 一个不可扩展的空对象同时也是一个冻结对象
Object.defineProperty()
语法:
Object.defineProperty(obj, prop, descriptor)
-
obj
:要定义属性的对象。 -
prop
:要定义或修改的属性的名称或 [Symbol
] -
descriptor
:要定义或修改的属性描述符:-
value
:该属性对应的值。可以是任何有效的JavaScript值(数值,对象,函数等)。默认undefined
。 -
writable
:为true
时,属性的值,也就是value
,才能被赋值运算符改变。 默认false
。 -
configurable
:为true
时,该属性的描述符才能够被改变和删除。 默认false
。 -
enumerable
:为true
时,该属性才会出现在对象的枚举属性中。默认false
。 -
get
:属性的 getter 函数,如果没有 getter,则为undefined
。当访问该属性时,会调用此函数。
执行时不传入任何参数,但是会传入this
对象(由于继承关系,这里的this
并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。默认undefined
。 -
set
:属性的 setter 函数,如果没有 setter,则为undefined
。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的this
对象。 默认为undefined
。
-
function defineReactive(obj, key, val) {
// 获取当前key的属性描述符
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
return Object.defineProperty(obj, key, {
get() {
const value = getter ? getter.call(obj) : val
return value
},
set(newVal) {
val = newVal
}
})
}
let obj = { a: 10 }
defineReactive(obj, 'a')
console.log(obj.a)
Object.defineProperties()
在一个对象上定义新的属性或修改现有属性,并返回该对象。
let obj = {}
Object.defineProperties(obj, {
'property1': {
value: true,
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
})
Object.entries()
返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in
循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。
const obj = {
name: 'abc',
age: 18,
[{}]: 'aa',
[Symbol['address']]: 'China'
}
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`)
}
/**
name: abc
age: 18
[object Object]: aa
undefined: China
*/
Object.keys()
返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序与使用for...in
循环的顺序相同。
const obj = {
name: 'abc',
age: 18,
[{}]: 'aa',
[Symbol['address']]: 'China'
}
for (const key of Object.keys(obj)) {
console.log(key)
}
/**
name
age
[object Object]
undefined
*/
Object.values()
返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for...in
循环的顺序相同。
const obj = {
name: 'abc',
age: 18,
[{}]: 'aa',
[Symbol['address']]: 'China'
}
for (const value of Object.values(obj)) {
console.log(value)
}
/**
abc
18
aa
China
*/
实例方法
Object.prototype.hasOwnProperty()
返回一个布尔值,表示对象自身属性中是否具有指定的属性。
const parent = {
key: 'aaa'
}
const obj = Object.create(parent)
obj.age = 18
console.log(obj.age) // 18
console.log(obj.hasOwnProperty('age')) // true
console.log(obj.key) // aaa
console.log(obj.hasOwnProperty('key')) // false
// 也可以使用 Object 原型上的 hasOwnProperty 属性
console.log(Object.prototype.hasOwnProperty.call(obj, 'age')) // true
Object.prototype.isPrototypeOf
返回一个布尔值,表示一个对象是否存在于另一个对象的原型链上。
function Parent() {}
function Child() {}
Child.prototype = Object.create(Parent.prototype)
const child = new Child()
console.log(Parent.prototype.isPrototypeOf(child)) // true
console.log(Child.prototype.isPrototypeOf(child)) // true
console.log(Object.prototype.isPrototypeOf(child)) // true
Object.prototype.toString()
返回一个表示该对象的字符串。
Object.prototype.toString()
返回 [object Type]
,这里的 Type 是对象的类型。如果对象有 Symbol.toStringTag
属性,其值是一个字符串,则它的值将被用作 Type。许多内置的对象,包括 Map
和 Symbol
,都有 Symbol.toStringTag
。一些早于 ES6 的对象没有 Symbol.toStringTag,但仍然有一个特殊的标签。它们包括(标签与下面给出的类型名相同):
- Array
- Function(它的 typeof 返回 "function")
- Error
- Boolean
- Number
- String
- Date
- RegExp
console.log(Object.prototype.toString.call(1)) // [object Number]
console.log(Object.prototype.toString.call('')) // [object String]
console.log(Object.prototype.toString.call(true)) // [object Boolean]
console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call({})) // [object Object]
console.log(Object.prototype.toString.call([])) // [object Array]
console.log(Object.prototype.toString.call(function () {})) // [object Function]
console.log(Object.prototype.toString.call(new Date())) // [object Date]
console.log(Object.prototype.toString.call(new RegExp(/\d/g))) // [object RegExp]
console.log(Object.prototype.toString.call(new Error())) // [object Error]
console.log(Object.prototype.toString.call(Symbol())) // [object Symbol]
console.log(Object.prototype.toString.call(123n)) // [object BigInt]
// 自定义
let obj = {
[Symbol.toStringTag]: 'MyObj'
}
console.log(Object.prototype.toString.call(obj)) // [object MyObj]
Object.prototype.valueOf()
将 this
值转换为一个对象。此方法旨在用于自定义类型转换的逻辑时,重写派生类对象。
function MyNumberType(n) {
this.number = n
}
MyNumberType.prototype.valueOf = function () {
return this.number
}
const object1 = new MyNumberType(4)
console.log(object1 + 3) // 7