likes
comments
collection
share

可可爱爱的属性描述符属性描述符想必大家都耳熟能详,近日一男子针对属性描述符做了不太详细的介绍,旨在记录通过属性描述符对属

作者站长头像
站长
· 阅读数 21

本文旨在介绍属性描述符。同时温习两个小可爱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  指定属性被读取时的回调getter
  • set  指定属性被修改时的回调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
评论
请登录