2017-08-01 20 views
6

Tôi muốn thiết lập một số Proxy cảnh báo tôi khi một thuộc tính mới được xác định trên đối tượng window. (Trên thực tế tôi muốn nắm bắt tất cả các khai báo biến toàn cầu)Proxy trên cửa sổ

let handler = { 
    defineProperty(target, key, descriptor) { 
     console.log('hey', key); 
     return false; 
    } 
}; 
window = new Proxy(window, handler); 
window.foo = 'bar'; 
// nothing happens 

Đoạn mã trên làm việc cho bất kỳ đối tượng nhưng cửa sổ:

let handler = { 
    defineProperty(target, key, descriptor) { 
     console.log('hey', key); 
     return false; 
    } 
}; 
let target = {}; 
target = new Proxy(target, handler); 
target.foo = 'bar'; 
// console: "hey bar" 

Có cách nào để thiết lập một Proxy trên window đối tượng và nếu không thể, có giải pháp khôn lanh nào để đạt được cùng một mục tiêu không?

+3

Không, vì bạn không thể thay thế cửa sổ với proxy của bạn. – Bergi

+2

Mã của bạn chỉ nên đăng nhập vào 'proxy.foo = 'bar'', không phải cho' target.foo =' bar''. Những gì bạn đăng thực sự có hiệu quả không? – Bergi

+2

có thể trùng lặp của [Làm thế nào tôi có thể phát hiện khi một biến toàn cầu được thiết lập trong javascript?] (Https://stackoverflow.com/q/38759116/1048572) – Bergi

Trả lời

1

Câu trả lời ngắn gọn là không. Bạn không thể sử dụng proxy cho việc này. Nó luôn luôn là tốt hơn để sửa đổi và refactor ứng dụng của bạn để thoát khỏi sự cần thiết phải làm như vậy shenanigans. Nhưng tôi biết rằng đôi khi chúng ta không có thời gian để làm những điều đúng đắn. Althrough Tôi không khuyên bạn làm điều này, bạn vẫn có thể nhận được một thay đổi trên đối tượng cửa sổ.

Bạn có một số tùy chọn để thực hiện việc này. Nếu bạn biết danh sách các vars bạn đang tìm kiếm, bạn có thể sử dụng một cái gì đó như Watch.JS Về cơ bản nó có thể theo dõi tất cả các thay đổi, nhưng tôi đã không thể làm cho nó hoạt động đáng tin cậy, vì vậy tốt hơn là chỉ định một danh sách

watch(window, ['list', 'of', 'vars'], (prop, action, newVal, oldVal) => { 
    console.log('Property changed', prop, action, newVal, oldVal); 
}, 1); 

là một thay thế, bạn có thể tạo một trình kiểm tra bẩn đơn giản

let props = Object.keys(window); 
const check =() => { 
    const currentProps = Object.keys(window); 
    const newProps = currentProps.filter(item => props.indexOf(item) === -1); 
    if (newProps.length) { 
     console.log('Added these properties', newProps); 
     props = currentProps; 
    } 
    requestAnimationFrame(check); 
}; 
requestAnimationFrame(check); 

Nhưng trong trường hợp bạn quyết định đi với một trong hai giải pháp, bạn phải chắc chắn rằng tất cả các kiểm tra sẽ dừng lại khi cần thiết để tránh rò rỉ bộ nhớ hoặc tiêu thụ CPU. Mã kiểm tra này không tiêu thụ quá nhiều nhưng có thể theo lý thuyết. Vì vậy, bạn phải theo dõi nó. On dữ liệu hồ sơ trang trống trông như thế này profile data

Và hãy nhớ sử dụng unwatch trong trường hợp Watch.JS hoặc để thêm một điều kiện để ngăn chặn sự kiểm tra trong trường hợp bạn sử dụng giải pháp thứ hai khi họ sẽ hoàn thành công việc

+0

Chú ý rằng các đạo cụ không đếm được không được theo dõi với Object.keys. Hiệu suất sẽ tồi tệ hơn đáng kể khi các đạo cụ không thể đếm được cũng được theo dõi. – estus

+0

@estus Giải pháp này là đủ imo và tôi đã không sử dụng 'getOwnPropertyNames' để giảm kích thước bộ sưu tập để lọc. Vì tác giả muốn chỉ nhận các khai báo biến toàn cầu và không xem trên tất cả các thuộc tính cửa sổ. –

+0

thuộc tính cửa sổ có thể được khai báo với Object.defineProperty, quá. Nhiệm vụ trở nên đơn giản hơn nhiều khi nó được biết trước các biến này là gì. – estus

0

Bạn thực sự không cố kích hoạt proxy cửa sổ. Bạn cần phải làm:

let proxy = new Proxy(window, handler); 
proxy.foo = 'bar'; 

Và không có, bạn không thể làm

window = new Proxy(window, handler); 

như cửa sổ là unreplaceable.

3

Proxy chậm và không nên được sử dụng ở những nơi quan trọng về hiệu suất, trong khi window ảnh hưởng đến toàn bộ ứng dụng và chắc chắn có thể được coi là hiệu suất quan trọng.

Nhưng thuộc tính windowread-only, tức là không thể định cấu hình và không có người truy cập được đặt, không thể thay thế bằng proxy.

Các thay thế cho Proxy có thể do thám window thay đổi này là watch phương pháp Firefox cụ thể, nó có thể được sử dụng trong kịch bản chạy trong Firefox (ví dụ mở rộng) nhưng không phải bất cứ nơi nào khác. Cụ thể, Object.observe cũng không thể quan sát được window theo thiết kế, cũng được xóa khỏi Chrome và các trình duyệt V8 khác.

chung này có thể đạt được bằng cách bỏ phiếu window tính:

let oldProps; 

setInterval(() => { 
    console.time('Polling window'); 
    let newProps = Object.getOwnPropertyNames(window); 

    if (oldProps) { 
    let addedProps = newProps.filter(prop => oldProps.indexOf(prop) < 0); 
    console.log('Added props', addedProps); 
    } 
    oldProps = newProps; 
    console.timeEnd('Polling window'); 
}, 500); 

Nếu mã này là nghĩa vụ phải được sử dụng trong sản xuất, nó phải được tối ưu hóa, vì filter là tương đối chậm, và indexOf đi qua toàn bộ mảng trên mỗi lần lặp lại, điều này dẫn đến mã rất không hiệu quả.

Raw for hoặc while vòng lặp là con đường để đi:

let oldProps; 

setInterval(() => { 
    console.time('Polling window'); 
    let newProps = Object.getOwnPropertyNames(window).sort(); 

    if (oldProps) { 
    for (let oldI = 0, newI = 0; oldI < oldProps.length || newI < newProps.length; oldI++, newI++) { 
     let oldProp = oldProps[oldI]; 
     let newProp = newProps[newI]; 

     if (newProp > oldProp || newProp === undefined) { 
     newI--; 
     console.log('Removed prop', oldProp); 
     } else if (newProp < oldProp || oldProp === undefined) { 
     oldI--; 
     console.log('Added prop', newProp); 
     } 
    } 
    } 
    oldProps = newProps; 
    console.timeEnd('Polling window'); 
}, 500);