原文:http://ariya.ofilabs.com/2013/07/es6-and-proxy.html
能夠攔截在一個物件上的指定操作的能力是非常有用的,尤其是在故障除錯的時候.通過ECMAScript 6中的新特性——代理(proxy),這種能力才最終得以實現.
在目前最新的ES6規範草案中(2013年5月14日釋出,第15次修訂版),第15.18小節——代理物件(Proxy Objects)這部分的文件仍然是空的.不過隨著規範的日趨穩定,這裡會補充上完整的參考文件的.目前,可以在ES wiki上的直接代理(Direct Proxies)頁面內找到最詳細的資料.在我寫這篇文章的時候,只有Firefox 18+實現了最新的代理規範(Chrome實現了一箇舊版的已經被廢棄的代理規範——Proxy.create).
演示代理特性最好的方式就是通過下面這個簡單的例子:
var engineer = { name: 'Joe Sixpack', salary: 50 }; var interceptor = { set: function (receiver, property, value) { console.log(property, 'is changed to', value); receiver[property] = value; } }; engineer = Proxy(engineer, interceptor);
在上面的程式碼中,我們首先建立了一個簡單的物件engineer和另外一個物件interceptor.然後,
engineer物件被另外一個由Proxy()函式構建的代理物件所代替
.傳入Proxy()的第二個引數interceptor是一個處理器(handler)物件.一個處理器物件可以包含有多種處理方法,在這個例子中只有一種處理方法,就是set.set處理方法能夠攔截到那些在代理物件身上進行的所有的屬性賦值操作
.
讓我們看看執行下面的這句賦值語句時會發生什麼:
engineer.salary = 60;
這時,先前設定好set處理方法將會被呼叫
.因此,會輸出這樣一條資訊:
salary is changed to 60
每當代理物件engineer上的一個屬性被賦值時,處理器物件
interceptor都會得到通知.只要提前設定好對應的處理方法(set),我們就能夠攔截到這一操作,實現自己想要的功能.當然,不只是屬性的賦值操作,代理物件身上的其他操作,比如屬性的讀取,屬性的刪除,屬性的遍歷等操作也都可以使用set之外的其他處理方法來進行攔截.除了除錯用途,代理物件還可以用到那些需要資料繫結(data binding)功能的框架中.我們可以把資料模型(data model)設定成為一個代理物件,監控它的屬性的值的變化.也就不需要再使用其他的語法(比如必須使用顯示的set方法來更改模型的值
)或者持續追蹤模型的值的變化(比如髒狀態檢查(dirty state checking))了.
你覺的代理還有什麼用呢?
譯者注:關於除錯用途,我講一個我自己的真實案例,情節稍有簡化.就在前兩天,公司的專案中出現一個bug,需要我來解決.我分析出是一個物件的某個屬性在執行途中被誤刪除導致,由於用的是模組化開發,這個物件又被多個模組中的程式碼處理過,我很難找到這個delete語句到底在哪個檔案中.雖然可以在本頁面內載入的十幾個js檔案中搜尋delete然後一一排除,但我決定試一下更高階的除錯方法.
我在這個物件剛剛生成的程式碼位置處的下一行寫下一句:
obj.watch("prop", function(){console.log(arguments.callee.caller)})
我使用了Firefox特有的watch方法來找出到底哪個函式刪掉了obj物件的prop屬性,但情況出乎我意料,重新整理頁面後沒有任何東西輸出.於是我查一查MDN,原來watch只能監測到屬性的變更,而不會監測到屬性的刪除.關鍵是這個MDN頁面我去年翻譯過啊,但我當時沒注意到這個細節.
於是我又使出Firefox特有的第二個特性,Proxy.啪啪啪打出這樣一句:
obj = Proxy(obj, {deleteProperty : function(){console.log(arguments.callee.caller)}})
就這樣成功找到了那個想要找的函式,這應該就是作者一開始所說的故障除錯的用途了.