深刻理解ES6筆記(十一)代理(Proxy)和反射(Reflection)API(12)

主要知識點:代理和反射的定義、經常使用的陷阱函數、可被撤銷的代理、將代理對象做爲原型使用、將代理做爲類的原型
圖片描述

《深刻理解ES6》筆記 目錄segmentfault

代理與反射

  • 代理是什麼?

經過調用 new Proxy() ,你能夠建立一個代理用來替代另外一個對象(被稱之爲目目標對象) ,這個代理對目標對象進行了虛擬,所以該代理與該目標對象表面上能夠被看成同一個對象來對待。
代理容許你攔截目標對象上的底層操做,而這原本是JS引擎的內部能力,攔截行爲適用了一個能響應特定操做的函數(被稱之爲陷阱);函數

  • 反射是什麼?

被Reflect對象所表明的反射接口,是給底層操做提供默認行爲的方法的集合,這些操做是可以被代理重寫的。每一個代理陷阱都有一個對應的反射方法,每一個方法都與對應的陷阱函數同名,而且接收的參數也與之一致。spa

建立一個簡單的代理

使用Proxy構建能夠建立一個簡單的代理對象,須要傳遞兩個參數:目標對象以及一個處理器,後者是定義一個或多個陷阱函數的對象。若是不定義陷阱函數,則依然使用目標對象的默認行爲:代理

let target = {};
let proxy = new Proxy(target, {});
proxy.name = "proxy";
console.log(proxy.name); // "proxy"
console.log(target.name); // "proxy"
target.name = "target";
console.log(proxy.name); // "target"
console.log(target.name); // "target"

使用 set 陷阱函數驗證屬性值

假設你想要建立一個對象,並要求其屬性值只能是數值,這就意味着該對象的每一個新增屬性都要被驗證,而且在屬性值不爲數值類型時應當拋出錯誤。爲此你須要定義 set 陷阱函數來重寫設置屬性值時的默認行爲,該陷阱函數能接受四個參數:code

  1. trapTarget :將接收屬性的對象(即代理的目標對象);
  2. key :須要寫入的屬性的鍵(字符串類型或符號類型);
  3. value :將被寫入屬性的值;
  4. receiver :操做發生的對象(一般是代理對象)。

Reflect.set() 是 set 陷阱函數對應的反射方法,同時也是 set 操做的默認行爲。
Reflect.set() 方法與 set 陷阱函數同樣,能接受這四個參數,讓該方法能在陷阱函數內部被方便使用:對象

let target = {
    name: "target"
};
let proxy = new Proxy(target, {
    set(trapTarget, key, value, receiver) {
        // 忽略已有屬性,避免影響它們
        if (!trapTarget.hasOwnProperty(key)) {
            if (isNaN(value)) {
                throw new TypeError("Property must be a number.");
            }
        }
        // 添加屬性
        return Reflect.set(trapTarget, key, value, receiver);
    }
});
// 添加一個新屬性
proxy.count = 1;
console.log(proxy.count); // 1
console.log(target.count); // 1
// 你能夠爲 name 賦一個非數值類型的值,由於該屬性已經存在
proxy.name = "proxy";
console.log(proxy.name); // "proxy"
console.log(target.name); // "proxy"
// 拋出錯誤
proxy.anotherName = "proxy";

使用 get 陷阱函數進行對象外形驗證

JS 語言有趣但有時卻使人困惑的特性之一,就是讀取對象不存在的屬性時並不會拋出錯誤,而會把 undefined 看成該屬性的值,例如:blog

let target = {};
console.log(target.name); // undefined

在多數語言中,試圖讀取 target.name 屬性都會拋出錯誤,由於該屬性並不存在;但 JS 語言卻會使用 undefined 。
對象外形( Object Shape )指的是對象已有的屬性與方法的集合,因爲該屬性驗證只須在讀取屬性時被觸發,所以只要使用 get 陷阱函數。該陷阱函數會在讀取屬性時被調用,即便該屬性在對象中並不存在,它能接受三個參數:接口

  1. trapTarget :將會被讀取屬性的對象(即代理的目標對象);
  2. key :須要讀取的屬性的鍵(字符串類型或符號類型);
  3. receiver :操做發生的對象(一般是代理對象)。

相應的Reflect.get()方法一樣擁有這三個參數。進行對象外形驗證的示例代碼:圖片

//import {color,sum,magicNumber} from "./export.js"
import * as example from "./export.js"
console.log(example.color)
console.log(example.magicNumber)

console.log(example.


    sum(76, 2))



let proxy = new Proxy({}, {
    get(trapTarget, key, receiver) {
        if (!(key in receiver)) {
            throw new TypeError("Property " + key + " doesn't exist.");
        }
        return Reflect.get(trapTarget, key, receiver);
    }
});
// 添加屬性的功能正常
proxy.name = "proxy";
console.log(proxy.name); // "proxy"
// 讀取不存在屬性會拋出錯誤
console.log(proxy.nme); // 拋出錯誤

使用 has 陷阱函數隱藏屬性

in運算符用於判斷指定對象中是否存在某個屬性,若是對象的屬性名與指定的字符串或符號值相匹配,那麼in運算符就會返回true。不管該屬性是對象自身的屬性仍是其原型的屬性。
has陷阱函數會在使用in運算符的狀況下被調用,控制in運算符返回不一樣的結果,has陷阱函數會傳入兩個參數:字符串

trapTarget:代理的目標對象;
key:屬性鍵;

Reflect.has()方法接收相同的參數,並向in運算符返回默認的響應結果,用於返回默認響應結果。

let target = {
    name: "target",
    value: 42
};
let proxy = new Proxy(target, {
    has(trapTarget, key) {
        if (key === "value") {
            return false;
        } else {
            return Reflect.has(trapTarget, key);
        }
    }
});
console.log("value" in proxy); // false
console.log("name" in proxy); // true
console.log("toString" in proxy); // true
相關文章
相關標籤/搜索