JavaScript花样百出的属性定义
前言
Object.defineProperty
在JavaScript
中最简单直接的给对象定义属性的方式为对象.属性名=属性值
,但有时候需要对对象的属性做一些更深层次的定义,那么可以通过Object.defineProperty
的方式给对象定义属性
简单示例如下
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'Bruse'
})
console.log(obj)
console.log(Object.getOwnPropertyDescriptor(obj, 'name'))
输出
{name: 'Bruse'}
{value: 'Bruse', writable: false, enumerable: false, configurable: false}
属性描述符
在定义属性的时候,可以通过配置不同的属性描述符
来定义属性是否可以被删除、被修改等。[和Java中的属性修饰符private、public、statis、final等有点像,都控制了外部访问以及修改对象某个属性的权限,但Java中并不能很灵活地删除属性]
configurable 是否可配置
配置configurable
描述符,可以决定该对象的该属性是否可以被删除,被修改。
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'Bruse',
configurable: false // 不可配置
})
delete obj.name
console.log(obj)
当属性不可配置时,尝试进行删除该属性,运行会抛出异常
Uncaught TypeError: Cannot delete property 'name' of #<Object>
同理,修改属性值也是一样
obj.name = '123'
运行抛出异常
Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
但只要在定义属性的时候定义为configurable: false
,以上操作则都不会产生问题
configurable
和writable
有一种情况是configurable
为false
,但是writable
为true
的话,那么还是可以修改属性的值
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'bruse',
configurable: false,
writable: true
})
obj.name = 'bat'
console.log(obj)
输出{name: 'bat'}
如果再把writable
改为false
,那么在对属性赋值时将会抛出异常
Object.defineProperty(obj, 'name', {
writable: false
})
obj.name = 'wa'
console.log(obj)
抛出异常
Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
Tips:若
configurable
为false
,那么writable
可以由true
变为false
,但无法从false
改为true
configurable: false 不可逆
当属性一开始就定义了configurable: false
时,无法再通过Object.defineProperty
重新修改
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'Bruse',
configurable: false
})
Object.defineProperty(obj, 'name', {
configurable: true
})
console.log(Object.getOwnPropertyDescriptor(obj, 'name'))
运行会抛出异常,也就是说无法从configurable: false
转为configurable: true
Uncaught TypeError: Cannot redefine property: name
at Function.defineProperty
configurable: true可逆
与之相反的是,若一开始配置属性configurable: true
,反而倒是可以将其修改为configurable: false
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'Bruse',
configurable: true
})
Object.defineProperty(obj, 'name', {
configurable: false
})
console.log(Object.getOwnPropertyDescriptor(obj, 'name'))
运行正常,最后输出
{value: 'Bruse', writable: false, enumerable: false, configurable: false}
enumerable 是否可枚举
当enumerable
为true
时,对象的该属性可以被for...in...
或Object.keys
获取
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'Bruse',
configurable: true,
enumerable: true
})
console.log(Object.keys(obj))
输出['name']
当enumerable
为false
时,对象的该属性则不可被for...in...
或Object.keys
获取
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'Bruse',
configurable: true,
enumerable: false
})
console.log(Object.keys(obj))
输出[]
writable 是否可更改
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'Bruse',
writable: true
})
obj.name = 'bat'
console.log(obj)
输出{name: 'bat'}
若将writable
修改为false
,则无法再修改name
的值
Object.defineProperty(obj, 'name', {
writable: false
})
obj.name = 'wa'
console.log(obj)
抛出异常,obj.name
变成了只读属性,只能读取无法修改
Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
value 属性值
value
其实就是定义属性时,该属性的默认值。即便属性没有默认值,只要writable
为true
,那么也可以在定义了属性之后为该属性赋值
const obj = {}
Object.defineProperty(obj, 'name', {
writable: true
})
obj.name = 'bat'
console.log(obj)
输出{name: 'bat'}
Tips:若在Object.defineProperty()定义属性时,没有设置value的值,那么默认为undefined
get访问器函数
get
函数其实跟Java
的get/set
方法差不多,get
函数可以让我们在获取对象属性值时,做一些额外的操作
let name = 'Bruse'
const obj = {}
Object.defineProperty(obj, 'name', {
configurable: true,
enumerable: true,
get() {
return name
}
})
console.log(obj.name) // 输出Bruse
我们也可以做一些额外操作装饰一下返回值
get() {
return `My name is ${name}` // 输出My name is Bruse
}
set访问器函数
set
函数可以让我们在设置对象属性值时,做一些额外的操作
let name = 'Bruse'
const obj = {}
Object.defineProperty(obj, 'name', {
configurable: true,
enumerable: true,
get() {
return `My name is ${name}`
},
set(v) {
if (v === undefined || v === null) { // 做一下空判断,设置默认值
name = 'secret'
} else {
name = v
}
}
})
obj.name = undefined
console.log(obj.name) // 输出My name is secret
obj.name = 'Bruse'
console.log(obj.name) // 输出My name is Bruse
Tips: 使用get/set访问函数的话,就不能再配置value或writable了,两者互斥
Object.preventExtensions
Object.preventExtensions()
可以让对象变得无法扩展,即无法再新增新的属性。
const obj = {}
obj.age = 16 // 新增age属性
console.log(obj)
对象默认是可扩展的,所以当定义了一个对象后,还可以继续给这个对象动态新增一些属性,上边代码执行后输出{age: 16}
也可以通过Object.isExtensible()
来查看对象是否可拓展
console.log(Object.isExtensible(obj)) // 返回true,表示可拓展
接下来使用Object.preventExtensions()
使对象不可拓展
Object.preventExtensions(obj)
obj.phone = '121xxxxxx' // 新增属性
console.log(obj)
此时obj
对象变得不可拓展,并且执行代码时会抛出异常
Uncaught TypeError: Cannot add property phone, object is not extensible
虽然对象不可再拓展,但原有的属性还是可以做修改或删除
Object.preventExtensions(obj)
obj.age = 123
console.log(obj)
输出{age: 123}
Object.preventExtensions(obj)
delete obj.age
console.log(obj)
输出{}
Object.seal
Object.seal
可以看做是Object.preventExtensions
的升级,除了让对象不可再新增属性外,还把对象已有的属性标记为不可配置
const obj = {}
obj.age = 16
console.log(Object.getOwnPropertyDescriptor(obj, 'age'))
输出
{value: 16, writable: true, enumerable: true, configurable: true}
接着调用Object.seal
Object.seal(obj)
console.log(Object.getOwnPropertyDescriptor(obj, 'age'))
再次输出
{value: 16, writable: true, enumerable: true, configurable: false}
此时若尝试删除obj.age
属性的话,会产生异常
delete obj.age
执行错误
Uncaught TypeError: Cannot delete property 'age' of #<Object>
尝试添加属性也会产生异常
obj.phone = '12133'
抛出异常
Uncaught TypeError: Cannot add property phone, object is not extensible
可以通过调用Object.isSealed()
判断对象是否被封闭
console.log(Object.isSealed(obj)) // 输出true
Object.freeze
Object.freeze()
则比Object.preventExtensions
和Object.seal
还要更近一步,它会让对象不能添加新属性,已有属性不可配置,且不能修改值
const obj = {}
obj.age = 16
console.log(Object.getOwnPropertyDescriptor(obj, 'age'))
输出
{value: 16, writable: true, enumerable: true, configurable: true}
使用Object.freeze
Object.freeze(obj)
console.log(Object.getOwnPropertyDescriptor(obj, 'age'))
输出
{value: 16, writable: false, enumerable: true, configurable: false}
此时尝试修改属性
obj.age = 18
抛出异常
Uncaught TypeError: Cannot assign to read only property 'age' of object '#<Object>'
尝试新增属性
obj.phone = '113444'
还是抛出异常
Uncaught TypeError: Cannot add property phone, object is not extensible
甚至无法再修改属性的定义了
Object.defineProperty(obj, 'age', {
enumerable: false
})
抛出异常
Uncaught TypeError: Cannot redefine property: age
at Function.defineProperty (<anonymous>)
Tips: 可以通过Object.isFrozen()判断对象是否被冻结
转载自:https://juejin.cn/post/7282735710851973157