ES6新特性:Proxy代理器


  ES6新特性:Proxy; 要使用的話, 直接在瀏覽器中執行即可, nodebabel目前還沒有Proxypolyfill;,要使用的話,直接在瀏覽器中運行就好了, 瀏覽器的兼容性為:chrome>49或者firefox>18;

  Proxy的基本使用:

  Proxy如其名, 它的作用是在對象和和對象的屬性值之間設置一個代理,獲取該對象的值或者設置該對象的值, 以及實例化等等多種操作, 都會被攔截住, 經過這一層我們可以統一處理,我們可以認為它就是“代理器” ;

  Proxy是一個構造函數, 使用new Proxy創建代理器, 第一個參數為一個對象, 第二個參數也為一個對象, 返回被包裹后的代理器, 我們使用基本的getset寫一個demo:

<script>
var obj = new Proxy({}, {
    get : function( target , prop ) {
        console.log("我要獲取值了");
        return target[prop];
    },
    set : function( target, prop, value) {
        console.log("我要設置值了");
        target[prop] = value;
    }
});
obj.vvvv = 1; 
obj.vvvv;
</script>

  如果實例化的時候不給第二個參數設置get和set, 相當於沒有這個代理器:

var obj = new Proxy({},{});
obj.vvvv = 1;
console.log( obj.vvvv );

  如果給一個對象設置兩個代理器或者更多的話, 所有的代理器都會生效:

<script>
var obj = new Proxy({}, {
    get : function( target , prop ) {
        console.log("我要獲取值了");
        return target[prop];
    },
    set : function( target, prop, value) {
        console.log("我要設置值了");
        target[prop] = value;
        }
});
obj = new Proxy(obj, {
    get : function( target , prop ) {
        console.log("我要再獲取值了");
        return target[prop];
    },
    set : function( target, prop, value) {
        console.log("我要再設置值了");
        target[prop] = value;
    }
});
obj.vvvv = 1;
obj.vvvv;
</script>

  通過代理器, 能夠對用戶設置的值進行驗證,  只有驗證通過了才設置到對象上;

let validator = {
    set: function(obj, prop, value) {
        if (prop === 'age') {
            if (!Number.isInteger(value)) {
                throw new TypeError('The age is not an integer');
            }
            if (value > 200) {
                throw new RangeError('The age seems invalid');
            }
        };
        obj[prop] = value;
    }
};

let person = new Proxy({}, validator);

person.age = 100;
console.log(person.age); // 100
person.age = 'young'; // 拋異常
person.age = 300; // 值太大了,也拋異常

  代理器Proxy有點像Object的geter和seter,  可以認為是geter和seter的擴展:

    <script>
        let obj = {
            get hehe() {
                console.log("獲取值哦");
                return obj.val;
            },
            set hehe(value) {
                return this.val = value;
            }
        };
        obj.hehe = 1;
        console.log(obj.val);
        console.log(obj.hehe);
    </script>

  Proxy的第二個參數 

  Proxy的第二個參數為一個對象, 對象的參數為以下的列表, Proxy提供了更多的接口 , 通過不同的參數, 我們可以截獲代碼的運行並重新處理, 顧名思義, 代理嘛:

  handler.getPrototypeOf()
  handler.setPrototypeOf()
  handler.isExtensible()
  handler.preventExtensions()
  handler.getOwnPropertyDescriptor()
  handler.defineProperty()
  handler.has()
  handler.get()
  handler.set()
  handler.deleteProperty()
  handler.ownKeys()
  handler.apply()
  handler.construct()

  handler.getPrototypeOf(),

  getPrototypeOf方法必須返回一個對象, 否則會報錯:

var obj = {};
var proto = {};
var handler = {
    getPrototypeOf(target) {
        console.log(target === obj);   // true
        console.log(this === handler); // true
        return proto;
    }
};

var p = new Proxy(obj, handler);
console.log(Object.getPrototypeOf(p) === proto);    // true

  5種觸發getPrototypeOf的方法, 包攬了所有了所有獲取原型的方法:

var obj = {};
var p = new Proxy(obj, {
    getPrototypeOf(target) {
        return Array.prototype;
    }
});
console.log(
    Object.getPrototypeOf(p) === Array.prototype,  // true
    Reflect.getPrototypeOf(p) === Array.prototype, // true
    p.__proto__ === Array.prototype,               // true
    Array.prototype.isPrototypeOf(p),              // true
    p instanceof Array                             // true
);

 

  handler.setPrototypeOf()

  當對象被設置原型的時候會執行我們設定的代碼:

let handler = {
    setPrototypeOf : function(target, value) {
        console.log("setPrototypeOf");
        target.__proto__ = value;
        target.hehe = "1111";
        return target;
    }
};
let proxy = new Proxy( {}, handler );
proxy.__proto__ = Object.prototype
console.log(proxy);
輸出:setPrototypeOf
又輸出: {hehe: "1111"}

 

  handler.constructor();

  當對象被new的時候會執行handler.constructor方法, 和上面不同的是第一個參數應該為一個函數:

<script>
var p = new Proxy(function() {}, {
    construct: function(target, argumentsList, newTarget) {
        console.log("called: " + argumentsList.join(", "));
        return { value: argumentsList[0] * 10 };
    }
});

console.log(new p(1)); 
//輸出:called: 1
//輸出:Object {value: 10}
</script>

 

 

  Proxy的方法:

    Proxy.revocable()返回一個可以取消的Proxy代理, 當實例化完畢后,在 執行 Proxy實例對象.revoke();   那么這個proxy實例相當於被內存回收, 不存在一樣;

var revocable = Proxy.revocable({}, {
    get: function(target, name) {
        return "[[" + name + "]]";
    }
});
var proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"
revocable.revoke();
console.log(proxy.foo); // 拋出異常
proxy.foo = 1           // 拋出異常
delete proxy.foo;       // 拋出異常
typeof proxy            // "object", 但是它還是一個對象....

 

  實際應用:

  通過constructapply兩個變量, 可以實現一個:繼承構造函數的工具函數extend:

function extend(sup,base) {
    ///獲取base方法的constructor
    var descriptor = Object.getOwnPropertyDescriptor(
        base.prototype,"constructor"
    );
    //重寫base方法的constructor,指向繼承超類的對象
    base.prototype = Object.create(sup.prototype);
    //利用構造器的代理器, 當用戶new這個函數的返回對象時候, 會生成一個繼承超類和base類的對象
    var handler = {
        construct: function(target, args) {
            var obj = Object.create(base.prototype);
            this.apply(target,obj,args);
            return obj;
        },
        apply: function(target, that, args) {
            sup.apply(that,args);
            base.apply(that,args);
        }
    };
    var proxy = new Proxy(base,handler);
    descriptor.value = proxy;
    //修復constructor方法
    Object.defineProperty(base.prototype, "constructor", descriptor);
    return proxy;
}

var Person = function(name){
    this.name = name;
};

var Boy = extend(Person, function(name, age) {
    this.age = age;
});

Boy.prototype.sex = "M";

var Peter = new Boy("Peter", 13);
console.log(Peter.sex);  // "M"
console.log(Peter.name); // "Peter"
console.log(Peter.age);  // 13

   給一個對象綁定一個set,當對象的selected元素發生改變, 那么就改變dom節點的屬性:

<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <div id="item-1">
        item-1
    </div>
    <div id="item-2">
        item-2
    </div>

    <script>let view = new Proxy({
                selected: null
            },
            {
                set: function(obj, prop, newval) {
                    let oldval = obj[prop];

                    if (prop === 'selected') {
                        if (oldval) {
                            oldval.setAttribute('aria-selected', 'false');
                        }
                        if (newval) {
                            newval.setAttribute('aria-selected', 'true');
                        }
                    }

                    // The default behavior to store the value
                    obj[prop] = newval;
                }
            });

    let i1 = view.selected = document.getElementById('item-1');
    console.log(i1.getAttribute('aria-selected')); // 'true'

    let i2 = view.selected = document.getElementById('item-2');
    console.log(i1.getAttribute('aria-selected')); // 'false'
    console.log(i2.getAttribute('aria-selected')); // 'true'
    </script>
</body>
</html>

 

  瀏覽器的DEMO:

let products = new Proxy({
        browsers: ['Internet Explorer', 'Netscape']
    },
    {
        get: function(obj, prop) {
            // 如果實際的屬性為latestBrowser
            if (prop === 'latestBrowser') {
                return obj.browsers[obj.browsers.length - 1];
            }

            // 其他的屬性
            return obj[prop];
        },
        set: function(obj, prop, value) {
            // 如果實際的屬性為latestBrowser
            if (prop === 'latestBrowser') {
                obj.browsers.push(value);
                return;
            }

            // 其他屬性的話, 把值轉化為數組;
            if (typeof value === 'string') {
                value = [value];
            }

            // 設置屬性
            obj[prop] = value;
        }
    });

console.log(products.browsers); // ['Internet Explorer', 'Netscape']
products.browsers = 'Firefox';
console.log(products.browsers); // 輸出: ['Firefox']

products.latestBrowser = 'Chrome';
console.log(products.browsers); //  輸出:['Firefox', 'Chrome']
console.log(products.latestBrowser); //  輸出:'Chrome'

 

  參考:

    mdn:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

    阮一峰:http://es6.ruanyifeng.com/#docs/proxy

作者: NONO
出處:http://www.cnblogs.com/diligenceday/
QQ:287101329
微信:18101055830 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM