初識Proxy、Reflect

前言

https://segmentfault.com/a/11...

Vue3.0應該立刻就要發佈正式版了。據說在新版本中,Proxy取代了Object.defineProperty進行雙向綁定。主要緣由應該是Object.defineProperty在處理數組響應是會存在缺陷。javascript

let demo = {};
let arr = [];

Object.defineProperty(demo, "arr", {
  get: function() {
    return arr;
  },
  set: a => {
    console.log("hear set");
    arr = a;
  },
  configurable: true,
  enumerable: true
});

demo.arr = [1, 2, 3, 4]; // => 'hear set';
console.log(arr === demo.arr); // => true
/* 經過索引修改數組,沒法觸發set */
demo.arr[2] = null; // => 沒有輸出hear set。
console.log(arr === demo.arr); // => true
/* 經過push,pop,sort等方法一樣沒法觸發set */
demo.arr.push("13"); // => 沒有輸出hear set。

Edit static

Proxy

http://es6.ruanyifeng.com/#do...

示例

Proxy原意爲代理,在實際操做中,能夠理解爲,在目標數據前增長一個攔截器。經過攔截器,能夠實現監聽、修改目標數據。java

let obj = new Proxy(
  {},
  {
    set: function(target, key, receiver) {
      console.log(`set key ${key}`);
      return Reflect.set(target, key, receiver);
    },
    get: function(target, key, receiver) {
      console.log(`get key ${key}`);
      return Reflect.get(target, key, receiver);
    }
  }
);

obj.count = 1;
// => set key count;
obj.count++;
// => get key count;
// => set key count;

Edit static

構造函數

let obj = new Proxy(target, handler)

其中new Proxy表示生成一個Proxy實例,target爲須要代理的對象,handler則是一個對象,定義了各類代理行爲。
若是handler爲空對象,訪問proxy與訪問target效果相同。es6

Proxy的實例方法

get()

get方法用於攔截某個屬性的讀取操做,能夠接受三個參數,依次爲目標對象、屬性名和 proxy 實例自己(嚴格地說,是操做行爲所針對的對象),其中最後一個參數可選。segmentfault

var person = {
  name: "張三"
};

var proxy = new Proxy(person, {
  get: function(target, property) {
    if (property in target) {
      return target[property];
    } else {
      throw new ReferenceError("Property \"" + property + "\" does not exist.");
    }
  }
});

proxy.name // "張三"
proxy.age // 拋出一個錯誤

set()

set方法用來攔截某個屬性的賦值操做,能夠接受四個參數,依次爲目標對象、屬性名、屬性值和 Proxy 實例自己,其中最後一個參數可選。數組

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 // 報錯

has()

has(target, propKey)攔截propKey in proxy的操做,返回一個布爾值。app

deleteProperty()

deleteProperty(target, propKey)攔截delete proxy[propKey]的操做,返回一個布爾值。函數

ownKeys()

ownKeys(target)攔截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循環,返回一個數組。該方法返回目標對象全部自身的屬性的屬性名,而Object.keys()的返回結果僅包括目標對象自身的可遍歷屬性。spa

getOwnPropertyDescriptor()

getOwnPropertyDescriptor(target, propKey)攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述對象。雙向綁定

defineProperty()

defineProperty(target, propKey, propDesc)攔截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一個布爾值。代理

preventExtensions()

preventExtensions(target)攔截Object.preventExtensions(proxy),返回一個布爾值。

getPrototypeOf()

getPrototypeOf(target)攔截Object.getPrototypeOf(proxy),返回一個對象。

isExtensible()

isExtensible(target)攔截Object.isExtensible(proxy),返回一個布爾值。

setPrototypeOf()

setPrototypeOf(target, proto)攔截Object.setPrototypeOf(proxy, proto),返回一個布爾值。若是目標對象是函數,那麼還有兩種額外操做能夠攔截。

apply()

apply(target, object, args)攔截Proxy實例做爲函數調用的操做,好比proxy(...args)proxy.call(object, ...args)、proxy.apply(...)

construct()

construct(target, args)攔截Proxy實例做爲構造函數調用的操做,好比new proxy(...args)

Reflect

http://es6.ruanyifeng.com/#do...

簡介

Reflect對象與Proxy對象同樣,也是 ES6 爲了操做對象而提供的新 API。其目的是:

  1. Object對象的一些明顯屬於語言內部的方法(好比Object.defineProperty),放到Reflect對象上。
  2. 修改某些Object方法的返回結果,讓其變得更合理。
  3. Object操做都變成函數行爲。
  4. Reflect對象的方法與Proxy對象的方法一一對應

使用Proxy監聽數組

const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, { set });
function set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);
    queuedObservers.forEach(observer => observer());
    return result;
}
const person = observable({
    name: "Amber",
    age: 18
});
function print() {
    console.log(`${person.name}, ${person.age}`);
}
observe(print);

Edit static

使用Proxy、Reflect實現觀察者模式

相關文章
相關標籤/搜索