ES6新特性:Proxy; 要使用的话, 直接在浏览器中执行即可, node和babel目前还没有Proxy的polyfill;,要使用的话,直接在浏览器中运行就好了, 浏览器的兼容性为:chrome>49或者firefox>18;
Proxy如其名, 它的作用是在对象和和对象的属性值之间设置一个代理,获取该对象的值或者设置该对象的值, 以及实例化等等多种操作, 都会被拦截住, 经过这一层我们可以统一处理,我们可以认为它就是“代理器” ;
Proxy是一个构造函数, 使用new Proxy创建代理器, 第一个参数为一个对象, 第二个参数也为一个对象, 返回被包裹后的代理器, 我们使用基本的get和set写一个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提供了更多的接口 , 通过不同的参数, 我们可以截获代码的运行并重新处理, 顾名思义, 代理嘛:
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()
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 );
当对象被设置原型的时候会执行我们设定的代码:
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"}
当对象被new的时候会执行handler.constructor方法, 这个有点屌哦, 因为你现在可以去new一个对象了, 平常你只能new构造函数的:
<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.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", 但是它还是一个对象....
通过construct和apply两个变量, 可以实现一个:继承构造函数的工具函数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