Check out a free preview of the full Deep JavaScript Foundations, v3 course:
The "Abstract Operations" Lesson is part of the full, Deep JavaScript Foundations, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Kyle introduces the operations JavaScript uses under the hood to perform coercion, starting with toPrimitive. - https://static.frontendmasters.com/resources/2019-03-07-deep-javascript-v2/deep-js-foundations-v2.pdf

Get Unlimited Access Now

Transcript from the "Abstract Operations" Lesson

[00:00:00]
>> Kyle Simpson: So earlier, we described, and I mentioned a thing called an abstract operation. If you recall, I showed you the two number, and I said that's an abstract operation. So let's talk about what those are, because they are the fundamental building block that makes up how we deal with type conversion.

[00:00:17] So you see here in the spec that we have these abstract operations which perform, fundamentally, the task of type conversion. In JavaScript, we refer to type conversion as coercion. Okay? So when you think of conversion and coercion, you should really think of them as interchangeable, at least as far as JavaScript is concerned.

[00:00:37] The first abstract operation that we have is called ToPrimitive. Now obviously, if we don't have a primitive, we need to turn it into a primitive. So if we have something non-primitive, like one of the object types, like an object, an array, a function, whatever, and we need to make it into a primitive, this is the abstract operation that's going to be involved in doing that.

[00:01:00] By the way, the abstract operations are not a thing the JavaScript engine, they're not like a function that could somehow be called. There may, in fact, be actual methods inside of a JavaScript engine or not, they're not like required to be actual things. But when we call them abstract, we mean they're a conceptual operation.

[00:01:18] So any time you have something that is not a primitive and it needs to become a primitive, conceptually, what we need to do is this set of algorithmic steps, and that's called ToPrimitive, as if it were a function that could be invoked, okay? So the ToPrimitive abstract operation takes an optional type hint.

[00:01:37] So in other words, it says, if you have something that is not a primitive, tell me what you think you would like, what kind of type you would like it to be. If you're doing a numeric operation and it invokes ToPrimitive, guess what hint it's gonna send in?

[00:01:52] It's gonna say, I would like to have a number. That doesn't guarantee you a number, by the way. It's just a hint to say, the place that I'm using it, I would like it to be a number. If you're doing something string-based, guess what hint it's going to send in.

[00:02:04] It's going to send in string. Those are basically the only two hints. It can say, I would like it to be a number, I would like it to be a string, or I'm not going to tell you at all, so just give me whatever primitive you can. Okay?

[00:02:13] Another thing you need to understand about the algorithms within JavaScript is that they are inherently recursive, which means that they define something, for example to ToPrimitive. If the return result from ToPrimitive is not a primitive, if it's another non-primitive, like another object, then it's gonna get evoked again, and it's gonna keep getting invoked until we can get something that's an actual primitive, or in some cases get an error, okay?

[00:02:42] So there's a lot of these sort of spec machinations that happen that are recursive. And when we look at coercive equality a bit later, you'll see this idea that it's sort of being re-applied over and over and over again until we get a full result. So ToPrimitive, the way it works, essentially, boiling down the algorithm.

[00:02:58] The way it works is that there are two methods that can be available on any non-primitive. Any object, function, array, whatever. Any non-primitive, there are these two functions. And you've almost certainly seen these before. The valueOf function() and the toString() function. And this algorithm says, if you've told me that the hint is number, then I'm going to first try to invoke the valueOf(), if it's there, and see what it gives me.

[00:03:25] And if it gives me a primitive, then we're done. If it doesn't give me a primitive, or it doesn't exist, then we try the toString(). And we either get a primitive or not. And if we tried both of those, and we don't get a primitive, generally that's gonna end up resulting in an error, okay?

[00:03:42] That's if the hint was number. If the hint was string, they just reverse the order that they consult them in, but they still essentially consult both of them. So if the hint is string, we would ask for that object, that non-primitives, toString() method, and if it has it, use what it returns.

[00:03:57] And if it gives us legitimately a primitive like a string, which it should, then we'll just use that. And then we'll try to valueOf(), okay? So just keep in mind, if you're gonna use something that is not a primitive in some place that definitely needs primitives, like math or concatenation.

[00:04:17] In your mind, you should realize, it is going to end up coercing it through this ToPrimitive algorithm, and it's gonna end up either invoking the valueOf() or the toString(). We'll see examples of the usage of those in a little bit.