【ES6】Proxy对象


引言:ES6规范里面新增了Proxy对象,在高级范畴的js编程或者底层脚本的编写有这极强的作用。

一、Proxy的基本用法

        Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程"( meta programming),即对编程语言进行编程。
        Proxy可以理解成在目标对象前架设一“拦截”层,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理"”某些操作,可以译为“代理器”。
        在介绍Proxy对象的基本用法之前,我们先来讲述一下JS中对象的机制。例如,我先简单的创建一个对象:var obj = {};,现在obj对象是没有属性的。假如我再写一条语句obj.id=1;,此时,虽然obj对象没有id这条属性,但是obj.id=1操作是调用了JS对象机制内部的set方法。
        下面,给出Proxy对象定义的基本格式:var proxy = new Proxy(target , handler);

Proxy对象的所有用法都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成个Proxy实例,target参数表示所要拦截的目标对象的名称,hander参数是一个函数方法,用来定制拦截行为。

        代码块1-1运用Proxy对象写了一个拦截对象对自己的属性进行赋值与取值的代理器。

/***@@    代码块1-1    拦截器举例一    @@***/
var obj = new Proxy({},{
	get: function (target, key, receiver) {
		console. log(`getting ${key}!`);
		return Reflect.get(target, key, receiver);
	},
	set: function (target, key, value, receiver) {
		console. log(`setting ${key}!`);
		return Reflect.set(target, key, value, receiver);
});

obj.count=1; 
// setting count!
++obj.count;
//getting count!
//setting count!
//2
代码块1-1解释:
首先,定义obj对象为Proxy类。
Proxy第一个参数应该是一个对象名,表示要拦截的目标对象,本例为空,则指向源对象obj。
Proxy第二个参数是自定义的拦截方法。
本例是对JS对象机制的get与set进行重定义,其中target参数是目标对象名,key是属性名。
 
当obj.count=1;代码运行时,首先会调用obj对象的set函数机制,然后被Proxy拦截,输出setting count!。
当obj.count++;运行时,先get到count的值,再去set。

        代码块1-1对一个空对象做了一层拦截,重定义了属性的读取(get)和设置(set)行为。Proxy 实际上重载(overlad)了点运算符,即用自己的定义覆盖了语言的盾始定义。
        下面是另一个拦截读取属性行为的例子。见代码块1-2。

/***@@    代码块1-2    拦截读取属性    @@***/
var proxy = new Proxy({}, {
	get: function(target, property) {
		return 35;
	}
});
proxy.time // 35
proxy.nane // 35
proxy.title // 35

        上面的代码中,作为构造函数Proxy接受两个参数:第一个参数是所要代理的目标对象(上例中是一个空对象),即如果没有Proxy介人,操作原来要访问的就是这个对象;第二个参数是配置对象,对于每一个被代理的操作,需要提供一 个对应的处理函数,该函数将拦截对应的操作。比如,上面的代码中, 配置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35,所以访问任何属性都将得到35。
        注意,要使Proxy起作用,必须针对Proxy实例(上例中是proxy对象)进行操作,而不是针对目标对象(上例中是空对象)进行操作。
        如果handler没有设置任何拦截,那就等同于直接通向原对象。见代码块1-3。

/***@@    代码块1-3       @@***/
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"

        上面的代码中,handler 是一个空对象,没有任何拦藏效果,访问hndler就等同于访问target。
        一个技巧是将 Proxy对象设置到object.proxy属性,从而可以在object对象上调用。
var object= { proxy: new Proxy(target, handler) }

二、Proxy示例的方法

1)get()

        get方法用于拦截某个属性的读取操作。前面已经有一个例子, 下面是另一个拦截读取操作的例子。见代码块2-1-1。

/***@@    代码块2-1-1       @@***/
var person = {
	name: "张三"
};

var proxy = new Proxy(person, {
	get: function(target, property) {
		if (property in target) {
			return target[property];
		} else {
			throw new ReferenceError("Property \"" + property + "\" does not 	exist.");
		}
	}
});
proxy.name // "张三”
proxy.age //抛出一个错误

2)set()

        set方法用于拦截某个属性的赋值操作。
        假定Person对象有个age属性,该属性应该是个不大于200的整数,那么可以使用Porxy对象保证age的属性值符合要求。见代码块2-2-1。

/***@@    代码块2-2-1       @@***/
let validator = {
	set: function(obj, prop, value) {
		if (prop === 'age') {
			if (!Number.isInteger(value)) {
				throw new TyeError('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;

person.age // 100
person.age = 'young' //报错
person.age = 300 //报错

        上面的代码中,由于设置了存值函数set,任何不符合要求的age属性赋值都会抛出一个错误。利用set方法,还可以数据绑定,即每当对象发生变化时,会自动更新DOM。
        有时,我们会在对象上设置内部属性,属性名的第一个字符使用 下画线开头,表示这些属性不应该被外部使用。结合get和set方法,就可以做到防止这些内部属性被外部读写。

/***@@    代码块2-2-1   屏蔽下划线开头的属性    @@***/
var handler = {
	get (target, key) {
		invartant(key, 'get');
		return target[key];
	},
	set (target, key, value) {
		invartant(key, 'set');
		return true;
	}
} 
functton invartant (key, action) {
	if (key[0] === '_') {
		throw new Error(`Invalid attempt to ${action} private "${key}" property`);
		}
}
var target = {};
var proxy = new Proxy(target, handler);

proxy._prop
//Error: Invalid attempt to get private " prop" property
proxy._prop = 'c'
// Error: Invalid attempt to set private " prop" property

        上面的代码中,只要读写的属性名的第一个字符是下画线,一律抛出错误,从而达到禁止读/写内部属性的目的。

3)apply()

        apply方法拦截函数的调用、call和apply操作。形式如下:
var handler = {
apply (target, ctx, args) {
return Reflect. apply(…arguments);}
}

        apply方法可以接受3个参数,分别是目标对象、目标对象的上下文对象( this )和目标对象的参数数组。下面是一个例子。

var target= function () { return 'I am the target'; };
var handler =
	apply: function () {
		return 'I am the proxy';
	};
var p = new Proxy(target, handler);
p() === 'I am the proxy';
// true

        上面的代码中,变量p是Proxy的实例,当作为函数调用时(p()),就会被apply方法拦截,返回一个字符串。

查看更多ES6教学文章:

1. 【ES6】let与const 详解
2. 【ES6】变量的解构赋值
3. 【ES6】字符串的拓展
4. 【ES6】正则表达式的拓展
5. 【ES6】数值的拓展
6. 【ES6】数组的拓展
7. 【ES6】函数的拓展
8. 【ES6】对象的拓展
9. 【ES6】JS第7种数据类型:Symbol
10. 【ES6】Proxy对象
11. 【ES6】JS的Set和Map数据结构
12. 【ES6】Generator函数详解
13. 【ES6】Promise对象详解
14. 【ES6】异步操作和async函数
15. 【ES6】JS类的用法class
16. 【ES6】Module模块详解
17. 【ES6】ES6编程规范 编程风格

参考文献

阮一峰 《ES6标准入门(第2版)》

发布了57 篇原创文章 · 获赞 43 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43592352/article/details/103989185