zoukankan      html  css  js  c++  java
  • 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 

  • 相关阅读:
    小学生学python(六)类与函数
    Windows 10 搭建 Flask
    CentOS 8 上安装 python3
    6_7 selenium使用代理IP
    6_6 模拟浏览器的前进后退&窗口句柄切换
    6_5 selenium操作cookie
    6_4 行为链
    6_3 selenium操作表单元素
    6_2 selenium定位元素的方法
    6_1 selenium 安装与 chromedriver安装
  • 原文地址:https://www.cnblogs.com/diligenceday/p/5467634.html
Copyright © 2011-2022 走看看