ECMAScript6 Proxy的使用

Posted softwarecrash

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
		}

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

以上是关于ECMAScript6 Proxy的使用的主要内容,如果未能解决你的问题,请参考以下文章

es6入门教程完整版

阿里四面:你知道Spring AOP创建Proxy的过程吗?

ECMAScript6 | 特性(部分)

ECMAScript6学习笔记

Vue3.0 双向绑定原理

关于ECMAScript6一些知识