快來圍觀一下JavaScript的Proxy

descire發表於2018-05-26

的確寫Proxy文章很多,那麼今天我也不湊字數了,炒兩個栗子吧。

一、虛擬屬性

  const person = {
    name: 'xiaoyun',
    province: '江蘇省',
    city: '南京市'
  }
複製程式碼

對於上述物件,我們可能需要地址資訊(由省市拼接而成),在此之前我們可能會採取下列方式:

  • 直接在person物件上宣告一個address屬性;
  • 當用到address資訊時,再通過person拼接。

第一個方法的主要弊端是汙染了原有的物件,而第二種方法就很不靈活。現在我們可以通過Proxy實現比較好的效果:

  const enhancePerson = new Proxy(person, {
    get (target, name) {
      switch (name) {
        case 'address':
          return `${target['province']}-${target['city']}`
        default:
          return target[name]
      }
    }
  })
  enhancePerson.address // 江蘇省-南京市
複製程式碼

通過這種方式我們就可以實現虛擬屬性了,因為它不會被遍歷出來:

  Object.keys(enhancePerson) // [ 'name', 'province', 'city' ]
複製程式碼

這裡還有一個我覺得比較容易忽略的點:

  person === enhancePerson // false
  enhancePerson.city = '蘇州市'
  enhancePerson.city === person.city // true
複製程式碼

顯然這兩個並不是同一個物件,但是我通過改變enhancePerson的city屬性卻影響到了person的city屬性,這就是我們通常會忽略掉的,如果你不設定Proxy的set方法,它會保持預設行為:

  set (target, propKey, value) {
    target[propKey] = value
  }
複製程式碼

可能有些同學會想不就是不讓它遍歷出來嗎?看這招:

  const person = {
    name: 'xiaoyun',
    province: '江蘇省',
    city: '南京市',
    get address () {
      return `${this.province}-${this.city}`
    }
  }

  const enhancePerson = new Proxy(person, {
    ownKeys (target) {
      return Object.keys(target).filter(item => item !== 'address')
    }
  })

  enhancePerson.address // 江蘇省-南京市
  Object.keys(enhancePerson) // [ 'name', 'province', 'city' ]
複製程式碼

雖然是實現了上述的功能,但是Proxy中的ownKeys攔截的方法太多,所以我們攔截ownKeys之後,會導致某些方法無法使用,並且攔截ownKeys返回的結果也有嚴格的要求:

  • 返回的結果必須是一個陣列
  • 並且陣列的元素必須是String或者Symbol型別
  • 結果必須包含物件的所有不可配置、自身擁有的屬性
  • 如果物件不能擴充套件,則結果只能包含自身擁有的屬性,不能含有其他屬性

所以在攔截方法注意點很多,不然很容易出現問題。

Tip: 未通過defineProperty定義的屬性的描述器屬性預設為true,否則預設為false。

二、擴充套件基本操作

當我第一次接觸Python時,比較有印象的就是它的List的一個語法: arr[1:3],以前只有羨慕的份,現在完全可以自己擼一個:

  const arr = [1, 2, 3, 4, 5, 6, 7, 8]

  const list = new Proxy(arr, {
    get (target, name) {
      if (name.includes(':')) {
        const indexs = name.split(':')
        return target.slice(indexs[0], indexs[1])
      }
      return target[name]
    }
  })

  list['2:6'] // [ 3, 4, 5, 6 ]
複製程式碼

是不是?,對於物件,我們同樣可以採用類似的方法:

  const obj = {
    a: {
      b: {
        c: 'xiaoyun'
      }
    }
  }

  const obj1 = new Proxy(obj, {
    get (target, name) {
      const keys = name.split('.')
      return keys.reduce((pre, next) => {
        if (pre !== null && pre !== undefined) {
          pre = pre[next]
        }
        return pre
      }, target)
    }
  })
  obj1['a.b.c'] // xiaoyun
複製程式碼

    喜歡本文的小夥伴們,歡迎關注我的訂閱號超愛敲程式碼,檢視更多內容.

快來圍觀一下JavaScript的Proxy

相關文章