zoukankan      html  css  js  c++  java
  • javascript之Object.defineProperty的奥妙

    直切主题

    今天遇到一个这样的功能:

    • 写一个函数,该函数传递两个参数,第一个参数为返回对象的总数据量,第二个参数为初始化对象的数据。如:
    var o = obj (4, {name: 'xu', age: 21})   //  返回了一个能容纳4条数据的对象,初始数据为name:'xu'和age: 21
    • 返回的该对象总会有以下属性:overLength(数据容纳量)、size(当前数据条数)
    • 返回的对象应该有以下方法:cache(保存一条数据)、delete(删除一条数据)
    • 每一次引用某属性后,该属性值消失。如:
    o.name // xu
    o.name //undefined
    o.size //1

    那么问题来了,如何设置属性在使用一次后自动消除呢,经过思考,想到了,get和set方法,get和set又是什么呢,如下:

    var o = {
      _id: 11,
      get id () {
        return this._id + '_get'
      },
      set id (value) {
        this._id = value + '_set'
      }
    }
    
    console.log(o.id)  // 11_get
    o.id = '666'
    console.log(o.id)  // 666_set_get

    js中引用属性的值视为get,给属性赋值视为set,get和set方法可以在获取/设置属性值时进行一定的操作。

    本文中所用到了get和set方法,不过并不是上述例子那样简单的在对象中书写,我们借助了本文的主角Object.defineProperty方法。

    什么是Object.defineProperty

    Object.defineProperty是es5新加的给对象属性设置描述符的方法,使用方法如下:

    Object.defineProperty(obj, prop, descriptor)  // 对象、属性、描述符

    基本的描述符有3个:

    • writable  --  是否为可写
    • configurable  --  是否为可配置的
    • enumerable  --  是否为可枚举的

    下面分别举个简单的例子来说明其用法

    writable

    顾名思义,设置属性是否为可写,如果是false,则属性之后的赋值操作无效

    var o = {
      name: 'xu'
    }
    Object.defineProperty(o, 'name', {
      writeable:false
    })
    o.name = 'lee'
    console.log(o.name)  //xu

    configurable

    属性是否可配置,设置为false后,该属性不可被删除,也不可再更改为可配置的,但是可以从可写改为不可写

    var o = {
      name: 'xu'
    }
    Object.defineProperty(o, 'name', {
      configurable:false
    })
    o.name = 'lee'
    console.log(o.name)  // lee   不可配置可写
    delete o.name
    console.log(o.name)  //lee  删除失败
    Object.defineProperty(o, 'name', {
      writable: false
    })
    o.name ='li'
    console.log(o.name)  // lee  不可配置不可写

    enumerable

    属性是否可枚举,如果是false,则属性不可枚举,不可枚举属性对  for ... in语句和Ojbect.keys是不可见的。

    var o = {
      name: 'xu',
      age: 21
    }
    Object.defineProperty(o, 'name', {
      enumerable: false
    })
    console.log(Object.keys(o)) //["age"]

    分界线----------------------------------------------------------------------------

    除了上面三个属性,defineProperty方法内部还可以定义属性的value值, get/set方法,那么本章则用到了使用Object.defineProperty方法定义属性的get/set值。

    问题解答

    思路:本题主要围绕着属性的get/set做进一步处理,要求引用一次属性值后,属性值消失(undefined),那么就需要在get方法中做文章,所以我们先制定一个定义属性描述符的方法,我把它定义成这样:

    var _defineProperty = function (ret, key) {
        Object.defineProperty(ret, key, {
          set: function (value) {
            //  针对 o.key = value 的set方法
            if (!datas[key]) {
              ret.size++
            }
            datas[key] = value
          },
          get: function () {
            //  获取当前数据,如果当前数据有值,返回值并将当前属性值设置为undefined
            var res = undefined
            if (typeof datas[key] !== undefined) {
              res = datas[key]
              ret.size--
              datas[key] = undefined
            }
            return res
          }
        })
      }

    本题的主要部分就在这里,这段代码利用的属性的get和set方法,针对题目的要求,做出了这个功能函数。注意:返回的对象ret并没有保存数据,他只是通过set和get方法对datas缓存对象中的数据进行的设置和读取。

    datas对象是这样的,他利用的es6中的Object.assign方法,把init拷贝了一份作初始化

    //  缓存数据
      var datas = Object.assign({}, init)

    整个代码就在这里,也很简单,写的也比较乱。如果有问题,请指出。

    function FirstVaild (overLength, init) {
    
      //  定义最终返回对象,初始两个值,一个对象总容量,一个是当前长度
      var ret = {
        overLength: overLength, 
        size: Math.min(Object.keys(init).length, overLength)  //如果传入初始数据条数大于容量,取容量值
      }
      //  缓存数据
      var datas = Object.assign({}, init)
    
      //  定义属性的函数
      var _defineProperty = function (ret, key) {
        Object.defineProperty(ret, key, {
          set: function (value) {
            //  针对 o.key = value 的set方法
            if (!datas[key]) {
              ret.size++
            }
            datas[key] = value
          },
          get: function () {
            //  获取当前数据,如果当前数据有值,返回值并将当前属性值设置为undefined
            var res = undefined
            if (typeof datas[key] !== undefined) {
              res = datas[key]
              ret.size--
              datas[key] = undefined
            }
            return res
          }
        })
      }
    
      //  将dirty初始化false,并定义每个属性的get/set
      Object.keys(init).slice(0,ret.size).map(function (key) {
        _defineProperty(ret, key)
      })
    
    
      Object.assign(ret, {
        cache: function (key ,value) {
          if (this.size >= this.overLength) {
            throw '内存已满,请扩展容量'
          }
          //  属性不存在,定义他,并且长度+1, 属性存在但值不存在,长度+1,
          if (!(key in datas)) {
            _defineProperty(this, key)
            this.size++
          } else if (!datas[key]) {
            this.size++
          }
          //  不论怎样,新值覆盖旧值
          datas[key] = value
          return this
        },
        //  删除属性直接把datas中的属性设置为undefined
        delete: function (key) {
          datas[key] = undefined
          this.size--
          return this
        }
      })
      return ret
    }

    本章就到这里,博主为一介前端菜鸟,并没有多少知识,try my best,写点基础知识,分享给正在学习前端的新人们。

  • 相关阅读:
    MYSQL实战-------丁奇(极客时间)学习笔记
    java并发编程实践——王宝令(极客时间)学习笔记
    分布式锁-----秒杀系统
    MYSQL IN 出现的慢查询问题
    携程2018年年度技术合集
    MySQL分库分表
    Mysql 千万级别数据数据查询
    视频协议融合平台人脸识别/车牌识别平台EasyCVR内调用接口二次开发疑难解答
    国标GB28181/Ehone协议视频人脸识别/车牌识别平台EasyCVR新版本支持大华SDK接入开发记录
    国标GB28181协议接入视频智能分析平台EasyCVR的设备用ws_flv为什么会有无法播放的情况?
  • 原文地址:https://www.cnblogs.com/xujiazheng/p/6123633.html
Copyright © 2011-2022 走看看