I think I finally “get” JS objects

Taylor Hunt - May 30 '23 - - Dev Community

While learning JavaScript, at some point I looked up what “object-oriented” meant.

That was a mistake.

The first reason it was a mistake was because I was exposed to way too many unhelpful OOP diagrams. You know the kind:

A diagram about class vs. instance vs. extensions that looks like it was drawn in Powerpoint. It’s about faces and hats or something; if you really want to read the text, this links to the original SVG.

This diagram from Wikimedia Commons is typical: it models something you’d never code in a real program. (Why is that so common?)

The other reason it was a mistake was because it taught me about Java and C++ programmers’ opinions on objects… but those opinions barely mattered to the language I was learning. I read about “information hiding”, “encapsulation”, and “polymorphism”, but I didn’t understand how they were relevant.

And as it were, they mostly weren’t relevant!

  • Some jargon was for techniques difficult in other languages, but so easy in JavaScript that you don’t even think about them.

  • Some jargon was for how low-level languages would optimize performance, in ways that JavaScript is too high-level to care about.

  • Some jargon described techniques that JavaScript objects can’t even do.

  • And, lastly, some jargon was for bad ideas — even in the originating languages.

For years, I was dimly aware that it seemed unnecessary to mess with prototypes, classes, constructors, and this for like, 98% of my code — so why was I expected to use them for everything, or I wasn’t doing object-orientation “properly”?

It turns out object-oriented code is great at some things, but not all things

What I’ve since realized is that JavaScript reuses its full-power Objects for many non-OO purposes, most of which are much simpler. I’ve used JS objects for roughly 4 things:

  1. Grouping variables
  2. Namespacing
  3. Typed-ish data
  4. Actual full-power Objects

The first 3 aren’t “real” object-orientation (or whatever that means), but they reuse the object machinery JavaScript already has because it’s convenient.

1. Grouping variables

Sometimes, you want to collect related variables together. Other languages features for that are called dictionaries, hashes, maps, or structs. Perl, being Perl, has 6 ways to do it, with names like “BagHash”.

It’s like prefixing variable names that go together:

var player_x = 0;
var player_y = 0;
var player_health = 100;

// or:
var player = {
 x: 0,
 y: 0,
 health: 100
};
Enter fullscreen mode Exit fullscreen mode

Usually the specifics of how exactly to group variables in other languages is for performance reasons. For example, C’s structs group values together to make them efficient byte-wise, but if you want to look up the properties at runtime by name, you’ll need a hash function/table instead.

These sorts of grouped variables as objects are also used to 1.) return multiple values, 2.) implement optional arguments in a friendly way, and 3.) other places where the language doesn’t support passing around multiple variables at once. As a counterexample, Python has optional keyword function arguments and multiple return values, so it doesn’t use objects for either. But in JavaScript, we use object and object accessories for both:

function getCoordinates (
  target,
  { relativeTo } // Using object destructuring for optional arguments
) {
  let ret = { x: 0, y: 0, relativeTo };
  if (relativeTo) {
    ret.x = getHorizontalOffset(target, relativeTo[0]);
    ret.y = getVerticalOffset(target, relativeTo[1]);
  } else {
    ret.x = getHorizontalOffset(target);
  }

  return ret; // Returning 3 related variables in the same object
}
Enter fullscreen mode Exit fullscreen mode

2. Namespacing

There’s no technical reason that the Math object exists. But there is a human reason: it’s easy to remember math-related stuff lives in this object.

They could be separate identifiers with math_ prefixes — older languages did that all the time. But this seems a little cleaner; contrast the myriad functions in PHP’s global scope.

Modern-day JavaScript now handles namespacing with module imports and exports, but you can still wrap those up into an object, the syntax is similar, and many old browser APIs and libraries still use objects for namespacing.

(This is also known as static properties/methods, which kind of sucks as a name and infected JS from other, less fun programming languages. Go @ TC39 about it.)

3. Typed-ish data objects

Typed-ish data objects (a very rigorous term I just made up) bundle data with ways to read and manipulate said data, like Date, DOMRect, or Array.

  • Typed-ish data objects are when this and methods become useful — methods that don’t use this are just namespaced functions.
  • I think this is the level where instanceof would become useful, but JS/DOM design mistakes popularized duck typing in the JavaScript world anyway.

These objects let you change their data in different ways, and the different ways to read that data will update to match your changes:

var d = new Date();
d.toDateString(); // "Sat Apr 11 2020"
d.setMonth(7);
d.toDateString(); // "Tue Aug 11 2020"
Enter fullscreen mode Exit fullscreen mode

These kinds of objects can use (and often do use) information-hiding/internal representations, but it’s not required. For example, a Date only stores one UNIX timestamp as an integer, and it even lets you look at and change that integer directly if you want. And as for TypedArray, well, it’s in the name.

For example, a Color object where the constructor takes any string that CSS can parse as a color, then expands it into an object with R, G, B, and A properties. (This part is left as an exercise to the reader, because it’s surprisingly hard.)

At first, an object shaped like the following:

{
  R: 255,
  G: 40,
  B: 177,
  A: 255
}
Enter fullscreen mode Exit fullscreen mode

…doesn’t seem that much more useful than using hex codes directly in JS like 0xff28b1ff. But by making an actual color Object you can new up, we can add useful features, such as:

  • We could enforce that R, G, and B never exceed 255 or go lower than 0 with a setter.
  • It could have tint() and shade() methods that are easier to understand than manual RGB tinkering.
  • The Color object could return the numeric equivalent of its hexadecimal code for its .valueOf(), output a human-friendly hsla() syntax for its .toString(), or output to an array for data transfer with .toJSON().

4. Full-force capital-O internal state Objects, also known as Art

Finally, you have the full-force objects that act as discrete… well, objects. Like DOM elements, Document, and so on. They use the hell out of this, and need to. (It’s possible to model these sorts of things in functional programming, but it involves trying to avoid this so hard you end up inventing that.1)

HTMLInputElement.validity.validate(), though, is very good. It uses a bunch of properties on one specific user-visible object, then changes what the element is doing to reflect its calculations. And if you call the .focus() method on one <input>, it changes the properties of others and the activeElement property of the document that owns them all.

For an even more complex example, consider jQuery objects and their seemingly-simple API:

$('.widget').addClass('loaded').fadeIn().on('click', activateWidget);
Enter fullscreen mode Exit fullscreen mode

There is a great deal going on under the hood there. But because jQuery is doing those object operations under the hood, it turns into a wonderful API that has stood the test of time.

Anyway, I’m not a good enough programmer to really understand or work on level 4 yet.

So what?

My first draft originally tried to tie this all into a lesson for readers, but I suspect this post is too confusing for beginners and also too mundane for advanced devs, so I’m publishing it for me. As a treat.


  1. No, I will not explain what I mean. 

. . . . . . . . . . . . .
Terabox Video Player