2017-04-05 65 views
1

我正在通過谷歌的Chrome版本57.0.2987.133以下腳本:爲什麼是代理在ES2015一個Map對象不工作

var loggingProxyHandler = { 
 
    "get" : function(targetObj, propName, receiverProxy) { 
 
     let ret = Reflect.get(targetObj, propName, receiverProxy); 
 
     console.log("get("+propName.toString()+"="+ret+")"); 
 
     return ret; 
 
    }, 
 

 
    "set" : function(targetObj, propName, propValue, receiverProxy) { 
 
     console.log("set("+propName.toString()+"="+propValue+")"); 
 
     return Reflect.set(targetObj, propName, propValue, receiverProxy); 
 
    } 
 
}; 
 

 
function onRunTest() 
 
{ 
 
    let m1 = new Map(); 
 
    let p1 = new Proxy(m1, loggingProxyHandler); 
 
    p1.set("a", "aval"); // Exception thrown from here 
 
} 
 

 
onRunTest();
NOTE: Requires a browser supporting ES2015's Proxy

運行時,我看到了處理器的get陷阱被稱爲返回地圖的設定功能 然後我收到以下錯誤:

"Uncaught TypeError: Method Map.prototype.set called on incompatible receiver [object Object]" 
at Proxy.set (native) 
... 

我試過removin g loggingProxyHandler中的陷阱函數(使其成爲空對象),但仍然收到相同的錯誤。

我的理解是,代理對象應該能夠爲所有原生ES5和ES2015 JavaScript對象生成。數組似乎在相同的代理處理程序下工作良好。 我誤解了規格嗎?
我的代碼丟失了什麼? Chrome中有沒有已知的錯誤? (我做了搜索,發現Chrome的無缺陷對這個問題)。

+0

的可能重複[?爲什麼我的代理,它包裝一個地圖的函數調用拋出類型錯誤](http://stackoverflow.com/questions/42381028/why-is-這聽起來像你實際上想要做的就是覆蓋(攔截)set和get調用,而不是路由所有的*屬性訪問*通過(通過代理包裝的一個映射函數調用拋出typeerror) – Bergi

+1

一個代理? – Bergi

+0

要說清楚:不要使用'Proxy'來攔截異常行爲,這超出了正常的'對象'語義。改爲使用子類。 – ftor

回答

4

你得到錯誤的原因是,代理未捲入了p1.set調用(比set陷阱其他  —無關,儘管名稱相同  —正在調用以檢索函數引用)。所以一旦檢索到函數引用,就會調用this設置爲代理,而不是Map   —其中Map不喜歡。

如果你真的試圖攔截在Map所有屬性訪問調用,您可以結合你從get返回任何函數引用修復它(見***線):

var loggingProxyHandler = { 
 
    "get" : function(targetObj, propName, receiverProxy) { 
 
     let ret = Reflect.get(targetObj, propName, receiverProxy); 
 
     console.log("get("+propName.toString()+"="+ret+")"); 
 
     if (typeof ret === "function") { // *** 
 
      ret = ret.bind(targetObj);  // *** 
 
     }        // *** 
 
     return ret; 
 
    }, 
 

 
    "set" : function(targetObj, propName, propValue, receiverProxy) { 
 
     console.log("set("+propName.toString()+"="+propValue+")"); 
 
     return Reflect.set(targetObj, propName, propValue, receiverProxy); 
 
    } 
 
}; 
 

 
function onRunTest() 
 
{ 
 
    let m1 = new Map(); 
 
    let p1 = new Proxy(m1, loggingProxyHandler); 
 
    p1.set("a", "aval"); 
 
    console.log(p1.get("a")); // "aval" 
 
} 
 

 
onRunTest();
NOTE: Requires a browser supporting ES2015's Proxy

但是,如果您的目標只是攔截Map#getMap#set,您根本不需要代理。或者:

  1. 創建一個Map子類並實例化它。儘管如此,假設您控制Map實例的創建。

  2. 創建一個繼承Map實例的新對象,並覆蓋getset;你不必控制原始Map的創作。

  3. 用您自己的版本替換Map實例上的setget方法。

這裏的#1:

class MyMap extends Map { 
 
    set(...args) { 
 
    console.log("set called"); 
 
    return super.set(...args); 
 
    } 
 
    get(...args) { 
 
    console.log("get called"); 
 
    return super.get(...args); 
 
    } 
 
} 
 

 
const m1 = new MyMap(); 
 
m1.set("a", "aval"); 
 
console.log(m1.get("a"));

#2:

const m1 = new Map(); 
 
const p1 = Object.create(m1, { 
 
    set: { 
 
    value: function(...args) { 
 
     console.log("set called"); 
 
     return m1.set(...args); 
 
    } 
 
    }, 
 
    get: { 
 
    value: function(...args) { 
 
     console.log("get called"); 
 
     return m1.get(...args); 
 
    } 
 
    } 
 
}); 
 

 
p1.set("a", "aval"); 
 
console.log(p1.get("a"));

#3:

const m1 = new Map(); 
 
const m1set = m1.set; // Yes, we know these are `Map.prototype.set` and 
 
const m1get = m1.get; // `get`, but in the generic case, we don't necessarily 
 
m1.set = function(...args) { 
 
    console.log("set called"); 
 
    return m1set.apply(m1, args); 
 
}; 
 
m1.get = function(...args) { 
 
    console.log("get called"); 
 
    return m1get.apply(m1, args); 
 
} 
 

 
m1.set("a", "aval"); 
 
console.log(m1.get("a"));

+0

它看起來像我需要去與一個子類機制,如果我想在這裏達到某種程度的AOP。但是,然後訪問Map的size屬性是有問題的,因爲它不是通過方法調用實現的。當我添加推薦的綁定時,它確實得到了異常,但是一旦函數通過get陷阱綁定到targetObj,set陷阱將不會被調用,從而有效地取消代理。我想如果我想追求代理,我可以嘗試生成一個全新的函數,綁定到targetObject,但調用代理處理程序陷阱(通​​過閉包)。 – Rand

+0

@Rand:是的。沒有理由設置陷阱會被調用,沒有任何設置屬性。我不明白爲什麼訪問'size'有問題:屬性是繼承的,只是訪問它。 –

+0

@ T.J.Crowder你說:「我不明白爲什麼訪問大小有問題:屬性是繼承的,只是訪問它。」我不明白爲什麼,但它是。在Chrome 59上,你的第一個例子工作,但如果我添加'console.log(p1.size)',它會給出'Uncaught TypeError:方法Map.prototype.size在不兼容的receiver [object Object]上調用'。有任何想法嗎?你的例子是我見過的最接近Map代理的工作。 –

相關問題