JavaScript: The Hard Parts, v3

Implement @@toPrimitive

Will Sentance
Codesmith
JavaScript: The Hard Parts, v3

Lesson Description

The "Implement @@toPrimitive" Lesson is part of the full, JavaScript: The Hard Parts, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Will introduces a special hidden property called Symbol.toPrimitive. This property contains a function that defines the coercion behavior, such as returning a specific number. You cannot add this property directly with dot notation, but you can assign it using the Symbol.toPrimitive key with bracket notation. This allows objects to be coerced to numbers or strings as needed, overriding the default behavior that usually results in NaN for objects.

Preview

Transcript from the "Implement @@toPrimitive" Lesson

[00:00:00]
>> Will Sentance: Let's see, can we do the same with our own objects? Well, first, actually, can we see if we can just coerce them using the unary operator here to number, or we could call it with number. So if we were to, with our back to our original data, user stored is Will and 105 as an object, user submitted is Will and 105 as an object. Can we, rather than compare them by their memory position, could we try to maybe coerce them, convert them?

[00:00:31]
We know that the three equals doesn't do any coercion, but maybe we could try and force them into a number by putting the unary in front of each of them. It ain't going to work. JavaScript just says, I'm sorry, you can't convert your object into a number, even if there's a number on the object 105, we're just going to give you NaN, our default, it wasn't a number.

[00:00:58]
And by the way, you want to see something really fun, that, by the way, is not just JavaScript, it is many programming languages, because it's actually the default way of implementing it. Not a number is a number, but every single one of them is a unique not a number. So not a number does not equal, is not the same as another not a number. And to be fair, if it were, would we really want, and that's because, this is implemented in Python, for example, in other languages, this is not just a JavaScript thing.

[00:01:29]
We don't want to imply that the underlying, I guess you could say numeric or non-numeric data that coerced into this non-number number is actually the same. So it's sort of fair to say that therefore all the not a numbers had a different journey and so treat them all as different. So we need instead to manually update the hidden at at to primitive property on our objects, and I will actually immediately point to what Joe said.

[00:02:05]
Manually update, meaning manually add an at at to primitive property on our objects with a function that states exactly how to coerce the object to numbers. And I think we're about to do that right here. Let's create a function called coerce, call it anything, you could call it return 105, but I'm going to use it in a different way later, so let's just leave it for now as coerce, whose only thing is return the number 105.

[00:02:30]
Beautiful. Let's create a function that just returns 105 that I want to attach to my user stored and my user submitted. That when we want our objects to coerce, it's going to run that coerce function. But how the heck do I get to attach it to user stored and user submitted? I can't add an at at to property directly, it's a hidden property. I can't go user stored at at to primitive and assign coerce impossible.

[00:02:56]
So how can we ever store a function on our user stored and user submitted under this at at to primitive property? We're stuck. Good. Oh, we'll see you in a second. Before we find the answer, let's do a full wipe down on the board, and then we're going to discover how to use a really fascinating, but kind of weird new way of storing data, or new data type, sorry, in JavaScript.

[00:03:27]
I always call it a way of storing data, but a data type in JavaScript that definitely ain't a string. We literally can't write it out. If we try to console log it, it just shows us a stringified version of its data type, and yet is going to be a vital way for adding hidden properties to JavaScript objects, like our ones user submitted and user stored, that we will know that JavaScript can go and look at that hidden property, find the stuff it needs to coerce, but that we will not be able to hand add.

[00:04:04]
And yet we're somehow going to need to add it by reference. Before we go, how do I add a property to an object by reference as opposed to by directly storing it using dot notation? Is there a way I can add a property name where I'm not going to hand insert it, but instead going to refer to it? What's my notation for adding, Chris? Square brackets, square brackets.

[00:04:31]
Ryan, if we can start in our global, oh no. If we can start in our global memory, just declare us the objects, just to help me as I write them, so we have some nice constructive verbalizing as we go. What are we declaring? User stored as an object with the name Will as a string and ID as 105 as an integer. Name Will, I hope you can already see I'm leaving lots of free space for hidden properties on my object.

[00:05:16]
OK, and then the next one we're declaring is user submitted, another object with the name of Will as a string. Beautiful and ID as 105 as an integer. And let's just for our purposes, show that these would technically actually be stored as references to our underlying position in memory where they are being referenced from 1001, something like that, 1002, that'll do.

[00:05:51]
OK, then we're going to have our on submit. I love the white pen so much, it's just beautiful. Our on submit function, and we're also going to define this coerce function, whose only job is return out the number 105 when it gets called. And then we are going to call, because the user clicks their, they click their submit button, you know, we're being a bit hand wavy on that, and we're going to call our on submit function, and just to make sure everybody's wide awake, we're going to create a brand new what people?

[00:06:25]
Execution context, execution context, well done everybody. In reality, we're not, for once, we're not that interested in our flow of the execution so much as how our flow of coercion is working. So into it we go, where we are going to check if our user stored, when coerced, which we saw before, to number, evaluated to not a number, when evaluated to not a number, is coerced and not a number, is equal to our user submitted.

[00:07:14]
And we saw that even with our coercion, instead of getting something useful out of user stored or user submitted, maybe a number like the 105, instead we got not a number and not a number, which we discovered are not the same. Brilliant, but we want to, and I think we can potentially take control of what that user stored, let me just make sure that's really clear, people.

[00:07:57]
The plus said convert to primitive and do so as a number, it's our unary operator, run the to number, and our user stored object converted, coerced to not a number, which is itself a sort of number, I mean it is a number, it is numeric, but without a numeric value whatever, I don't know. And they are not the same, but regardless, if they were, we wouldn't really care because it's a null, it's a not, it's a non-data specified.

[00:08:32]
It's as much defined by what it's not. OK, alright. But, we know people, that there is a way of taking control of this to primitive pipeline. We saw it with Date. When Date ran, it returned out objects that when we tried to do math on together, in this case, we're doing math of to number, and we try to run an operator, sorry, of the unary operator that kicks off to number via to primitive, when we did that with the two Date objects with the time, we managed to run this at at to primitive property that had stored on it, we didn't get to look in the function, but automatically stored on it in a function that coerced the objects to their primitive numeric value, which in this case, in that case was the value of date value, the hidden property with the number of milliseconds since the beginning of time.

[00:09:23]
And we compared them, we did math on them, we compared two objects and got 1000, pretty nice, to be honest, quite a nice interface for that object. Can we add an at at to primitive to our user stored, to our user submitted? You bet we cannot, at least manually, I cannot do user stored dot at at to primitive. This is not, this is like me doing date value, or this is like me doing square bracket scope.

[00:10:01]
I can't access this stuff. It's different to the square bracket, which are truly, you know, there's no way we can ever, I can add it, but I can't write it and I can't access it manually, so I can't write it manually like this. It's going to be a property, an identifier, a label that I can't hand write. So how the heck do I get access to it? Well it turns out, JavaScript lets us use, but not write ourselves an at at to primitive label.

[00:10:44]
All I need, and I always want to do it in a different color, let's try yellow, I think. All I need is to attach an at to primitive label here, a bit like the hidden property date value and assign a function to it. If I can somehow get access to that label, even if I can't write user stored to primitive, if I can somehow just attach a label there, even if I can't directly write it, then I can store coerce on that.

[00:11:20]
And it turns out JavaScript lets us use, even though we can't write them, our at at to primitive labels, stored in a big old built-in object called symbol. It's up here. OK, let me get this right here. So it is a big old object, built in, that has on it actually regular labels. So let me just show you this is built in by my dots around it. It has built in it labels, and those labels are, well, one of them is a regular old, don't get it twisted, a regular old string label like any other, to primitive, stored on it people, is a hidden label, we're doing them in yellow now, at at to primitive.

[00:12:19]
People, this thing is so hidden that if I wrote symbol.to primitive in my console and said console log symbol.to primitive. I don't see at at to primitive, I get reflected back to me, a stringified version, I get, actually I get, for what it's worth, I get symbol. Parens, symbol, to primitive, i.e., the label. I cannot see this new data type that we'll hear the name of in a moment, but I can refer to it.

[00:13:03]
Chris, how could I refer to this hidden label that is being stored on a regular label on an object? What could I use, how do I go and refer to this hidden label, Chris? It's a property on symbol. Ah, yeah, yeah, so what would I say to get access to that hidden label? Symbol, square bracket. Well, well, hold on, symbol dot, yeah, well done to Chris. Symbol dot what?

[00:13:37]
To primitive. And then I know that that would evaluate as any other property on an object will to its value, which is, well, we can't even see it in the console, but we know it's this at at to primitive, hidden label. So let's do that, our user stored. We are going to use square brackets notation to go and grab this label via accessing the object by name of symbol, its property name to primitive, and then we're going to assign to that what function, Chris?

[00:14:17]
Coerce. Coerce, exactly, the coerce function. User stored. Go look for symbol.to primitive, what's stored there. Now we can see this on the blackboard, but know that in our console, for example, we don't even get to see this. We will just, if we were to console log symbol.to primitive, we basically get symbol.to primitive. That's it. But this is going to allow us to say on user stored, attach this at at to primitive, hidden property.

[00:14:54]
We get to have access to a list, in fact, there's a whole bunch of labeled hidden properties on this symbol object that we can attach to our objects. That give us fine-grained control over what these objects do when JavaScript, for example, tries to coerce them into primitives. So what's the function we're attaching everyone together, that we're attaching to our user stored?

[00:15:29]
Coerce, coerce, exactly, there it is. The function coerce. OK, and then we do the same thing below, user submitted. Well, let's just do it again, just so we're really sure. Yes, so we're really sure, oh my goodness. OK, why am I doing this again? Symbol.to primitive. We go again looking for that. We go again looking for that label here. And just like a string, if we had a label name, that string is unique, it's the only letter's name.

[00:16:17]
So too here. We have our unique label, there it is, and we attach it to our, on our user submitted object, and we have the coerce function stored, and so now, we hit our on submit. Everyone together so that we all feel invigorated, a brand new what everybody? Execution, thank you everybody, and into it we go where we're going to assess if our user stored coerced to number via our unary operator is equal to our user submitted, coerced to, sometimes when I'm pointing out, when I'm talking at the board really loudly, I'm like, Jesus Christ, turn the volume down.

[00:17:00]
The plus, the unary operator is going to kick off our to primitive. But whereas before, the built in for our object coerced it to NaN, not a number, for our plus, meaning to primitive with number, and we'll see how to adjust for whether number or string, but for now, you know, we don't even really care, we're just going to kick off the to primitive function. Now, we know that JavaScript says user stored is going to hit the to primitive with the unary operator.

[00:17:46]
Oh, what do I do? Off I go, let me check, is there a hidden property at at to primitive? And you bet there is. And so we grab our hidden property and we execute our coerce function. New execution context, its only output, Chris, is what? 105. 105. Same thing on the other side. Check out the at at to primitive and find the coerce function, its only output is 105.

[00:00:00]
Is 105 people, strictly equal to 105. Everyone together? Yes, hooray! Flipping heck, it's true. Yay. When JavaScript tries to coerce our objects to primitive, the coerce functions will run, and each will return 105. We have taken manual control of our object to primitive coercion pipeline, using this remarkable built-in feature.

Learn Straight from the Experts Who Shape the Modern Web

  • 250+
    In-depth Courses
  • Industry Leading Experts
  • 24
    Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now