Check out a free preview of the full A Tour of JavaScript & React Patterns course
The "Proxy Pattern Solution" Lesson is part of the full, A Tour of JavaScript & React Patterns course featured in this preview video. Here's what you'd learn in this lesson:
Lydia codes the solution to the Proxy Pattern challenge. Questions about higher-order functions, creating proxies for nested objects, and using the Reflect.set method are also covered in this segment.
Transcript from the "Proxy Pattern Solution" Lesson
[00:00:00]
>> I'm just going to copy these requirements real quick cuz I will forget that. Let's see. Okay, this was pasted without any, okay, it's fine. So first, we need to create a new proxy. So I'm just going to create const userProxy = newProxy and then the target object is going to be the user.
[00:00:25]
So first, we have to get the, or we will just add the get method. So in here the username, actually we just have one gets, this is easier to read. When a property is retrieved so that's when the get method is executed, change the output to a new Date.
[00:00:41]
And then the value of property is the target. So let's see console.log. And instead of this target property, I'm just going to do Reflect.get target, property. I'm just typing this all out now, I mean, this could have been just TEP, all those things. But just to make it a bit more clear, so target, property.
[00:01:09]
So now let's just see if it works. [LAUGH] So we do userProxy.age node index js. Yes, so now we logged it with a timestamp. I know we could have used a different timestamp, this is always really long. And then the value of age is 42, I think I have some, here.
[00:01:36]
So that's what we needed to do to add logging to when a property is retrieved, now we just need to add the set one. So this one gets to target, the property that we're changing, I cannot type, and the value that we are changing it to. So with these two requirements where the username property has to be a string that only contains letters and has to be at least three characters long.
[00:02:01]
No, of course, you can add this validation in many different ways. But I'm just going to, if property, so the property that we're changing is age, or wait, did I say age or username, sorry, is username. If it's not, isAllLetters, [LAUGH] isAllLetters, the value that we're changing it to then throw new Error ("You can only use letters in your username"), or something like that.
[00:02:36]
Or if the username is shorter than three characters, cuz we also had that requirements. So if the value length is shorter than three, throw new Error("please provide a valid username"), or something like that. So now let's say that we want to add the userProxy. Let's just see if this works.
[00:02:57]
Or otherwise so if that's not the case, we want to just return Reflect.sets cuz we're setting in here. That's again the target, property value. So then it successfully REST request to the actual userProxy. So now we will use that username is ha, actually ha. So this shouldn't be doable because we were checking that it's at least three characters long, so let's see if this throws an error.
[00:03:26]
Yeah, so please provide a valid username. So right now, we didn't, if we do haha, It just works. Actually, I could have just add another log here of successfully updated your username or something. And if it's all letter, so now let's change it to a haha10, this should not work right here, you can only use letters in your username.
[00:03:48]
So now we have the first one, which is the username property has to be a string that only contains letters and is at least three characters long. Next one is the email property has to be a valid email address. So in here, if property is email, if it's not I think is valid email or something I named it, yeah.
[00:04:10]
The value that we're passing it, then throw new Error("please provide a valid email") please. So now if we do email is john@doe.com, actually I'll just add a log here just to say successfully updated or something, so it successfully updates. But if we do an invalid email maybe like johndoe.com, which is not an email, it should throw an error, please provide a valid email.
[00:04:38]
So that's one validation. Next, the age property has to be a number and has to be at least 18. So if property is age, if type of value is not number. And, of course, you can add these if statement in better ways. This is just to kind of simply show that this is one possibility, ("please provide a valid age") or something.
[00:05:05]
And then if value is lower than 18, throw a new Error("You have to be at least 18 years old"). So now when I say, I have an invalid age, maybe it's somehow a string, which can happen with JavaScript. We say please provide a valid age, this was not valid.
[00:05:27]
Or if maybe he is too young, he's 17, You have to be at least 18 years old. So yeah, again, this is pretty nice to just validate to make sure that the users that we're creating are all the users who are expecting, questions?
>> Are higher order functions generally used to implement proxy patterns?
[00:05:50]
>> They can, I don't know if that's usually what people do, cuz now with this newProxy object that's built in, you don't really have to do that. But it's definitely a useful way to kind of add it as almost a middleware, like a mediator. Before we add anything, you can use the built in proxy in whatever way to ensure that the requests that are made to the target object are always what we're expecting.
[00:06:17]
I don't know if there's a normal way that people usually do it, I haven't seen it. I've only mainly seen it like this, but I'm sure that everyone comes up with their own implementation of it.
>> Could intercepting request and response objects be a use case?
>> Yeah, absolutely, so that's essentially what it's doing, it is intercepting.
[00:06:37]
Or do you mean with middleware, express middleware which is kind of different. I mean, it kinda acts as a middleware function which is another pattern, but, yeah, I guess you can compare it definitely to middleware but it's not. A middleware is usually used for one single entry point to multiple objects.
[00:07:01]
But with a proxy it's only proxy to one, it's proxy object, proxy object. For the middleware, you have a middleware many objects to kind of route them to different ways. So it is similar if you use middleware for just one single endpoints, but that's usually not why you would wanna use a middleware anyways.
[00:07:20]
But it's similar, you can use a proxy as middleware, I guess. [LAUGH]
>> How would you handle arbitrarily nested objects?
>> Actually, let's see if we can add just a proxy, I haven't done this before. Actually, just do it address, I guess we can do this, street: 'Main street.
[00:07:37]
I just wanna create a proxy for just the address, const addressProxy = newProxy(user.address). Not sad, get: ( target property). Just going to log this the same way. Let's see. So now if we do addressProxy.street =, no, we're just getting it, sorry, so let's see what this is. I'm just going to comment this out real quick maybe, it'll throw an error main is not defined, property is not defined.
[00:08:17]
Cuz it's property, so yeah, you can just create proxy, obviously, I guess you can just create it on any object. So if you only care about the deeply nested objects, you can just add that proxy to do just that thing. And I think you could probably also, maybe add it directly, no, maybe not, I don't know.
[00:08:37]
Although, yeah, I guess, you can. You can get, Yeah, I don't know, I'm just kinda playing around here now. I mean, you can use proxies I guess on any object. And if you know the structure of the object that you're using, just make sure that if you're formatting, validating anything that you account for all these possibilities, which in this case I did not.
[00:09:05]
Cuz I just knew that aid only because it's already had firstname, last name, all those things, yeah?
>> Why not implement these methods directly on the user object, what's the benefit of moving it to a proxy?
>> So if you add this to the user objects and it would allocate memory for those functions, whenever you create that user.
[00:09:30]
Whereas with a proxy, we can keep it on just that proxy if that makes sense. So if we added this directly, say here, maybe instead of directly modifying. And I guess another one which is maybe we can just set firstname, which we will just always be using, sorry, to set the first name, which is maybe a value.
[00:09:54]
And maybe we can update the firstName that way, but there's still a possibility in that case that we can just directly modify user.firstName. Or maybe we don't have firstname, firstname is always a function or something. That wouldn't really be memory efficient because functions are other objects under the hood.
[00:10:12]
So every time we create an object, we're essentially creating a new object for all of these types. So using that validation directly on the object, a, doesn't stop people from modifying the properties cuz we could still just do that. If we know the name of the property, we could still use .notation bracket notation to update them.
[00:10:32]
And secondly, it's definitely more difficult to implement that on a direct object. Well, maybe in a class, you can easily update with this .blah blah equals the new value. But with a regular object that gets a bit more tricky, if that explains the question? I guess the more you use proxies, the more you will see their value cuz at least that's kinda what happened to me.
[00:10:59]
First I was, why would you really need a proxy for all these things? But when you really start using it in actual application, you're this is the perfect use case for a proxy and then you'll see it just kinda clicks. But it definitely takes some implementation in different settings to see the value, [LAUGH] of proxies for sure.
[00:11:18]
>> It reminds me of almost an interface.
>> Yeah, exactly, definitely is.
>> You could hand this out to an export.
>> Mm-hm.
>> And not have to expose the object directly. The person said I could see value in displaying dates in different formats, as a useful use case.
[00:11:43]
>> Yeah, there are, honestly, so many use cases to proxies, and I think it applies to so many different applications. Yeah, they're pretty powerful.
>> Why use the reflect object instead of directly accessing the properties? Honestly, it's just easier to type is more readable, so, You don't really have to worry about the names that you gave to the object and the property here.
[00:12:15]
Like you said before, I had the type of property not defined because I usually call it prop and all those things. Personally, I prefer using reflect just for readability. People know what's going on. And also with a set, you usually have to return a truthy value. Only if it returns a truthy value will it actually forward that, set the modification command to the target object.
[00:12:38]
But this is pretty easy to forget because if we didn't return anything here, we'd return undefined which is falsie. So we'd never actually forward that request to the target object. But Reflect.set automatically returns a truthy value, meaning that it will forward that request. So otherwise, you could have maybe done, well, first you had change it in that case.
[00:12:57]
So object.prop, = value. And then maybe return true or something, which to me isn't always very intuitive. I forget to do that and then you don't see the update. So personally, I prefer to use Reflect.set. Maybe it also has some built in benefits that I'm not aware of.
[00:13:16]
But to me, I just like it for readability, and yeah, it's easy to use.
>> What should you do to set with a proxy to a falsie value?
>> To a falsie value? So do you mean if you wanna set something else to false, maybe is human, or is that not?
[00:13:37]
>> Cuz I think you were saying that, if it's a falsie value with reflect it doesn't get set.
>> No, so if that sets method, in this case, returns not a truthy value, for example, here. Actually, let me find that, we throw an error here which of course is a falsie value.
[00:13:59]
It stops the execution there. So it will not forward the command to the actual target object. The value itself can be false, but we cannot return a falsie value from this method. So if I do return false here, I mean after everything, even maybe after here or anything false.
[00:14:19]
Maybe it doesn't have to be explicitly false, it can just be undefined, the request will not get a pass to the target object. But, of course, the value itself can be false, as long as we don't return a falsie value from this specific method. Because it's not that the returned value of the set method is what gets passed to the target objects, that's not the case.
[00:14:44]
That would have been maybe with middleware, the return value from middleware gets passed to the endpoint. But with the proxy, that's not the case. The only thing that a proxy does is within this method, we can change the property. So we can just say, okay, the set method is now returned to value to the target object.
[00:15:04]
No, that's not what a proxy does, we have to explicitly update the target object with that value, if that makes more sense. Cuz I think that's maybe the confusion where with middleware that's what happens, but that's not the case with the proxy. So if it's false, maybe it's some target.is human is false or is the value in that case is false, that's totally possible.
[00:15:25]
That's not a problem at all, as long as then it returns true. [LAUGH]
>> Yep, they said that clears things up, so thanks.
>> Okay, cool.
>> So you said if you're using reflect, you don't have to do that return true, it'll just return truthy if it's able.
[00:15:39]
>> Exactly, yeah, reflect set returns a truthy value, so you don't have to worry about that
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops