ES6之 Proxy 的 get 方法

這篇是我之前寫的文章,若有錯誤,請指正,謝謝!bash


Proxy是在ES2015(ES6)中新添加內置對象,用於自定義一些基本操做。函數

這篇文章是我在學習Proxy的時候對於get方法的一些心得。學習

做爲ES2015新定義的內置對象,Proxy 可以攔截而且自定義對象以及函數的一些基本操做,具備很高的優先級和便利性,可以讓咱們在寫代碼的時候多出一種解決難題的途徑。ui

Proxy的get方法用於攔截對象屬性的讀取操做,例如 obj.key 和 obj.[key]。在給Proxy的handler參數中設置get方法後,每當進行讀取操做時,會優先調用該get方法,咱們能夠在這個方法函數中對讀取行爲進行攔截。請看下面的代碼:spa

const obj = { key: 1 }
const proxy = new Proxy(obj, {
  get: function(target, property, receiver) {
    console.log('get', property)
    return target[property]
  }
})
console.log(proxy.key)
// get key
// 1
複製代碼

get方法的參數一共有三個:target是實例化Proxy時使用的對象,在這個例子中是obj;而property是此次讀取操做中想要獲取的屬性名,在這個例子中是key;最後一個參數receiver則是這個實例化的Proxy自身,即proxy。code

在這個例子中,我在get方法的最後返回了target[property],這是爲了可以讓讀取操做可以進行下去。因爲Proxy的get方法是最早被調用的,因此這裏返回的內容就是咱們讀取操做可以得到的結果;若是咱們在這裏不返回任何值,那麼就會獲得undefined。對象

receiver和死循環

要注意的是,千萬不要在get方法中讀取receiver的屬性,由於receiver實質上就是proxy自身,因此receiver.key這句代碼就等同於proxy.key,會從新調用get方法致使死循環。繼承

const obj = { key: 1 }
const proxy = new Proxy(obj, {
  get: function(target, property, receiver) {
    console.log(receiver.key)
    return target[property]
  }
})
console.log(proxy.key)
// 死循環!
複製代碼

原型鏈上的getter

有時候,咱們會在對象之中使用getter和setter來定製屬性的賦值和讀取。在這時,若是proxy的get方法內部有使用到target[property]的話,target[property]的值會受到目標對象的getter的影響。所以調用get方法的時候請注意在目標對象中是否有用到getter。原型鏈

const obj = {
  get key() {
    return 'string'
  },
  set key(value) {
    console.log(`key is ${value}, it is a ${typeof value}`)
  }
}
const proxy = new Proxy(obj, {
  get: function(target, property, receiver) {
    if(typeof target[property] !== 'string') {
      return target[property]
    } else {
      throw new TypeError(`The type of ${property} is String!`)
    }
  }
})
proxy.key = 100
console.log(proxy, obj)
// key is 100, it is a number
// The type of key is String!
複製代碼

在上方的例子中,若是訪問obj的非數字類型的屬性,就會拋出一個錯誤,可是因爲obj中getter的緣由,不管我給key屬性賦什麼值,在訪問key屬性的時候確定會拋出錯誤。 若是僅僅要注意目標對象中的getter還算容易的,可是若是目標對象繼承自其餘對象,那麼事情就變得有些麻煩了,請看下面的例子:get

const parentObj = {
  get key() {
    return 'string'
  },
  set key(value) {
    console.log(`key is ${value}, it is a ${typeof value}`)
  }
}
const obj = Object.create(parentObj)
const proxy = new Proxy(obj, {
  get: function (target, property, receiver) {
    if (typeof target[property] !== 'string') {
      return target[property]
    } else {
      throw new TypeError(`The type of ${property} is String!`)
    }
  }
})
proxy.key = 100 
console.log(proxy.key)
// key is 100, it is a number
// The type of key is String!
複製代碼

如代碼所示,目標對象obj繼承自parentObj,而parentObj中使用了getter方法,那麼使用proxy的get方法仍舊會報錯。實際運用中原型鏈可能會很長,getter可能會存在於原型鏈的任何一個地方中,因此在使用Proxy的get方法時請必定要注意。

可是若是把parentObj上的key遮蔽掉,就不會發生拋出錯誤的狀況了。好比在建立obj的時候申明的key,代碼以下:

const parentObj = {
  get key() {
    return 'string'
  },
  set key(value) {
    console.log(`key is ${value}, it is a ${typeof value}`)
  }
}
const obj = Object.create(parentObj, {
  key: {
    value: null,
    writable: true
  }
})
const proxy = new Proxy(obj, {
  get: function (target, property, receiver) {
    if (typeof target[property] !== 'string') {
      return target[property]
    } else {
      throw new TypeError(`The type of ${property} is String!`)
    }
  }
})
proxy.key = 100 
console.log(proxy.key)
// 100
複製代碼

一樣的,咱們也可使用Object.defineProperty()和Object.assign()這兩個方法來達到相同的目的:

Object.defineProperty(obj, 'key', {
  value: null,
  writable: true
})
obj = Object.assign({}, obj, { key: null })
複製代碼

可是要注意使用Object.assign()的時候不能這麼些:

obj = Object.assign(obj, { key: null })
複製代碼

這樣寫法沒法遮蔽掉parentObj上的key屬性,使用的時候仍舊會拋出錯誤。


謝謝閱讀,我的成果,請勿轉載:)

相關文章
相關標籤/搜索