2016-07-14 11 views
6

मेरे पास एक कक्षा है जो XMLHttpRequest इंटरफ़ेस लागू करती है। open() पर यूआरएल के आधार पर, मैं निर्धारित कर सकता हूं कि डिफ़ॉल्ट XMLHttpRequest या मेरे कस्टम कार्यान्वयन का उपयोग करना है या नहीं। मेरा विचार प्रॉक्सी का उपयोग करने यह करने के लिए है:क्या प्रॉक्सी के लक्ष्य को बदलना संभव है?

let xhr = new XHRProxy(); 
xhr.open('GET', 'http://blah'); // Decide here depending on URL 

मैं कुछ ES6 प्रॉक्सी, जो होनहार लगता है का उपयोग कर परीक्षण है, लेकिन दुर्भाग्य से किया प्रॉक्सी लक्ष्य प्रॉक्सी के निर्माण के बाद बदला नहीं जा सकता:

var foo = { 
    name() { 
     return "foo"; 
    } 
}; 
var bar = { 
    name() { 
     return "bar"; 
    } 
} 
var handler = { 
    get(target, property, receiver) { 
     if (property === "switchToBar") { 
      // FIXME: This doesn't work because a Proxy's target is not exposed AFAIK 
      receiver.target = bar; 
      return function() {}; 
     } else { 
      return target[property]; 
     } 
    } 
} 
var proxy = new Proxy(foo, handler); 
console.log(proxy.name()); // foo 
proxy.switchToBar(); 
console.log(proxy.name()); // foo :(

मुझे लगता है कि मैं लक्ष्य को सेट न करके मैं जो चाहता हूं उसे पूरा कर सकता हूं - वांछित ऑब्जेक्ट में प्रतिनिधि के लिए सभी जाल को परिभाषित करने के बजाय - लेकिन मैं एक आसान समाधान की उम्मीद कर रहा हूं।

+0

ध्यान दें कि आप '.switchToBar' प्रॉपर्टी' प्राप्त करने पर पहले से ही लक्ष्य को बदलने की कोशिश कर रहे हैं, 'switchToBar()' विधि को कॉल करने पर नहीं – Bergi

+0

हाँ, लेकिन मुझे नहीं लगता कि यह इस उदाहरण के लिए महत्वपूर्ण है। और XHR.open के साथ इसका उपयोग करने के लिए, मैं वास्तव में XHR.open कार्यान्वयन को कॉल करने से पहले प्रतिनिधि को सेट करना चाहता हूं। –

उत्तर

6

यहाँ पर "सभी जाल को परिभाषित करने के लिए वांछित वस्तु को सौंपने के लिए"

(function() { 
    let mutableTarget; 
    let mutableHandler; 

    function setTarget(target) { 
    if (!(target instanceof Object)) { 
     throw new Error(`Target "${target}" is not an object`); 
    } 
    mutableTarget = target; 
    } 

    function setHandler(handler) { 
    Object.keys(handler).forEach(key => { 
     const value = handler[key]; 

     if (typeof value !== 'function') { 
     throw new Error(`Trap "${key}: ${value}" is not a function`); 
     } 

     if (!Reflect[key]) { 
     throw new Error(`Trap "${key}: ${value}" is not a valid trap`); 
     } 
    }); 
    mutableHandler = handler; 
    } 

    function mutableProxyFactory() { 
    setTarget(() => {}); 
    setHandler(Reflect); 

    // Dynamically forward all the traps to the associated methods on the mutable handler 
    const handler = new Proxy({}, { 
     get(target, property) { 
     return (...args) => mutableHandler[property].apply(null, [mutableTarget, ...args.slice(1)]); 
     } 
    }); 

    return { 
     setTarget, 
     setHandler, 
     getTarget() { 
     return mutableTarget; 
     }, 
     getHandler() { 
     return mutableHandler; 
     }, 
     proxy: new Proxy(mutableTarget, handler) 
    }; 
    } 

    window.mutableProxyFactory = mutableProxyFactory; 
})(); 

const { 
    proxy, 
    setTarget 
} = mutableProxyFactory(); 

setTarget(() => 0); 
console.log(`returns: ${proxy()}`); 

setTarget({ val: 1 }); 
console.log(`val is: ${proxy.val}`); 

setTarget({ val: 2 }); 
console.log(`val is: ${proxy.val}`); 

setTarget(() => 3); 
console.log(`returns: ${proxy()}`); 

मैं की तरह वहाँ किसी कारण इस बॉक्स से बाहर समर्थित नहीं है होना चाहिए लग रहा है एक जाना है, लेकिन मैं पर्याप्त नहीं है उस पर टिप्पणी करने के लिए जानकारी।

थोड़ी देर के लिए इस पर हैकिंग करने के बाद, मैंने कुछ चीजें देखी हैं। ऐसा लगता है कि प्रॉक्सी कन्स्ट्रक्टर के साथ कहा जाने वाला मूल लक्ष्य प्रॉक्सी की पहचान के हिस्से के रूप में माना जाता है। मूल लक्ष्य को एक सादे ऑब्जेक्ट पर सेट करना और फ़ंक्शन को लक्ष्य को स्वैप करना बाद में प्रॉक्सी कहलाता है, जब प्रॉक्सी कहा जाता है। निश्चित रूप से इसके लिए कुछ मोटे किनारे हैं, इसलिए सावधानी के साथ उपयोग करें।

+0

ओह बहुत चालाक! हैंडलर के लिए प्रॉक्सी का उपयोग करने का अच्छा विचार, जो बहुत ही कम कोड के साथ लक्ष्य (और हैंडलर) को बदलने की अनुमति देता है। आप सही हैं कि इस तरह के हैक के साथ कुछ मुद्दे हैं। मैं अपने (कम सुरुचिपूर्ण) कार्यान्वयन के कुछ परीक्षण में भाग गया। उदाहरण के लिए, 'Object.preventExtensions (प्रॉक्सी);' एक त्रुटि फेंक देगा क्योंकि प्रॉक्सी म्यूटेबल लक्ष्य के बजाय बनाए गए लक्ष्य का उपयोग करके कुछ सत्यापन करता है। मुझे लगता है कि लक्ष्य/हैंडलर के लिए सेटर्स नहीं हैं एक निरीक्षण - और यहां तक ​​कि यदि अच्छे कारण हैं, तो यह अभी भी दुखी है। यह ज्यादातर कम से कम काम करता है। धन्यवाद! –

2

क्या प्रॉक्सी के लक्ष्य को बदलना संभव है?

नहीं, यह संभव नहीं है। प्रॉक्सी हैंडलर पहले से ही एक सामान्य जेनेरिक इंटरफेस है, और ऑपरेशन को एक अलग हैंडलर को आगे बढ़ाने के लिए सभी जाल को परिभाषित करके यह आसानी से प्राप्त किया जा सकता है। यही कारण है कि लक्ष्य को बदलने के लिए कोई अतिरिक्त विधि नहीं है, इंटरफेस को न्यूनतम रखा जाता है। लक्ष्य को बदलने योग्य नहीं बनाकर, प्रॉक्सी का आकार संरक्षित है (उदा। चाहे वह कॉल करने योग्य या सरणी हो)।

1

इस बारे में कैसे? foo या bar सीधे लक्ष्य बनाने के बजाय, हम अपने लक्ष्य के लिए एक बॉक्स का उपयोग करते हैं, और बॉक्स में foo या bar डालते हैं।

var foo = { 
    name() { 
     return "foo"; 
    } 
}; 
var bar = { 
    name() { 
     return "bar"; 
    } 
}; 
var handler = { 
    get(target, property, receiver) { 
     if (property === "switchToBar") { 
      target.content = bar; 
      return function() {}; 
     } else { 
      return target.content[property]; 
     } 
    } 
}; 

var box = {content: foo}; 
var proxy = new Proxy(box, handler); 

console.log(proxy.name()); // foo 
// Switch over to bar by calling the function 
proxy.switchToBar(); 
// Or, we could do the switch from out here 
box.content = bar; 
// Either way, we get the same result 
console.log(proxy.name()); // bar 

इस मामले में, हमारा बॉक्स संपत्ति content संपत्ति वाला एक ऑब्जेक्ट है। लेकिन वैकल्पिक रूप से, आप इंडेक्स 0 पर किसी आइटम के साथ एक सरणी का उपयोग कर सकते हैं।

संबंधित मुद्दे