Guest post: Object.prototype.valueOf, Coercion, and Comparison Hackery in JavaScript

The talented Clark Feusier wrote this guest post on primitive values versus objects, type coercion, and Object.prototype.valueOf. Be sure to check out his other writings here


In this post, we will cover some fundamental JavaScript concepts — primitive values versus objects, type coercion, and Object.prototype.valueOf. With these fundamentals in hand, we will create a custom .valueOf method which will hack weird behavior into the comparison and logic operators — this will give us a chance to see the power and danger of our new knowledge. Let's get started!


What is .valueOf?

.valueOf is a method that lives on Object.prototype and returns a primitive value representation of the object on which .valueOf is invoked

Let's break this specification down for clarity.

First, by default, the .valueOf method is inherited by every object which is a descendent of Object — that is, any object that has failed property lookup delegated to Object.prototype.

Second, .valueOf converts an object representation into a primitive value representation of the given object. The syntax for .valueOf is anyObject.valueOf().

Big deal, just another JavaScript method, you say? Yes, .valueOf is just a typical JavaScript method; however, .valueOf is a very special method for the following reason:

JavaScript automatically invokes [the valueOf method] when encountering an object where a primitive value is expected (source)

That is to say, .valueOf is ubiquitous throughout your programs, whether you are aware of it or not. Now that you are aware, you might be asking which contexts cause JavaScript to 'expect' a primitive value when encountering an object? Or even, what are the differences between primitive values and objects in JavaScript? Let's address those questions now!


Primitives and Objects in JavaScript

In most languages (not just programming languages), there is a distinction between the language's primitive value types and the complex data structures composed of primitive values.

For example, ignoring all of the fun stuff in between phoneme and meaning, one could consider the sounds made by a human while speaking to be primitive values, which are being combined into complex representations like sentences, of which humans can interpret the semantics.

Or, think about the primitive values in mathematics like numbers and operators, which are combined into complex expressions that represent complex data structures, as well as provide behavior.

In JavaScript, this distinction is partially captured in the difference between the primitive value types and the JavaScript Object.

Primitive Values

A primitive value is a datum that is represented directly at the lowest level of the language implementation (source)

The primitive value types in JavaScript are Undefined, Null, Boolean, Number, and String.

A primitive value of type Undefined can only be undefined. A Boolean type primitive value could be true or false. A Number type primitive value could be anything like 1, 200000, 16.24, or -79. A primitive value of type String could be anything like '', "HEY!", or "i am a string".

Objects

An object is a collection of properties and has a single prototype object (source)

This means that an object is a mapping between keys and values, and that an object has an object where it looks for extra properties (usually behavior).

JavaScript uses objects to wrap around primitive values so that the values have extra behavior — this can be especially useful for the interpreter at runtime.

Why does this matter? or, the Payoff

JavaScript will convert data representations between primitive values and objects depending on the context

For example, when the interpreter needs to evaluate the truth or falsity (1 or 0) of a logical expression comparing two Number objects, it can convert the Number objects into two primitive values, which it can then use to compare via the lower level logic and number constraints on expressions as determined by the implementation of the JavaScript interpreter.

Understanding these differences between 5 and new Number(5) will allows us to avoid a lot of the quirks that bite most people who are new to JavaScript in their collective asses. One of the quirks that we are now fully suited to explain is coercion — how JavaScript converts representations of one type into representations of another type when operations are run on representations of disparate type. Let's explain how JavaScript uses coercion to powerful, albeit risky, ends.

Coercion in JavaScript

We can say that, generally, coercion happens in JavaScript when the interpreter needs to evaluate something of one type as a different type. For example, a boolean value, when used in a mathematical operation is coerced into a number. Or, a string value when compared with a number value will be coerced into a number when it is evaluated. Note: only the value on the left-side is coerced in the following (and all such expressions).

// boolean in mathematical expression
true + 9 // 10

// string in comparison with a number
"26" == 26 // true

At this point, you might be thinking that I have forgotten the original topic — never fear, here comes another kicker.

How is the coercion accomplished at runtime? Beneath the covers, JavaScript uses .valueOf() to coerce any values that need coercion — stubborn values.

In our first example above, at runtime, JavaScript applies .valueOf to the boolean value true. The Boolean.prototype object has a .valueOf method which returns primitive value 0 for the false boolean object and the primitive value 1 for the true boolean object. All of the built-in object types like Boolean, Number, and String override .valueOf in order to return a representation that is suitable for the type.

Let's try to sum up this section and its implications.

  1. JavaScript coerces objects into primitive values using the .valueOf method defined on the prototype of the given object type
    • Note: in string contexts, objects are coerced into primitive string values via .toString instead of valueOf
  2. On the flip-side, JavaScript coerces (wraps) primitive values into objects using ToObject
    • ToObject isn't the topic of our discussion, but for our purposes you can consider it the reverse of valueOf
  3. Each object type can provide its own .valueOf method, as seen by Boolean above

From the above summary, we can take our knowledge and leave theory-world. Let's use the above points to play around with writing our own custom .valueOf methods!


How to Write a Custom .valueOf Method

So, let's say that we have a custom object type called MyCustomObject.

function MyCustomObject() {}

var exampleObj = new MyCustomObject();  
exampleObj.valueOf(); // [object Object]  

Because our object has the default .valueOf, which isn't returning a primitive value, .valueOf will return the object itself. But, we don't want that. We want instances of MyCustomObject to have a better primitive value representation (it has low self-esteem)!

Let's override MyCustomObject.prototype.valueOf to return a custom value.

Note: we will actually be overriding Object.prototype.valueOf, which MyCustomObject has access to via failed-property-lookup delegation.

MyCustomObject.prototype.valueOf = function() {  
  return "never generic, always custom";
};

Now, if we wanted to repeat our example from above, we would get a different result.

var exampleObj2 = new MyCustomObject();  
exampleObj2.valueOf(); // 'never generic, always custom'  

One final note — your custom .valueOf doesn't take any arguments because it should be a getter.

Ok, so we now know quite a bit about the .valueOf method, primitive values and objects, and coercion in JavaScript. Let's close this post with a code example. We are going to hack comparison operators for a custom object — hopefully we will solidify our earlier learning, as well as demonstrate the power, danger, vanity, and possible stupidity of custom .valueOf methods.


Code Example: The Power and Danger of Custom .valueOf Methods

A somewhat common (read annoying) interview question is the following:

Provide an object that would make the following expression evaluate to true:
(mysteryObject < 1) && (mysteryObject > 1)

The question isn't one that I would ask in an interview, but some consider it a test of a candidate's knowledge of valueOf, closures, and immediately-invoked function expressions (IIFEs). But now that we know about .valueOf, we can solve this question relatively simply.

We know that when the interpreter encounters the comparison of mysteryObject < 1 and mysteryObject > 1, the mysteryObject is coerced into a primitive value with .valueOf before being compared to the number value on the right-side of the operator. We exploit this coercion in the following solution (credit to Rao for pointing me to this solution).

var mysteryObject = (function() {  
  var cachedValue = -2;
  return {
    valueOf: function() { return cachedValue += 2; }
  };
})();

(mysteryObject < 1) && (mysteryObject > 1) // true

We are assigning the result of an IIFE to our mysteryObject. The IIFE returns an object which has a custom .valueOf method — the returned object is where the interepreter will first look for a valueOf property before looking into the Object.prototype for the default valueOf function.

Our mysteryObject's custom valueOf method now increments the cachedValue variable by 2 and returns the new value everytime that valueOf is invoked on our mysteryObject.

When the interpreter evaluates mysteryObject < 1, the custom valueOf function will increment the cachedValue from -2 to 0. Because 0 is less than 1, the expression will evaluate to true.

Now, when the interpreter evaluates mysteryObject > 1, valueOf will increment the cachedValue from 0 to 2. Because 2 is greater than 1, the expression will evaluate to true. Two true values joined by a logical conjunction always evaluate to true. We won!

We can see the power and danger of writing a custom valueOf method — we just made the greater than/less than comparison operators and the logical && operator behave illogically!

Let's summarize!


Summary

  • Primitive values are represented at a primitive level of language implementation
  • Objects are represented at a higher-level of abstraction than primitive values (offering properties and behavior to the data)
  • JavaScript coerces primitive values into object representations and vice versa, depending on context
  • Primitive values are coerced into objects with ToObject
  • Objects are coerced into primitive values with either valueOf or toString depending on context
  • Overriding an object's valueOf method is as simple as defining a new valueOf method for the object, e.g., object.valueOf = function() {};
  • Override valueOf with caution and respect for side-effects

Next time you need to get the primitive value representation of an object, remember .valueOf, and think fondly of me.


Clark Feusier is a software engineer with a background in metalogic and product management. Clark's passion is the intersection between computer science, cognitive science, semantics, and programming -- his current focus is on harnessing the power of web technology. Clark's writings and projects are located at clarkfeusier.com.

comments powered by Disqus