Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Proxy in JavaScript | Part 1
Late Night Coder
Late Night Coder

Posted on

     

Proxy in JavaScript | Part 1

A Proxy object wraps another object and intercepts operations on it. While intercepting operations like reading, writing properties on the object, the proxy may choose to handle these operations and modify results.

Proxy

Syntax:let proxy = new Proxy(target, handler);

target: the object that has to be proxied. 

handler: the proxy configuration object, it may registertraps. Atrap is a handler for a particular kind of operation. By registering atrap handler it can intercept the operation and do its own thing.

If there is atrap for the operation onhandler only then the operation will be trapped and handled by proxy else operation directly occurs on the object itself.

letuser={};// target object -- object to be proxiedletuserProxy=newProxy(user,{});// proxy for user, note empty handler// operations on proxyuserProxy.name='Aniket';// set operation// should be intercepted by// `set` trap on handler// no `set` trap registerd so// operations are performed on object itselfconsole.log(userProxy.name);// 'Aniket;// get opertaion// should be intercepted by `get` trap// no `get` trap registerd so opertaion// is performed on object directlyconsole.log(user.name);// 'Aniket'// Thus we can see name property// directly on target itself
Enter fullscreen modeExit fullscreen mode

For most of the operations on objects, there are “Internal Methods” in JavaScript that describes how operations work at a low level, what proxy trap does is that it can intercept these methods and do its own thing.

Proxy visualisation

Below we show some of the “Internal Methods” and their corresponding proxy traps.

Proxy trap and corresponding Internal method

Internal Methods have some rules that our traps must follow, for eg:set the trap must returntrue if property setting was success elsefalse.[[GetPrototypeOf]] must always return the target’s prototype when applied on proxy as well.

The problem statement

It is common practice to use*_* is in the beginning of the property name to denote a private property. You cannot get/set/loop this property. Write a proxy to achieve this.

letuser={name:'Aniket',_password:'Password',// private propertyisCorrectPassword(pswd){returnthis._password===pswd;// `this` here is a gotcha},};
Enter fullscreen modeExit fullscreen mode

“set” trap

We will register aset trap on the handler to intercept write operation on the object.

Syntax:set(target, prop, value, receiver).

target : target object. 

prop : property name that is being set. 

value : the value of the property to be set. 

receiver : the object that is utilised as in getters.

letuserProxy=newProxy(user,{set(target,prop,value,reciver){// intercepts property writeif(prop.startsWith('_')){// check if property name start with `_`// then it is a private property so// don't allow to write or create a propertythrownewError("Access denied 💣");}else{target[prop]=val;// normally write on objectreturntrue;// must return true [[Set]] rule}}});
Enter fullscreen modeExit fullscreen mode

“get” trap

We will register aget trap to prevent direct accessuser._password to private property. Also, we have to ensure thatisCorrectpassword works correctly as it does indirect accessthis._password.

Syntax:get(target, property, receiver)

The arguments mean the same as above.

letuserProxy=newProxy(user,{get(target,prop,receiver){// intercept property readif(prop.startsWith('_')){// if property name starts with `_` then// we don't allow to read and raise an errorthrownewError("Access denied 💣");}else{// the property value may be a function or something elseletpropValue=target[prop];// in case it is a function// it may have `this` inside it// where `this` will ref to `userProxy`// as it will be invoked as `userProxy.isCorrectPassword(pswd)`// so `this == userProxy` but that will 🔥 our code// so we need to make sure that our function `this` ref `user`// and so we bind itreturn(typeofpropValue==="function"?propValue.bind(target):propValue);}}});
Enter fullscreen modeExit fullscreen mode

“deleteProperty” trap

We will registerdeleteProperty so that we can't delete a private property.

Syntax:deleteProperty(target, property)

letuserProxy=newProxy(user,{deleteProperty(target,prop){// deleteProperty trap to handle property deleteif(prop.startsWith('_')){thrownewError("Access denied 💣");}else{// delete property on objectdeletetarget[prop];returntrue;// successfully deleted}}});
Enter fullscreen modeExit fullscreen mode

“ownKeys” trap

for..in, Object.keys, Object.values and other methods utilise an “Internal Method” called[[OwnPropertyKeys]] to get a list of keys. For eg:

Object.getOwnPropertyNames() to get a list of non-symbol keys,

Object.getOwnPropertySymbols() to get a list of symbol keys, 

Object.keys() to get a list of non-symbol enumerable keys, etc.

They all call[[OwnPropertyKeys]] but tweak it a bit to return keys according to their use case. So we will registerownKeys(target) trap to return only public keys.

letuserProxy=newProxy(user,{ownKeys(target){// ownKeys will return a list of keys// we must get keys on target then filter// to remove all private keysreturnObject.keys(target).filter((key)=>!key.startsWith('_'));}});
Enter fullscreen modeExit fullscreen mode

Note: Our traps must follow the rules defined for “Internal Method”. The rule defined forownKeys withObject.keys() is that it must return non-symbol enumerable keys. Look at the example below to understand this gotcha.

letuserProxy=newProxy(user,{ownKeys(target){// this will return list of keys// and the calling method (Object.keys) tweak this list// to select and return a list of// non-symbolic and enumberable: true keys// thus for each item in list returned by ownKeys// it will only select item which is// non-symbolic and enumberable: truereturn['email','phone'];}});console.log(Object.keys(userProxy));// [] empty 😱 gotcha// solutionletuserProxy=newProxy(user,{ownKeys(target){// Object.keys will check property descriptor// for each key returned by ownKeys and see if// enumberable: truereturn['email','phone'];},getOwnPropertyDescriptor(target,prop){// checking for enumberablity of keys// is accessing its descriptor and seeing// if enumberable is true// here we are returning descriptor obj// with enumberable true in all casesreturn{enumerable:true,configurable:true,};}});```#### “has” trapThis trap work with the `in` operator that intercepts the `[[hasProperty]]` Internal Method. Let’s register a `has(target,property)` trap.```jsletrange={from:1,to:10,};// we need to check if 5 in range// 5 in range if 5 >= range.from && 5 <= range.toletrangeProxy=newProxy(range,{has(target,prop){// 5 >= 1 && 5 <= 10returnprop>=target.from&&prop<=target.to;},});console.log(5inrangeProxy);// true
Enter fullscreen modeExit fullscreen mode

“apply” trap

Until now all examples we have seen were on objects and now we will see an example offunction as target.

Syntax:apply(target, thisArgs, args)

thisArgs : it is the value ofthis 

args : it is a list of arguments for function

// Let us write a function `delay`// that delay exceution of any// function `f` by `ms` milliseconds// solution 1 closure wayfunctiondelay(f,ms){returnfunction(name){// *setTimeout(()=>f.bind(this,arguments),ms);}}varhi=(name)=>{console.log('Hi!'+name);};console.log(hi.length);// 1// function.length returns number a paramshi=delay(hi,3000);// hi is now function at line *console.log(hi.length);// 0 😱// we lost orignal hi function// and function at line * has no params so 0hi('Aniket');// 'Hi! Aniket'// runs after 3s// solution 2 proxy wayfunctiondelay(f,ms){returnnewProxy(f,{apply(target,thisArgs,args){setTimeout(()=>target.bind(thisArgs,args),ms);}});}varhi=(name)=>{console.log('Hi!'+name);};console.log(hi.length);// 1hi=delay(hi,3000);console.log(hi.length);// 1 😎hi('Aniket');// 'Hi! Aniket'
Enter fullscreen modeExit fullscreen mode

The End

Now teach the Proxy you learnt here to your friend for whom you have put proxy 😂. Here is the next part of the postPart 2. Stay tuned for more content.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I’m a software ninja 🥷 who loves abstractions and spread functional programming everywhere.
  • Location
    Delhi
  • Education
    BTech(BE) CSE
  • Work
    Software Engineer at Quillbot
  • Joined

More fromLate Night Coder

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp