Proxy例項set()

GXing007發表於2018-03-27

set()方法用來攔截某個屬性的賦值操作,可以接受4個引數,依次為目標物件,屬性名,屬性值,Proxy物件本身,最後一個引數可選。

假定Person物件有一個age屬性,該屬性應該是一個不大於 200 的整數,那麼可以使用Proxy保證age的屬性值符合要求。

let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    // 對於滿足條件的 age 屬性以及其他屬性,直接儲存
    obj[prop] = value;
  }
};

let person = new Proxy({}, validator);

person.age = 100;

person.age // 100
person.age = 'young' // 報錯
person.age = 300 // 報錯
上面程式碼中,由於設定了存值函式set,任何不符合要求的age屬性賦值,都會丟擲一個錯誤,這是資料驗證的一種實現方法。利用set方法,還可以資料繫結,即每當物件發生變化時,會自動更新 DOM。

有時,我們會在物件上面設定內部屬性,屬性名的第一個字元使用下劃線開頭,表示這些屬性不應該被外部使用。結合getset方法,就可以做到防止這些內部屬性被外部讀寫。

const handler = {
  get (target, key) {
    invariant(key, 'get');
    return target[key];
  },
  set (target, key, value) {
    invariant(key, 'set');
    target[key] = value;
    return true;
  }
};
function invariant (key, action) {
  if (key[0] === '_') {
    throw new Error(`Invalid attempt to ${action} private "${key}" property`);
  }
}
const target = {};
const proxy = new Proxy(target, handler);
proxy._prop
// Error: Invalid attempt to get private "_prop" property
proxy._prop = 'c'
// Error: Invalid attempt to set private "_prop" property

上面程式碼中,只要讀寫的屬性名的第一個字元是下劃線,一律拋錯,從而達到禁止讀寫內部屬性的目的。

下面是set方法第四個引數的例子。

const handler = {
  set: function(obj, prop, value, receiver) {
    obj[prop] = receiver;
  }
};
const proxy = new Proxy({}, handler);
proxy.foo = 'bar';
proxy.foo === proxy // true

上面程式碼中,set方法的第四個引數receiver,總是返回this關鍵字所指向的那個物件,即proxy例項本身。

注意,如果目標物件自身的某個屬性,不可寫也不可配置,那麼set不得改變這個屬性的值,只能返回同樣的值,否則報錯。