ES6之Proxy的get方法詳解
Proxy是在ES2015(ES6)中新新增內建物件,用於自定義一些基本操作。
這篇文章是我在學習Proxy的時候對於get方法的一些心得。
作為ES2015新定義的內建物件,Proxy 能夠攔截並且自定義物件以及函式的一些基本操作,具有很高的優先順序和便利性,能夠讓我們在寫程式碼的時候多出一種解決難題的途徑。
Proxy的get方法用於攔截物件屬性的讀取操作,例如 obj.key 和 obj.[key]。在給Proxy的handler引數中設定get方法後,每當進行讀取操作時,會優先呼叫該get方法,我們可以在這個方法函式中對讀取行為進行攔截。請看下面的程式碼:
const obj = { key: 1 } const proxy = new Proxy(obj, { get: function(target, property, receiver) { console.log('get', property) return target[property] } }) console.log(proxy.key) // get key // 1
get方法的引數一共有三個:target是例項化Proxy時使用的物件,在這個例子中是obj;而property是這次讀取操作中想要獲取的屬性名,在這個例子中是key;最後一個引數receiver則是這個例項化的Proxy自身,即proxy。
在這個例子中,我在get方法的最後返回了target[property],這是為了能夠讓讀取操作能夠進行下去。由於Proxy的get方法是最先被呼叫的,所以這裡返回的內容就是我們讀取操作能夠獲得的結果;如果我們在這裡不返回任何值,那麼就會得到undefined。
receiver和死迴圈
要注意的是,千萬不要在get方法中讀取receiver的屬性,因為receiver實質上就是proxy自身,所以receiver.key這句程式碼就等同於proxy.key,會重新呼叫get方法導致死迴圈。
const obj = { key: 1 } const proxy = new Proxy(obj, { get: function(target, property, receiver) { console.log(receiver.key) return target[property] } }) console.log(proxy.key) // 死迴圈!
原型鏈上的getter
有時候,我們會在物件之中使用getter和setter來定製屬性的賦值和讀取。在這時,如果proxy的get方法內部有使用到target[property]的話,target[property]的值會受到目標物件的getter的影響。因此呼叫get方法的時候請注意在目標物件中是否有用到getter。
const obj = { get key() { return 'string' }, set key(value) { console.log(`key is ${value}, it is a ${typeof value}`) } } const proxy = new Proxy(obj, { get: function(target, property, receiver) { if(typeof target[property] !== 'string') { return target[property] } else { throw new TypeError(`The type of ${property} is String!`) } } }) proxy.key = 100 console.log(proxy, obj) // key is 100, it is a number // The type of key is String!
在上方的例子中,如果訪問obj的非數字型別的屬性,就會丟擲一個錯誤,但是由於obj中getter的原因,無論我給key屬性賦什麼值,在訪問key屬性的時候肯定會丟擲錯誤。
如果僅僅要注意目標物件中的getter還算容易的,但是如果目標物件繼承自其他物件,那麼事情就變得有些麻煩了,請看下面的例子:
const parentObj = { get key() { return 'string' }, set key(value) { console.log(`key is ${value}, it is a ${typeof value}`) } } const obj = Object.create(parentObj) const proxy = new Proxy(obj, { get: function (target, property, receiver) { if (typeof target[property] !== 'string') { return target[property] } else { throw new TypeError(`The type of ${property} is String!`) } } }) proxy.key = 100 console.log(proxy.key) // key is 100, it is a number // The type of key is String!
如程式碼所示,目標物件obj繼承自parentObj,而parentObj中使用了getter方法,那麼使用proxy的get方法仍舊會報錯。實際運用中原型鏈可能會很長,getter可能會存在於原型鏈的任何一個地方中,所以在使用Proxy的get方法時請一定要注意。
但是如果把parentObj上的key遮蔽掉,就不會發生丟擲錯誤的情況了。比如在建立obj的時候申明的key,程式碼如下:
const parentObj = { get key() { return 'string' }, set key(value) { console.log(`key is ${value}, it is a ${typeof value}`) } } const obj = Object.create(parentObj, { key: { value: null, writable: true } }) const proxy = new Proxy(obj, { get: function (target, property, receiver) { if (typeof target[property] !== 'string') { return target[property] } else { throw new TypeError(`The type of ${property} is String!`) } } }) proxy.key = 100 console.log(proxy.key) // 100
同樣的,我們也可以使用Object.defineProperty()和Object.assign()這兩個方法來達到相同的目的:
Object.defineProperty(obj, 'key', { value: null, writable: true }) obj = Object.assign({}, obj, { key: null })
但是要注意使用Object.assign()的時候不能這麼些:
obj = Object.assign(obj, { key: null })
這樣寫法無法遮蔽掉parentObj上的key屬性,使用的時候仍舊會丟擲錯誤。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援碼農教程。