JS. Proxy. Pitfall to Know About

    This article will be of interest to those who use Proxy for reactivity or reflection.
    The behavior of JS methods is well known to us if we simply use them within an object.
    If a method is passed through a property to another object, then it works with that this, which is defined in the framework of another object.

    let obj1={prop1:'HEllo',method1(){console.log(this);}}
    let obj2={method2:obj1.method1};
    obj2.method2();
    

    This must be clearly understood when using Proxy.

    class MyProxy{
    	constructor(target){
    		return new Proxy(target,this);
    	}
    	get(target,prop){
                    console.log(target,prop); //Label1
    		return target[prop];
    	}
    }
    class PrimitiveType
    {
    	constructor(target,prop)
    	{
    	    this.target=target;
    	    this.prop=prop;
    	}
    	get(){
    		console.log(this);// Label2  
    		return this.target[this.prop];
    	}
    }
    prim=new PrimitiveType({a:'Привет'},'a');
    proxy= new MyProxy(prim);
    proxy.get();
    

    The result is that console.log (Label2); will give us a Proxy object, after which Proxy will work on both target and prop (see Label1); but the code seems to work. What to bathe.

    The method begins to communicate with the object (this) through Proxy. This is convenient and logical when we write reflection (reflection of the properties of the object and changes in behavior without changing the object). But if we don’t need it and we need the method to work specifically with the target object, what should we do? Why do we slow down the code?

    Moreover, if we introduce more logic, for example, property filters, etc., the code may accidentally bend. And when writing reactive code, there is a “roll-over”. (For example, when requesting a method and its subsequent execution, the method requests properties through proxies for which events have already been posted). That is, events begin to fire where it is not necessary and they were not expected.

    How to fix


    As understood, this is already overridden for the method before calling Handler.get in Proxy. You just need to redefine it again as follows:

    let answer=target[prop];
    if(typeof target[prop] ==='function'){
            answer=target[prop].bind(target);
    }
    

    We get the following code:

    class MyProxy{
    	constructor(target){
    		return new Proxy(target,this);
    	}
    	get(target,prop){ // по уму название ему valueOf.  Но для наглядного поведения ему имя get 
            let answer=target[prop];
            if(typeof target[prop] ==='function'){
                    answer=target[prop].bind(target);
            }
    		return answer;
    	}
    }
    class PrimitiveType
    {
    	constructor(target,prop)
    	{
    	    this.target=target;
    	    this.prop=prop;
    	}
    	get(){
    		console.log(this);
    		return this.target[this.prop];
    	}
    }
    prim=new PrimitiveType({a:'Привет'},'a');
    proxy= new MyProxy(prim);
    proxy.get();
    

    Finally, as a bonus.


    Creating a chain of reactivity / reflection. Each nested object will be a proxy:

    class MyProxy{
    	constructor(target){
    		return new Proxy(target,this);
    	}
    	get(target,prop){
    		let answer;
            let tp=target[prop];// так необходимо если target - Proxy или target[prop] -getter
    		if(typeof tp==='object' && tp!==null){
    			answer =new MyProxy(tp);
    		} else 
    		if(typeof tp ==='function'){ // Если необходима реактивность.  Для рефлексии  стоит убрать этот блок
            	answer=tp.bind(target);
            } else {
    			answer=tp;
    		}
    		return answer;
    	}
    }
    class PrimitiveType
    {
    	constructor(target,prop)
    	{
    	    this.target=target;
    	    this.prop=prop;
    	}
    	valueOf(){
    		console.log(this);
    		return this.target[this.prop];
    	}
    }
    prim=new PrimitiveType({a:'Привет'},'a');
    qwer={q:prim};
    proxy= new MyProxy(qwer);
    proxy.q
    

    Thanks for attention!

    Also popular now: