• ECMAScript6 Proxy的使用


    参考:http://es6.ruanyifeng.com/#docs/proxy

    Proxy 的13种拦截操作。

    • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
    • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
    • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
    • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
    • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy),返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
    • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
    • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
    • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
    • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
    • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
    • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
    • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
    • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

    get:

    /**
    		 * 1.get(target, propKey, receiver)
    		 * 	target:目标对象
    		 * 	propKey:属性名
    		 * 	receiver:操作行为所针对的对象(可选)
    		 *
    		 * 	get方法用于拦截某个属性的读取操作
    		 */
    		var student = {
    			name : "Bill",
    			age : 20
    		};
    		var handlerStudent = {
    			get: function(target, property, receiver){
    				if(property in target){
    					//这里的property = "name",所以不能以target.property的方式调用
    					return target[property];
    				}else{
    					// return "No this property!";
    					throw "Error";
    				}
    			}
    		};
    		try{
    			var getProxy = new Proxy(student, handlerStudent);
    			console.log(getProxy.name);	//Bill
    			console.log(getProxy.age);	//20
    			console.log(getProxy.sex);	//抛出异常
    		}catch(error){
    			console.log(error);
    		}
    

    set:

    /**
    		 * 2.set(target, propKey, value, receiver)
    		 * 	target:目标对象
    		 * 	propKey:属性名
    		 * 	value:属性值
    		 * 	receiver:操作行为所针对的对象(可选)
    		 *
    		 * set方法用于拦截某个属性的赋值操作
    		 */
    		
    		var handlerStudent = {
    			set: function(target, property, value){
    				if(property === "age"){
    					if(!Number.isInteger(value)){
    						throw "TypeError";
    					}
    					if(value >= 20){
    						throw "Value is invalid";
    					}
    				}
                       //这里的property = "age",所以不能以target.property的方式调用 target[property] = value; } }; try{ var setProxy = new Proxy({}, handlerStudent); setProxy.age = 10; console.log(setProxy.age); setProxy.age = 'Ac'; //TypeError setProxy.age = 1234; //Value is invalid }catch(error){ console.log(error); }

    apply:

    /**
    		 * 3.apply(target, object, args)
    		 * 	target:目标对象
    		 * 	object:目标对象的上下文
    		 * 	args:目标对象的数组
    		 *
    		 * apply方法拦截函数的调用,call和apply操作。
    		 */
    		function sum(a, b) {
    			return a + b;
    		}
    		var handler = {
    			apply(target, obj, args){
    				let a = args.length;
    				let b = 1;
    				for(let i = 0; i < a; i++){
    					b *= args[i];
    				}
    				return b;
    			}
    			/**
    			 * 你可能会遇到这种写法:
    			 * apply: function(target, obj, args){}
    			 * 不必担心,这与apply(target, obj, args){}是等价的。
    			 */
    		}
    
    		var proxy = new Proxy(sum, handler);
    		console.log(proxy(1, 2));				//2
    		/**
    		 * 为什么call()和apply传参的方式不同?请查阅相关的方法。
    		 */
    		console.log(proxy.call(null, 5, 7));	//35
    		console.log(proxy.apply(null, [10, 20]));//200
    

    has:

    /**
    		 * 4.has(target, propKey)
    		 * 	target:目标对象
    		 * 	propKey:属性名
    		 *
    		 *	has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法就会生效。典型的操作符就是in操作符。 
    		 */
    		var stu1 = {
    			name : 'Alice',
    			score: 59
    		};
    		var stu2 = {
    			name : 'Bill',
    			score: 86
    		};
    
    		var handler = {
    			has(target, prop){
    				if(prop === 'score' && target[prop] < 60){
    					console.log(target.name + " 不及格");
    					return false;
    				}
    				return prop in target;//这返回的是一个布尔值
    			}
    		}
    
    		var proxy1 = new Proxy(stu1, handler);
    		var proxy2 = new Proxy(stu2, handler);
    
    		'score' in proxy1;	//Alice 不及格
    		'score' in proxy2;	//<啥也没输出>不过你可以添加一个console.log();查看结果
    
    		/**
    		 * 即使for(...in...)中有in操作符,但是,has对其不生效。
    		 */
    		for(let i in proxy1){
    			console.log(proxy1[i]);// alice <这里有一个换行> 59
    		}
    		for(let i in proxy2){
    			console.log(proxy2[i]);//Bill <这里有一个换行> 86
    		}
    

    constract:

    /**
    		 * 5.construct(target, ages)
    		 * 	target:目标对象
    		 * 	args:构建函数的参数对象
    		 *
    		 * 	construct方法用于拦截new命令,返回的必须是一个对象
    		 */
    		
    		var handler = {
    			construct(target, args){
    				args.map(x => console.log(x));//3<这里有一个换行>4<这里有一个换行>5
    				return { num : args[0] * 10};
    			}
    		};
    		var proxy = new Proxy(function(){}, handler);
    		/**
    		 * 这里用new proxy()的原因是,返回的是一个对象,需要实例化才能调用其中的num(属性名)的属性值
    		 */
    		console.log((new proxy(3, 4, 5)).num);//30
    

    deleteProperty:

    /**
    		 * 6.deleteProperty(target, propKey)
    		 * 	target:目标对象
    		 * 	propKey:属性名
    		 *
    		 * 	deleteProperty方法用于拦截delete操作
    		 */
    		var handler = {
    			/**
    			 * deleteProperty无法删除不可配置的属性,否则会报错
    			 */
    			deleteProperty (target, propKey){
    				if(propKey[0] === '_'){
    					throw new Error("can't delete the property!");
    				}
    				return true;
    			}
    		}
    
    		var target = {_prop: 'a'};
    		var proxy = new Proxy(target, handler);
    
    		try{
    			delete proxy._prop;	
    		}catch(error){
    			console.log(error);
    		}
    

    defineProperty:

    /**
    		 * 7.defineProperty(target, propKey, propDesc):
    		 * 	target:目标对象
    		 * 	propKey:属性名
    		 * 	propDesc:属性描述符
    		 *
    		 * defineProperty方法用于拦截Object.defineProperty操作
    		 */
    		var handler = {
    			defineProperty (target, propKey, propDesc){
    				return false;
    			}
    		};
              //如果目标对象不可扩展,则defineProperty不能增加目标对象上不存在的属性,否则会报错。
              //另外,如果目标对象的某个属性不可写或不可配置,
              //则defineProperty方法不得改变这两个设置。 var proxy = new Proxy({foo1 : 2}, handler); proxy.foo = 'A'; console.log(proxy.foo + " " + proxy.foo1);//undefined 2

    getOwnPropertyDescriptor:

    /**
    		 * 8.getOwnPropertyDescriptor(target, propKey)
    		 * 	target:目标对象
    		 * 	propKey:属性名
    		 *
    		 * 	getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述
    		 * 	对象或者undefined.
    		 */
    			var handler = {
    				getOwnPropertyDescriptor(target, propKey){
    					if(propKey[0] === '_'){
    						return;
    					}
    					return Object.getOwnPropertyDescriptor(target, propKey);
    				}
    			};
    			var target = { _foo: 'bar', baz: 'tar'};
    			var proxy = new Proxy(target, handler);
    			console.log(Object.getOwnPropertyDescriptor(proxy, 'wat'));
    			//undefine
    			console.log(Object.getOwnPropertyDescriptor(proxy, '_foo'));
    			//undefine(对属性首字符为下划线的直接返回undefine)
    			console.log(Object.getOwnPropertyDescriptor(proxy, 'baz'));
    			//{value: "tar", writable: true, enumerable: true, configurable: true}
    

    getPrototypeOf(target):

    /**
    		 * 9.getPrototypeOf(target):
    		 * 	target:目标对象
    		 *
    		 * 	getPrototypeOf方法用于拦截获取对象原型。可以用于拦截以下操作:
    		 * 		-Object.prototype.__proto__
    		 * 		-Object.prototypr.isPrototypeOf()
    		 * 		-Object.getPrototyprOf()
    		 * 		-Reflect.getPrototyprOf()
    		 * 		-instanceof()
    		 */
    		var a = {foo : 1}
    		var proxy = new Proxy({},{
    			getPrototypeOf(target) {
    				//返回值必须是对象或者是null。如果目标对象不可扩展,getPrototype方法必须返回目标对象的原型对象
    				return a;
    			}
    		})
    		console.log(Object.getPrototypeOf(proxy) === a);//true
    

    isExtensible:

    /**
    		 * 10.isExtensible(target)
    		 * 	target:目标对象
    		 *
    		 * 	isExtensible方法拦截Object.isExtensible操作
    		 */
    		var proxy = new Proxy({}, {
    			isExtensible: function(target) {
    				//只能返回布尔值,否则将被自动转换为布尔值
    				return true;
    			}
    		});
    		//返回值必须与目标对象的isExtensible属性保持一致,否则报错
    		console.log(Object.isExtensible(proxy));
    

    ownKeys:

    /**
    		 * 11.ownKeys(target)
    		 * 	target:目标对象
    		 *
    		 * 	ownKeys方法用于拦截对象自身属性的读取操作
    		 * 		-Object.getOwnPropertyNames()
    		 * 		-Object.getOwnPropertySybols()
    		 * 		-Object.keys()
    		 */
    		var target = {
    			a : 1,
    			b : 2,
    			c : 3
    		};
    
    		var handler = {
    			ownKeys(target){
    				return ['a'];
    			}
    		}
    		//这里只举例一个操作,其余的请见最上方的参考
    		console.log(Object.keys(target));//["a", "b", "c"]
    		console.log(Object.keys(new Proxy(target, handler)));//["a"]
    

    preventExtensions:

    /**
    		 * 12.preventExtensions(target)
    		 * 	target:目标对象
    		 *
    		 * 	preventExtensions方法拦截Object.preventExtensions()
    		 * 	该方法只有一个限制,即目标对象不可扩展时(即Object.isExtensions为false),
    		 * 	其余该方法将返回true,否则报错
    		 */
    		
    		var proxy = new Proxy({}, {
    			preventExtensions(target){
    				//通过调用一次Object.preventExtensions()来防止Object.isExtensible()返回true
    				Object.preventExtensions(target);
    				return true;
    			}
    		});
    		console.log(Object.preventExtensions(proxy));//proxy{}
    

    setPrototypeOf:

    /**
    		 * 13.setPrototypeOf(target, proto)
    		 * 	target:目标对象
    		 * 	proto:原型对象
    		 *
    		 * setPrototypeOf方法用于拦截Object.setPrototypeOf
    		 */
    		var handler = {
    			setPrototypeOf(target, proto){
    				throw new Error('error');
    			}
    		}
    		var proto = {};
    		var target = {};
    		var proxy = new Proxy(target, handler);
    		try{
    			console.log(Object.setPrototypeOf(proxy, proto));
    		}catch(error){
    			console.log(error);
    		}
    

     注:任何细节请前往最上方的网站查看。

  • 相关阅读:
    Spring Boot整合Freemarker
    Spring Boot异常处理
    CSAPP缓冲区溢出攻击实验(下)
    SparkSQL基础应用(1.3.1)
    程序员的自我修养:(1)目标文件
    CSAPP缓冲区溢出攻击实验(上)
    Redis源码学习:字符串
    六星经典CSAPP-笔记(7)加载与链接(上)
    Redis源码学习:Lua脚本
    六星经典CSAPP-笔记(10)系统IO
  • 原文地址:https://www.cnblogs.com/softwarecrash/p/8784930.html
Copyright © 2020-2023  润新知