可可爱爱的属性描述符属性描述符想必大家都耳熟能详,近日一男子针对属性描述符做了不太详细的介绍,旨在记录通过属性描述符对属
本文旨在介绍属性描述符。同时温习两个小可爱freeze与seal。
问题描述:在项目中我们需要给对象中的属性进行各种限制例如不允许修改、删除等,最主要的是阻止用户的无效操作,那么这种情况我们通过什么玩意实现捏。
思考:想到了属性描述符对属性进行某种限制,用freeze、seal来管理某个对象。(只在原生js的情况下,vue框架有更好的方式像readonly)。
1 属性描述符
定义
也就是用来描述某个属性的一个对象,其中包含了各种描述限制,例如:writable
value
enumerable
configurable
get
set
等
可以通过 Object.getOwnPropertyDescriptor(obj,属性) 获取指定属性的属性描述符
const shopCar = {
goodsId: 18,
goodsName: '一条抠脚汉的丝袜',
goodsPrice: 89,
quantity: 1
}
console.log(Object.getOwnPropertyDescriptor(shopCar, 'goodsPrice'))
//{ value: 89, writable: true, enumerable: true, configurable: true}
有同学要问了这玩意是内置的么,是否可以修改这玩意,当然可以。
Object.defineProperty(obj,属性,{xxx}) 设置属性描述符
Object.defineProperty(shopCar, 'goodsPrice', {
value: 89,
writable: true,
enumerable: true,
configurable: false
})
但是当 configurable设置为false
,则指定属性的属性描述符将不能够被修改。
Object.defineProperty(shopCar, 'goodsPrice',{
value: 89,
writable: true,
configurable: false
})
OObject.defineProperty(shopCar, 'goodsPrice',{
value: 138,
writable: false,
configurable: false
})
// Cannot redefine property: goodsPrice
光上代码不解释简直就是流氓。
value
指定属性的值writable
指定属性的值是否可以被修改enumerable
指定属性是否可以被枚举configurable
指定属性是否可以被删除,并且属性的属性描述符是否可以被修改get
指定属性被读取时的回调getterset
指定属性被修改时的回调setter
好了,我们了解了其属性描述符的各个作用,我们就可以使用属性描述符来做各种限制,比如不允许删除、修改、监听属性(vue2响应式原理中就采用了get与set实现)。
2 vue2中响应式的原理
显然这里不是本文的核心问题重点,旨在通过vue2响应式的设计来表现get与set的描述场景。
我们先简单处理一下get与set。
let internalValue = 89
Object.defineProperty(shopCar, 'goodsPrice', {
enumerable: true,
configurable: true,
get() {
console.log('我去读了goodsPrice我的值是',internalValue)
return internalValue
},
set(val) {
console.log('我去修改goodsPrice我的值是',val)
internalValue = val
}
})
以上代码中能够看出,读取与修改属性都是可以被监听到的,所以在vue2中为了监听属性的改变对依赖属性的视图进行重新渲染,在setter中通知整个组件的观察者watcher工作,watcher在通知render重新渲染。这是get与set的应用场景,还可以通过二者实现对属性更多的操作。
3 限制属性 拿捏属性
- 限制1 限制属性修改或删除,限制对象添加属性
- 限制2 限制属性值的类型,类型不符抛出异常
- 限制3 限制原型上的属性增删改
- ......
开干,我玩的就是不真实。
限制1
像购物车商品的价格属性一般由系统指定并不允许操作的,也就是限制属性的修改与删除,很显然用到了writable与configurable。
class ShopCar {
constructor(car) {
this.goodsId = car.goodsId
this.goodsName = car.goodsName
this.goodsPrice = car.goodsPrice
this.quantity = car.quantity
Object.defineProperty(this, 'goodsPrice', {
writable: false,
enumerable: true,
configurable: false
})
}
}
let shopCar = {
goodsId: 18,
goodsName: '一条抠脚汉的丝袜',
goodsPrice: 89,
quantity: 1
}
const sc = new ShopCar(shopCar)
sc.goodsPrice = 138
delete sc.goodsPrice
console.log(sc)
//sc对象上的goodsPrice值依然是89,delete并未成功
或者利用setter抛出错误
class ShopCar {
constructor(car) {
...
Object.defineProperty(this, 'goodsPrice', {
enumerable: true,
configurable: false,
set(val) {
throw Error("商品价格不允许进行操作,谢谢配合")
}
})
}
}
let shopCar = {
goodsId: 18,
goodsName: '一条抠脚汉的丝袜',
goodsPrice: 89,
quantity: 1
}
const sc = new ShopCar(shopCar)
sc.goodsPrice = 138 // Error: 商品价格不允许进行操作,谢谢配合
或者freeze、seal
Object.freeze
对指定对象进行冻结,不允许添加、修改、删除任何属性Object.seal
对指定对象进行密封,不允许添加、删除但可以修改原有的可写的属性的值
Object.freeze能够有效防止各种流氓操作。
class ShopCar {
constructor(car) {
...
Object.freeze(this)
}
}
let shopCar = {
goodsId: 18,
goodsName: '一条抠脚汉的丝袜',
goodsPrice: 89,
quantity: 1
}
const sc = new ShopCar(shopCar)
sc.goodsId = 23 //无效
delete goodsId //无效
sc.newField = '新增属性' //无效
但Object.freeze固然安全,但是使可写的属性也被冻结,此时就需要Object.seal登场了。
class ShopCar {
constructor(car) {
...
Object.defineProperty(this, 'goodsPrice',{
writable: true
})
Object.seal(this)
}
}
let shopCar = {
goodsId: 18,
goodsName: '一条抠脚汉的丝袜',
goodsPrice: 89,
quantity: 1
}
const sc = new ShopCar(shopCar)
sc.goodsPrice = 138 //可写属性在seal状态下可以被修改
以上方法都可以对属性进行一个有效的限制。
限制2
可以在setter中对即将修改的数据进行类型验证,保证类型是符合要求的。
class ShopCar{
constructor(car) {
let internalIdValue = 23
Object.defineProperty(this, 'goodsId',{
get() {
return internalIdValue
},
set(val) {
if (typeof val === 'number') {
internalIdValue = val
} else {
throw Error("修改的值不符合类型")
}
}
})
}
}
let shopCar = {
...,
goodsId: 23
}
const sc = new ShopCar(shopCar)
sc.goodsId = '你猜' //抛出错误
限制3
通过Object.freeze限制原型是最好的选择。
Object.freeze(ShopCar.prototype)
ShopCar.prototype.incrementCar = '英菲尼迪' //无效
小结
还有更多的限制都可以通过属性描述符实现,通过getter和setter可以写出许多的"故事"。
我有bug,但是各位绝不能有bug。
我家三代农民,不说了各位,麦子熟了。
撤!
转载自:https://juejin.cn/post/7390344585673932852