初識ES6 Proxy屬性

前言

Proxy對象是ES6(ECMAScript 6)中提供用來操做對象的一個API,其含義官方給出的解釋是:Proxy 對象用於定義基本操做的自定義行爲(如屬性查找,賦值,枚舉,函數調用等)
es6

  • 小白:額~,讀了三遍,仍是不太明白。
  • 隔壁老王:Proxy本意是代理,那既然代理,確定要具有代理的倆基本素質,1.幫誰代理,2.乾點什麼。因此, 幫誰代理? 幫目標對象代理,乾點什麼?架設攔截,對外界的訪問進行過濾和改寫,串起來講就是:在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫。這下明白了吧,嘿嘿。
  • 小白:喔育,不愧是隔壁老王,就是厲害~

語法

let proxy = new Proxy(target, handler);
複製代碼

tgarget:要代理的目標對象。(能夠是任何類型的對象,包括原生數組,函數,甚至另外一個代理)。
handler:定義攔截行爲的配置對象(也是一個對象,其內部的屬性均爲執行操做的函數)。數組

  • proxy支持的攔截操做一共 13 種,即支持13種代理行爲(函數),這些函數若是你須要複寫其行爲方式,均要定義在handler這個對象中使用,下面咱們就針對這些支持的比較經常使用的操做函數進行舉例說明。其他的不是很經常使用,若是想了解能夠參考文末的資料去學習。

get(target, key, receiver)

含義:攔截對象屬性的讀取
target:要代理的目標對象。
key:屬性名。
receiver:proxy實例(可選參數,通常不用)
bash

const proxy1 = new Proxy({
            a: 1
        }, {
            get(target, key) {
              if(Reflect.has(target,key)) {
                return Reflect.get(target,key);
              }else {
                  return false;
              }
              
            }
    })
        proxy1.a //1
        proxy1.b //false
複製代碼

以上,a屬性由於有定義,因此可獲取到其值爲1,b屬性沒有被定義,輸出爲false,若是這裏沒有定義get方法,那麼獲取操做就會被轉發到目標對象身上,默認若是沒有此屬性,會返回undefined,而這裏卻返回false,正是由於從新定義了get方法,對返回內容進行了修改,正如其含義,對代理對象屬性的讀取進行了攔截。app

set(target, key, value, receiver)

含義:攔截對象屬性的設置
target:要代理的目標對象。
key:要設置的屬性名。
value:要設置的屬性值。
receiver:proxy實例(可選參數,通常不用)
函數

const handler = {
           set(target,key,value) {
               if(!Reflect.has(target,key)) {
                   Reflect.set(target,key,value);
               }else {
                  console.log(`${key}屬性已存在`) //a屬性已存在
               }
           },
           get(target,key) {
            return Reflect.get(target,key);
           }

       }
    const proxy = new Proxy({},handler);
     proxy.a = 1;
     console.log(proxy.a);//1
     proxy.a = 2;
     console.log(proxy.a) //1
複製代碼

以上,設置屬性的時候若是當前屬性已存在,則不能設置成功,所以,咱們能夠利用set方法來攔截設置符合咱們指望的屬性,根據應用場景,自由發揮。學習

var obj = {};
   const target = Object.defineProperty(obj, 'a', {
            value: 123,
            writable: false,
    });
    const handler = {
        set(target,key,value) {
            Reflect.set(target,key,value);
        },
        get(target, key) {
            return Reflect.get(target,key);
        }
    };

    const proxy = new Proxy(target, handler);
    proxy.a = 456;
   console.log( proxy.a) //123
複製代碼

以上,當目標對象的某個屬性爲不可寫狀態,那麼set方法將會失效。ui

  • 看到不少資料都寫到是當目標對象的屬性爲不可寫且不可配置狀態,set方法將會失效,但自測發現其結果與configurable屬性的狀態並沒有關係,只與writable屬性有關,有點小疑惑,但願看到的大佬能幫小弟解惑。

has(target, Key)

含義:判斷對象是否具備某個屬性。
target:要代理的目標對象。
key:要設置的屬性名。
當判斷一個對象中是否具備某個屬性時,has方法就會生效,典型的操做就是in運算符應用,返回值爲布爾類型this

const handler = {
            has(target, key) {
                if (key[0] === '_') {
                    console.log('it is a private property')
                    return false;
                }
                return key in target;
            }
        };
        const target = {
            _a: 123,
            a: 456
        };
        const proxy = new Proxy(target, handler);
        console.log('_a' in proxy) // it is a private property  false
        console.log('a' in proxy);//true
複製代碼

以上,當對proxy使用in運算符時,就會自動觸發has方法,若是判斷當前屬性以_開頭的話,就進行攔截,從而不被後面的in運算符發現,實現隱藏某些特定屬性的目的。spa

不過須要注意,當目標對象是不可擴展或者對象的屬性是不可配置時has方法不能隱藏目標對象的當前屬性,不然攔截會報錯。代理

var obj = {
            a: 10
        };
       Object.preventExtensions(obj); //使obj對象不可擴展,也就是使其不能增長新的屬性
        var p = new Proxy(obj, {
            has: function (target, key) {
                return false;
            }
        });

        'a' in p // Uncaught TypeError: 'has' on proxy: trap returned falsish for property 'a' but the proxy target is not extensible
        
        

   let obj = {};
        Object.defineProperty(obj, "a", {
            configurable: false, //當前屬性不可配置
            value: 10,
        });
        var p = new Proxy(obj, {
            has: function (target, key) {
                return false;
            }
        });

        'a' in p // Uncaught TypeError: 'has' on proxy: trap returned falsish for property 'a' which exists in the proxy target as non-configurable
    
複製代碼

apply(target, thisArgs, args)

含義:攔截函數的調用、call和apply操做
target:目標對象。
thisArgs:目標對象的上下文對象(this)。
args:目標對象的參數數組。

const handler = {
            apply(target, ctx, args) {
                return Reflect.apply(...arguments) * 2;
                //或者
                return target(...args) * 2
            }
        };

        function sum(...args) {
            let num = 0;
            args.forEach((item) => {
                num += item;
            })
            return num;
        }

        var proxy = new Proxy(sum, handler);
        proxy(1, 2) // 6
        proxy.call(null, 5, 6) // 22
        proxy.apply(null, [7, 8]) // 30
複製代碼

以上,目標對象這裏是sum,必須是一個函數,不然調用會報錯。每當proxy函數被直接調用或者call,apply方式調用都會當即觸發apply方法,從而該調用被apply方法攔截,這樣就能夠利用攔截修改返回值,若是不寫apply方法,默認調用sum方法,返回 結果。

construct(target, args,newTarget)

含義:攔截new命令
target:目標對象。
args:構造函數的參數列表。
newTarget:建立實例對象時,new命令做用的構造函數(下面例子的p)。

var p = new Proxy(function () {}, {
            construct: function (target, args, newTarget) {
                console.log('called: ' + args.join(', '));
                return {
                    value: args[0] * 10
                };

               // return 1 //Uncaught TypeError: 'construct' on proxy: trap returned non-object ('1')
            }
        });

        (new p(1)).value
        // "called: 1"
        // 10
複製代碼

以上,代理的目標對象必須是一個構造函數(只有構造函數纔可使用new操做符),當執行new p(1)時,會馬上觸發construct函數,也就是會被該函數攔截,這裏的返回值有講究,必須返回對象,不然會報錯。construct方法執行完畢,p實例也就初始化完成了。

Proxy代理時 target 內部 this 的指向會發生改變

const target = {
            foo: function () {
                console.log(this === proxy);
            }
        };
        const handler = {};
        const proxy = new Proxy(target, handler);

        target.foo() // false
        proxy.foo() // true

複製代碼

能夠看出,當目標對象一旦被proxy代理後,其內部this就會指向proxy,而非自身,所以須要注意這點。

結語

好啦,今天關於Proxy的介紹就到這了,以上也只是針對幾個比較經常使用的操做函數進行了舉例說明,若是想了解更多,建議參考如下資料,如有錯誤,還望各位大神指正!

阮一峯:ECMAScript 6 入門-Proxy
阮一峯:ECMAScript 6 入門-Reflect
Proxy - JavaScript | MDN

相關文章
相關標籤/搜索