1. 程式人生 > >Javascript Proxy物件 簡介

Javascript Proxy物件 簡介

Javascript Proxy物件 簡介

 

Javascript Proxy物件

 

改變你操作物件的方式

Proxies 是Javasript物件的中介軟體

...或者說至少是那種很早的版本。

ES6 中引入Proxies,讓你可以自定義Object的基本操作。例如,get就是Object的基礎操作方法。

const obj = {
   val: 10
};
console.log(obj.val);

這裡,console.log()表示式在物件obj上執行get方法來獲取val

的值。

另一個物件的基本操作方法是 set

const obj = {
   val: 10
};
obj.val2 = 20;

這裡,set方法用來給物件obj設定一個新的值。

如何建立Proxy?

const proxiedObject = new Proxy(initialObj, handler);

呼叫Proxy建構函式,new Proxy()將返回一個物件,不僅包含了initialObj裡的值,而且其基本操作(如getset)現在可以通過handler物件來指定一些自定義邏輯。

我們寫個例子來理解這個概念,

const handler = {
    get: function() {
        console.log('A value has been accessed');
    }
}

const initialObj = {
    id: 1,
    name: 'Foo Bar'
}

const proxiedObj = new Proxy(initialObj, handler);

console.log(proxiedObj.name);

現在,如果我們沒有構造一個Proxy物件,執行第14行的console.log(proxiedObj.name)會在控制檯輸出 “Foo Bar”。

不過現在我們定義了一個Proxy,並在第三行get方法中定義了一些自定義邏輯。

現在執行console.log(proxiedObj.name)會在控制檯輸出 “A value has been accessed”。

仔細看,你會發現控制檯中實際上有兩條記錄。 “A value has been accessed” 和 undefined。 為什麼?

get運算子的預設實現是返回Object中儲存的值。由於我們將它重寫為只記錄一條語句,該值永遠不會返回,因此第14行的console.log()

輸出undefined

讓我們來解決這個問題!

get運算子有兩個引數 - 物件本身和被訪問的屬性。

const handler = {
    get: function(obj, prop) {
        console.log('A value has been accessed');
        return obj[prop]; // 返回訪問的key在obj的值
    }
}

const initialObj = {
    id: 1,
    name: 'Foo Bar'
}

const proxiedObj = new Proxy(initialObj, handler);

console.log(proxiedObj.name);

返回屬性值

返回屬性值 --- 控制檯的輸出

 

好多了吧!

我們為get提供的自定義覆蓋被稱為“攔截器”(大概基於作業系統攔截的概念)。 handler物件基本上是一個包含一組“攔截”的物件,每當訪問物件屬性時都會被觸發。

我們給set也新增一個“攔截器”。 我們將做同樣的事情 - 任何時候設定一個值,我們將記錄被修改的屬性,以及為該鍵設定的值。

set操作符有三個引數 - 物件本身,被訪問的屬性和為該屬性設定的值。

const handler = {
    get: function(obj, prop) {
        console.log('A value has been accessed');
        return obj[prop];
    },
    set: function(obj, prop, value) {
        console.log(`${prop} is being set to ${value}`);
    }
}

const initialObj = {
    id: 1,
    name: 'Foo Bar'
}

const proxiedObj = new Proxy(initialObj, handler);

proxiedObj.age = 24

新增`set` “攔截器”

這裡,在第18行進行的訪問將觸發第6行定義的功能,該功能將記錄正在訪問的屬性和正在設定的值。

`Set` “攔截器” —— 控制檯的輸出

一個真實的例子

假設我們有一個定義叫person的物件

const person = {
   id: 1,
   name: 'Foo Bar'
};

如果我們想讓這個物件的id屬性是一個私有屬性呢? 沒人能夠通過person.id訪問這個屬性,如果有人這樣做,我們需要丟擲一個錯誤。 我們將如何做到這一點?

讓Proxies來拯救吧!‍

我們所需要做的就是給這個物件建立一個Proxy,並覆蓋get運算子來阻止我們訪問id屬性!

const handler = {
    get: function(obj, prop) {
        if (prop === 'id') { // Check if the id is being accessed
            throw new Error('Cannot access private properties!'); // Throw an error
        } else {
            return obj[prop]; // If it's not the id property, return it as usual
        }
    }
}

const person = {
    id: 1,
    name: 'Foo Bar'
}

const proxiedPerson = new Proxy(person, handler);

console.log(proxiedPerson.id);

阻止訪問私有屬性

這裡,在我們給get建立的“攔截器”,我們檢查被訪問的屬性是否是id屬性,如果是的話,我們會丟擲一個錯誤。 否則,我們照常返回值。

私有屬性 — 控制檯輸出

另一個極好的用例是校驗。 通過設定set“攔截器”,我們可以在設定值之前新增自定義驗證。 如果該值不符合驗證,我們可以丟擲一個錯誤!

const handler = {
    set: function(obj, prop, value) {
        if (typeof value !== 'string') {
            throw new Error('Only string values can be stored in this object!');
        } else {
            obj[prop] = value;
        }
    }
}

const obj = {};

const proxiedObj = new Proxy(obj, handler);

console.log(proxiedObj); // This will log an empty object
proxiedObj.name = 'Foo Bar'; // This should be allowed
console.log(proxiedObj); // This will log an object with the name property set

proxiedObj.age = 24; // This will throw an error.

自定義物件的屬性校驗

自定義校驗 - 控制檯輸出

在上面的例子中,我們已經看到了getset“陷阱”。 實際上可以設定更多的“陷阱”。 你可以在這裡找到整個列表

Proxy物件只是在閱讀關於它們的這篇文章之後才進入我的視野,我已經可以在我每天寫的程式碼中看到它們的用處了!