简单的说一下 Proxy

Proxy的理解

Proxy是es6的新特性,

	用于创建一个对象的代理,从而实现基本的拦截、自定义(属性查找、赋值、枚举、函数调用....)。

	Proxy可以直接监听整个对象而非属性,可以监听数组的变化,多达13种拦截方法。

vue3数据响应式的原理,采用的就是,Proxy代理


语法:let p = new Proxy(target, handler)

	  target:目标对象(可以是任意类型的对象,包括:数组、函数、甚至是另一个代理)

	  handler:具体的操作,其实就是一个对象,其属性是:当执行一个操作时定义代理的行为的函数
			   就是说里面写的是各种拦截函数,不同的拦截方法拦截的是不同的操作

监听方法如下,

	get(target, property, receiver),用于拦截某个属性的读取操作,

		target:目标对象,
		property:属性名,
		receiver:proxy实例本身(此参数可选)

	set(target, property, value, receiver),用于拦截某个属性的赋值操作,

		target:目标对象,
		property:属性名,
		value:属性值,
		receiver:proxy实例本身(此参数可选)

举个栗子,
	
	let obj = { name: 'White', age: 18, sex: '女' }
	
	// 给 obj 设置一个代理
	let p1 = new Proxy(obj, {
		get(target, property) {
			// 一些骚操作...

			// 最后,返回你想要的值即可(你想返回什么就返回什么)
			return target[property]
		},
		
		set(target, property, value){
			// 一些骚操作...

			target[property] = value
		}
	})

	p1.age = 123456

	// 读取 obj 的 age属性,注意定义代理后,得用代理来调用属性、方法
	console.log('p1.age=', p1.age)

	console.log('p1=', p1)

对Proxy的结构做一下解释


用,上面 obj对象 的代理,做例子,说一下吧

下面这就是 proxy代理 的样子(console.log('p1=', p1)的打印如下)

Proxy(Object) {name: 'White', age: 123456, sex: '女'}

[[Handler]]: Object
	get: ƒ get(target, property)
	set: ƒ set(target, property, value)
	[[Prototype]]: Object

[[Target]]: Object
	age: 123456
	name: "White"
	sex: "女"
	[[Prototype]]: Object

[[IsRevoked]]: false


[[Handler]]:是一个内部属性,指向一个对象,其中包含了钩子函数(handler functions),用来拦截对代理对象的各种操作,
			 这些钩子函数包括:get用于拦截对属性的读取操作,set用于拦截对属性的设置操作,以及其他诸如 apply、construct 等。
			 补充一下:这个get、set,就是我们在 p1代理中 自己定义的。

[[Target]]:是一个内部属性,指向被代理的目标对象,
			也就是说,Proxy代理的是这个目标对象,通过代理可以对目标对象进行各种操作的拦截。

[[IsRevoked]]:是一个内部属性,表示该代理是否被撤销,当代理被撤销时,它将失去对目标对象的代理能力,`[[IsRevoked]]`的值也会发生变化
				
			[[IsRevoked]]有两种结果:
				false:表示代理对象尚未被撤销,它仍然有效,可以继续拦截对目标对象的操作。
				true:表示代理对象已经被撤销,它会失去对目标对象的代理能力,无法再拦截对目标对象的操作,也无法再访问目标对象的属性和方法。

	实际使用中,通常不需要直接操作 `[[IsRevoked]]` 属性,
	因为代理对象的撤销通常是由系统自动处理的。当代理对象不再被引用时,它会被自动垃圾回收,从而触发撤销操作。

----------------------------- 具体解释一下 -----------------------------

首先

  • 为什么Proxy会取代Object.defineProperty
    
     1、Object.defineProperty,只能劫持对象的属性,
        不能监听数组,也不能对es6新产生的Map、Set这些数据解构做出监听,
        也不能监听新增、删除操作等等。
     
     2、Proxy可以直接监听整个对象而非属性,可以监听数组的变化,具有多达13种拦截方法
    
  • 什么是Proxy
    
    Proxy是es6的新特性,用于创建一个对象的代理,
    从而实现基本操作的拦截、自定义(如:属性查找、赋值、枚举、函数调用等等)
    
    Proxy可以直接监听整个对象而非属性,可以监听数组的变化。具有多达13种拦截方法
    
    【语法】:
        let p = new Proxy(target, handler)
        * 参数1,target:目标对象(可以是任何类型的对象,包括原生数组、函数、甚至是另一个代理)
        * 参数2,handler:具体的操作,其实就是一个对象,其属性是,当执行一个操作时定义代理的行为的函数,
                          就是说里面写各种拦截的函数,不同的拦截方法拦截的是不同的操作。
    	
    	getset的拦截方法如下:
    		1、get(target,property,receiver),用于拦截某个属性的'读取'操作,
    	          参数1,target:目标对象
    	          参数2,property:属性名
    	          参数3,receiver:proxy实例本身(可选)
    
    		2、set(target, property, value, receiver),用于拦截某个属性的'赋值'操作,
    	          参数1,target:目标对象
    	          参数2,property:属性名
    	          参数3,value:属性值
    	          参数4,receiver:proxy实例本身(可选)
    
    	'举个例子'
    		let obj1 = {
    			name: 'jack', age: 18, sex: '女',
    		}
    		
    		// 给obj设置一个代理
    		let p1 = new Proxy(obj1, {
    			get(target, property) {
    				console.log('get--target->', target)
    				console.log('get--property->', property)
    				console.log('get--target[property]=', target[property])
    				// 定义你要返回的值(你想返回什么就返回什么)
    				return target[property]
    			},
    			set(target, property, value) {
    				console.log('set--target->', target)
    				console.log('set--property->', property)
    				console.log('set--target[property]=', target[property])
    				console.log('set--value=', value) // 123456
    				target[property] = value
    			}
    		})
    
    		// 这里会走代理的set哦
    		p1.age = 123456
    
    		// 读取obj1的age属性看看,注意定义代理后,得用代理来调用属性、方法
    		console.log('p1.age=', p1.age)
    
    		console.log('------------------------------------------------------')
    		console.log('obj1=', obj1) // obj1= {name: 'jack', age: 123456, sex: '女'}
    		console.log('p1=', p1) // p1= Proxy(Object) {name: 'jack', age: 123456, sex: '女'}
    
    

小例子 - 1


'要求':obj1有个age属性,此属性是不大于200的整数,利用Proxy保证age的值符合要求

用 Number.isInteger(aaa) 判断'变量aaa'是否为整数
		Number.isInteger(26) // true
		Number.isInteger(26.0) // true
		Number.isInteger(26.1) // false
		Number.isInteger("15") // false
		Number.isInteger(true) // false

	const obj1 = { name: 'jack', age: 18, sex: '女', }
	const proxy1 = new Proxy(obj1, {
		get(target, property) {
			return target[property] // 定义你要返回的值(你想返回什么就return什么)
		},
		set(target, property, value) {
			// 对age属性继进行约束
			if (property === 'age') {
				// 判断是不是整数
				if (!Number.isInteger(value))  new TypeError('the age is not a integer')
				// 判断不大于200
				if (value > 200) new RangeError('the age seems invalid') 
			}
			// 对其他属性就直接赋值;对满足条件的age属性也直接赋值
			target[property] = value
		}
	})
	
	proxy1.age = 300 // 浏览器会抛出错误,因为 age > 200

小例子 - 2


'要求':
	事先给对象设置的内部属性,属性名的第一个字符用下划线开头,表示这些属性不能被外部使用,
    结合getset方法,就可以防止这些内部属性被外部读写。

	const obj2 = { name: 'bbbbbb', _age: 20, sex: '男', }
	
	// 定义工具函数,Proxy中要使用
	function invariant(property, action) {
		if (property[0] === '_') {
			throw new Error(`不能对${property}属性进行${action}操作`)
		}
	}
	
	let proxy2 = new Proxy(obj2, {
		get(target, property) {
			invariant(property, 'get')
			return target[property]
		},
		set(target, property, value) {
			invariant(property, 'set')
			target[property] = value
			return true
		}
	})

	console.log('proxy2._age=', proxy2._age) // 浏览器会抛出错误(Error: 不能对_age属性进行get操作)
	proxy2._age = 123 // 浏览器会抛出错误(Error: 不能对_age属性进行set操作)

set方法第四个参数 receiver


	这里涉及到了`原型链`,所以说一下
		const obj1 = {}
		const obj2 = { foo: 'bar' }
		Object.setPrototypeOf(obj1, obj2)
	
		const { foo } = obj1
		foo // "bar"
		将,obj1的原型对象设置为obj2,那么,在obj1找不到属性,就按照原型链到它(obj1)的原型对象(obj2)上去找


	let obj3 = { a: 1,  b: 2,  c: 3,  d: '哒哒哒' }
	let proxy3 = new Proxy(obj3, {
		set(target, property, value, receiver) {
			console.log('3-receiver=', receiver)
			target[property] = receiver
		}
	})
	proxy3.a = 11 // 进行赋值操作,从而触发proxy的set方法
	console.log(proxy3.a === proxy3) // true


	---------- 扩展深入一下 ----------

	let proxy4 = new Proxy(obj3, {
		set(target, property, value, receiver) {
			console.log('target=', target) // target= obj3
			console.log('property='. property) // value= undefined
			console.log('value=', value) // value= bar
			console.log('在这里看一下<原始的操作行为所在的对象>==', receiver) // 打印的是{ a: '1', b: '2' }
			target[property] = receiver
		}
	})
	const myObj = { a: '1', b: '2' }
	Object.setPrototypeOf(myObj, proxy4) // 将myObj的原型对象设置为proxy4
	myObj.foo = 'bar'
	console.log('myObj.foo === myObj:', myObj.foo === myObj) // myObj.foo === myObj: true
	console.log('myObj.foo=', myObj.foo) // myObj.foo= {a: '1', b: '2'}



  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值