淺談ES6中的Proxy

1.什麼是Proxy

MDN中的描述javascript

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).
Proxy 對象通常用於給基本操做定義自定義行爲(例如:屬性查詢,賦值,枚舉,函數調用等)java

Proxy是ES6中原生提供的一個構造函數,Proxy字面意思是「代理」,其實它更像一種攔截器,在訪問,賦值等基本操做時會先到咱們定義好的攔截方法中,根據訪問的信息去執行咱們想要的操做。chrome


2.Proxy的用法

Proxy構造函數中接受兩個參數數組

new Proxy(target, handler);
複製代碼
  • target 參數指的是目標對象
  • handler 指用戶自定義的行爲對象

來看一個使用例子🌰:app

var handler = {
        get (target, propkey, receiver) {
            console.log('getting values');
            return target[propkey] || 'value is not defined';
        },
        set (target, propkey, value, receiver) {
            console.log('setting values');
            return target[propkey] = value;
        }
    }
    var proxy = new Proxy({}, handler);
    
    console.log(proxy.a);
    // 輸出
    // getting values
    // value is not defined
    
    proxy.a = 111;
    // 輸出
    // setting values
    
    console.log(proxy.a)
    // 輸出
    // getting values
    // 111
複製代碼

上面代碼定義了一個擁有 get 和 set 的代理,當咱們在訪問proxy對象中的a時,會進入handler中的 get 方法並執行。一樣,當咱們給proxy賦值時,亦會進入handler中的set方法中。函數

  • proxy中的的get和set方法和Object.defineProperty的descriptor的get和set方法很像,但proxy中的get和set更爲強大,不只能夠監聽數組下標的變化,還能夠監聽到對象原型屬性的變化
/** * proxy監聽數組下標 */
    var proxyArr = new Proxy([], {
        get(target, propkey) {
            console.log('數組下標被監聽:get');
            return target[propkey];
        },
        set(target, propkey, value) {
            console.log('數組下標被監聽:set');
            return target[propkey] = value;
        }
    })
    console.log(proxyArr[0]);
    // 數組下標被監聽:get
    // undefined
    
    proxyArr[0] = 1;
    // 數組下標被監聽:set

    /** * proxy監聽對象原型 */
    var obj = {a:1};
    var prototypeObj = Object.create(obj);
    var proxyPrototype = new Proxy(prototypeObj, {
        get(target, propkey) {
            console.log('對象原型被監聽:get');
            return target[propkey];
        },
        set(target, propkey, value) {
            console.log('對象原型被監聽:set');
            return target[propkey] = value;
        }
    })
    console.log(proxyPrototype.a);
    // 對象原型被監聽:get
    // 1
    
    proxyPrototype.a = 2;
    // 對象原型被監聽:set
複製代碼
  • handler中的對象屬性
    Proxy不只能夠用於監聽數據變化,還能夠監聽調用函數,構造函數實例化等操做
    handler對象具體的參數有13個:
    • get(target, propkey, receiver)
    • set(target, propkey, receiver)
    • has(target, propkey)
    • deleteProperty(target, propkey)
    • ownkeys(target)
    • getOwnPropertyDescriptor(target, propKey)
    • defineProperty(target, propkey, propDesc)
    • preventExtensions(target)
    • getPrototypeOf(target)
    • isExtensible(target)
    • setPrototypeOf(target, proto)
    • apply(target, object, args) // 調用函數前觸發
    • construct(target, args) // 構造函數實例化前觸發

3.Proxy與defineProperty比較

  1. 上文說到,defineProperty不能監聽數組下標變化和對象原型的變化,Proxy則能夠支持。post

  2. defineProperty監聽的是一個對象的屬性,proxy監聽的是整個對象。測試

  3. defineProperty比較proxy的速度更快,咱們寫兩個測試的用例比較一下二者的速度ui

/** * defineProperty測試用例 */
    var defineObj = {};
    console.time('defineProperty');
    for (var x = 0; x < 100000; x++) {
        Object.defineProperty(defineObj, 'test_' + x, {
            get() {
                return value;
            },
            set(value) {
                return defineObj['test_' + x] = value;
            }
        });
    }
    console.timeEnd('defineProperty');
    
    /** * proxy測試用例 */
    var proxy = new Proxy({}, {
        get(target, propkey) {
            return target[propkey];
        },

        set(target, propkey, value) {
            return target[propkey] = value
        }
    });
    console.time('proxy');
    for (var x = 0; x < 100000; x++) {
        proxy['test_' + x] = 1;
    }
    console.timeEnd('proxy');
複製代碼
  • 在chrome中運行proxy的速度 spa

  • 運行defineProperty的速度

  1. 不只是運行速度的快了,proxydefineProperty佔用的內存更少,咱們用上面的例子改造測試一下二者的內存佔用
/** * proxy 內存佔用測試 */
    var i = 0;
    var proxy = new Proxy({}, {
        get(target, propkey) {
            return target[propkey];
        },

        set(target, propkey, value) {
            return target[propkey] = value
        }
    });
    console.log('start');
    var timer = null;
    timer = setInterval(function(){
        if (i > 10) {
            console.log('finish');
            return clearTimeout(timer);
        }
        i++;
        for (var x = 0; x < 10000; x++) {
            proxy['test_' + i + '_' + x] = 1;
        }
    }, 1000)
    
    
    /** * defineProperty 內存佔用測試 */
    var i = 0;
    var defineObj = {};
    console.log('start');
    timer = setInterval(function(){
        if (i > 10) {
            console.log('finish');
            return clearTimeout(timer);
        }
        for (var x = 0; x < 10000; x++) {
            Object.defineProperty(defineObj, 'test_' + i + '_' + x,{
                get() {
                    return value;
                },
                set(value) {
                    return defineObj['test_' + i + '_' + x] = value;
                }
            });
        }
        i++;
    }, 1000)
複製代碼
  • chrome 運行defineProperty的測試用例

  • 運行proxy的測試用例

4.最後


本節咱們瞭解了Proxy中的做用和與defineProperty相比,Proxy的優點。
下一節就來實踐一下,使用proxy實現一個雙向綁定

若有不嚴謹或者錯誤的地方,望大佬們指出~

相關文章
相關標籤/搜索