2016-07-14 34 views
6

Tôi có một lớp thực hiện giao diện XMLHttpRequest. Tùy thuộc vào URL được chuyển đến open(), tôi có thể xác định xem có nên sử dụng XMLHttpRequest mặc định hay triển khai tùy chỉnh của tôi hay không. Ý tưởng của tôi là sử dụng một proxy để làm điều này:Có thể thay đổi mục tiêu của Proxy không?

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

tôi đã làm một số xét nghiệm bằng cách sử dụng ES6 Proxy, mà có vẻ đầy hứa hẹn, nhưng tiếc là mục tiêu proxy không thể được sửa đổi sau khi xây dựng Proxy:

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 :(

Tôi nghĩ rằng tôi có thể thực hiện những gì tôi muốn bằng cách không thiết lập mục tiêu - thay vào đó xác định tất cả các bẫy để ủy quyền cho đối tượng mong muốn - nhưng tôi hy vọng cho một giải pháp đơn giản hơn.

+0

Lưu ý rằng bạn đang cố gắng thay đổi đích đã nhận thuộc tính '.switchToBar', chứ không phải gọi phương thức' .switchToBar() ' – Bergi

+0

Vâng, nhưng tôi không nghĩ rằng nó quan trọng đối với ví dụ này. Và để sử dụng nó với XHR.open, tôi thực sự muốn thiết lập các đại biểu trước khi gọi thực hiện XHR.open. –

Trả lời

6

Dưới đây là một đi vào "xác định tất cả các bẫy để uỷ thác cho đối tượng mong muốn"

(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()}`); 

tôi cảm thấy như phải có một số lý do này không được hỗ trợ ra khỏi hộp, nhưng tôi không có đủ thông tin để bình luận thêm về điều đó.

Sau khi hack vào điều này một thời gian, tôi đã quan sát thấy một vài điều. Có vẻ như mục tiêu ban đầu mà nhà xây dựng proxy được gọi là được coi là một phần của danh tính của proxy bất kể. Đặt mục tiêu ban đầu thành một đối tượng đơn giản và hoán đổi mục tiêu đến một hàm sau đó sẽ gây ra lỗi khi proxy được gọi. Có chắc chắn một số cạnh thô này, vì vậy sử dụng cẩn thận.

+0

Oh rất thông minh! Ý tưởng tuyệt vời để sử dụng proxy cho trình xử lý, cho phép mục tiêu (và trình xử lý) được thay đổi, với mã rất nhỏ. Bạn nói đúng là có một số vấn đề với loại hack này. Tôi chạy vào một vài thử nghiệm của tôi (ít thanh lịch) thực hiện. Ví dụ, 'Object.preventExtensions (proxy);' sẽ ném một lỗi vì Proxy thực hiện một số xác thực bằng cách sử dụng đích mà nó được xây dựng với thay vì mục tiêu có thể thay đổi. Tôi nghĩ rằng không có người định cư cho các mục tiêu/xử lý là một sự giám sát - và ngay cả khi có lý do chính đáng, nó vẫn còn buồn. Điều này chủ yếu là làm việc ít nhất. Cảm ơn! –

2

Có thể thay đổi mục tiêu của Proxy không?

Không, điều này là không thể. Trình xử lý proxy là một giao diện khá chung chung, và bằng cách xác định tất cả các bẫy để chuyển tiếp thao tác đến một trình xử lý khác, điều này có thể dễ dàng đạt được. Đó là lý do tại sao không có phương pháp bổ sung để thay đổi mục tiêu, giao diện được giữ ở mức tối thiểu. Bằng cách không làm cho mục tiêu có thể thay đổi, cũng hình dạng của proxy được giữ nguyên (ví dụ: cho dù đó là có thể gọi hoặc một mảng).

1

Làm thế nào về điều này? Thay vì làm cho foo hoặc bar mục tiêu trực tiếp, chúng tôi sử dụng hộp cho mục tiêu của chúng tôi và đặt foo hoặc bar vào hộp.

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 

Trong trường hợp này, hộp của chúng tôi là đối tượng có thuộc tính content. Nhưng cách khác, bạn có thể sử dụng một mảng với một mục tại chỉ mục 0.

Các vấn đề liên quan