La herencia prototípica de JavaScript explicada mediante CSS

La herencia de prototipos es posiblemente el aspecto menos entendido de JavaScript. Bueno, la buena noticia es que si comprende cómo funciona CSS, también puede comprender cómo funcionan los prototipos de JavaScript.

Es hermoso cuando algo simple es capaz de explicar algo aparentemente complejo, una analogía, como un martillo clava un poste profundamente en el suelo, una analogía lleva el punto a casa.

Soy un amante de las analogías, una analogía.

Aquí vamos.

Prototipos en botones CSS

¿Ves los dos botones de arriba? Los diseñaremos en CSS.

Sigamos adelante y escribamos rápidamente estilos para estos dos botones, comenzando con .btn

.btn { min-width: 135px; min-height: 45px; font-family: ‘Avenir Next’, sans-serif; font-size: 18px; font-weight: bold; letter-spacing: 1.3px; color: #4D815B; background-color: #FFF; border: 2px solid #4D815B; border-radius: 4px; padding: 5px 20px; cursor: pointer;}

Es un bloque de código CSS razonablemente simple.

Ahora pasemos a los estilos para .btn-solid

.btn-solid { min-width: 135px; min-height: 45px; font-family: ‘Avenir Next’, sans-serif; font-size: 18px; font-weight: bold; letter-spacing: 1.3px; color: #FFF; background-color: #4D815B; border: 2px solid #4D815B; border-radius: 4px; padding: 5px 20px; cursor: pointer;}

Como ya habrás notado, aparte de los en negrita, todos los demás estilos .btn-solidson idénticos al de .btn. Y si está familiarizado con Sass, es posible que sepa que los .btn-solidestilos se pueden reescribir en SASS así:

.btn-solid { @extend .btn; color: #FFF; background-color: #4D815B;}

Como puede ver, .btn-solidhereda estilos de .btn, luego anula algunos de ellos (fuente y color de fondo) para crearse a sí mismo. Lo que nos lleva a la conclusión de que.btn-solides una versión especializada de .btn. O, en otras palabras, .btn-solides .btnpero con diferentes colores de fuente y fondo. Eso tiene sentido, ¿verdad? Pero espera hay mas.

Digamos que queremos crear un botón grande, .btn-lg. Lo usaremos .btncomo prototipo para suministrar estilos base. Luego, de manera similar a cómo modificamos el fondo y los colores de fuente para crear .btn-solid, modificaremos la propiedad de tamaño de fuente a un valor mayor para crear un botón más grande.

Ambos .btn-lgy .btn-solidson versiones especializadas de .btn. .btnproporciona estilos base .btn-lgy .btn-solidque luego sobrescriben algunos de los estilos base para crearlos. Esto nos dice que un solo botón que decidamos, .btnen nuestro caso, se puede usar como proveedor de estilos base para múltiples artículos.

En esta sección, intentamos definir el concepto de prototipos para botones CSS. Un prototipo es una entidad que proporciona estilos base, que se pueden ampliar para crear diferentes instancias de botones. Esta definición de un prototipo está muy cerca de lo que realmente significan los prototipos en términos de programación.

En términos de programación, un prototipo es un objeto que proporciona un comportamiento básico a un segundo objeto. El segundo objeto luego amplía este comportamiento base para formar su propia especialización. Veamos en la siguiente sección cómo nuestro conocimiento de prototipos en botones CSS se asigna a JavaScript.

Prototipos en JavaScript

Considere el siguiente objeto de JavaScript:

let obj = { a: 1};

Sabemos que ase puede acceder al valor de obj.a, dado que aclaramente es una propiedad de obj. Pero hay más, también puede llamar obj.hasOwnProperty('a')para verificar si objrealmente tiene una propiedad nombrada a.

Ahora espere un segundo, por lo que podemos ver, objno tiene hasOwnPropertydefinida ninguna propiedad llamada . De donde hasOwnPropertyvino Para responder a esta pregunta tendremos que volver a los botones que acabamos de terminar de crear.

.btn-solidsolo tiene definidos los colores de fondo y fuente. ¿De dónde viene, por ejemplo border-radius? Sabemos que .btn-solides una especialización de .btn, por lo que podemos ver que .btn-solidestá recibiendo estilos como border-radius, width, height, y paddingde .btn. ¿Podría ser lo mismo con obj?

Al igual que .btn-solidy .btn-lgobtienen sus estilos base de .btn, objo cualquier otro objeto JavaScript para el caso, reciben su comportamiento base de otro objeto -Object.prototype . Y esto lo Object.prototypeha hasOwnPropertydefinido. Y como resultado, esto da objacceso al hasOwnPropertymétodo, al igual que .btn-solidtenía acceso a .btnla border-radiuspropiedad de.

Esto, un objeto (obj) que hereda sus propiedades y comportamiento base de otro objeto (Object.prototype), es lo que llamamos herencia prototípica. Tenga en cuenta que no hay nada classinvolucrado en la interacción.

El funcionamiento interno real de los prototipos de JavaScript y nuestros “prototipos” de CSS son muy diferentes. Pero para el propósito de nuestra analogía, podemos ignorar cómo funcionan detrás de escena.

Object.prototypeno es el único prototipo disponible en JavaScript. Hay Array.prototype, Function.prototype, Number.prototypey varios otros. El trabajo de todos estos prototipos es proporcionar comportamiento básico o métodos de utilidad a sus instancias.

Por ejemplo, cada matriz declarada en JavaScript tiene acceso a .push, .sort, .forEach, y .mapsólo debido a la vinculación de prototipos. Y por la misma razón, cada función tiene acceso a .call, .apply, .bind.

Los prototipos y la herencia de prototipos no son específicos de JavaScript. Son una construcción que JavaScript usa internamente y que nos permite usarla en nuestros propios programas. Antes de ver cómo podemos hacer eso exactamente, debemos comprender qué es el encadenamiento de prototipos.

Encadenamiento de prototipos

Tendremos que volver a la analogía de los botones por una vez. Digamos que quiero crear un botón sólido grande .btn-solid-lg:

Los estilos base .btn-solid-lgson proporcionados por .btn-solidy .btn-solid-lgsobrescribe la propiedad font-size para crearse a sí mismo.

Sin embargo, échale un vistazo más de cerca. .btn-solidtiene solo dos estilos de color de fondo y color (fuente) definidos en él. Esto significa que .btn-solid-lgtiene solo 3 estilos para sí mismo: color de fondo, color y tamaño de fuente. ¿Dónde están width, height, border-radiusviniendo?

Ok, aquí tienes una pista:

If you wanted to create an extra large button .btn-solid-xlg you could do so with .btn-solid-lg as prototype. But how does all of this map to JavaScript?

In JavaScript, you’re allowed to create prototype chains too. Once you understand this, you unlock a whole set of tools to write amazingly powerful code. Yes, amazingly powerful.

Let’s see how prototype chains in JavaScript work.

Remember the object we created in the previous section? The one we carefully named obj? Did you know that you can create as many objects as you want with obj as a prototype?

Object.create lets you create a new object from a specified prototype object. This means that you can create another object, obj2, which has obj as its first prototype:

let obj2 = Object.create(obj);
// Add a property 'b' to obj2obj2.b = 2;

If you have been following so far, you should realize that even though obj2 doesn’t have a property a defined on it, doing console.log(obj2.a) won’t result in an error, but instead 1 getting logged to the console. Kind of like this:

When obj2 looks for a, it first searches its own properties. If it can’t find the corresponding property, it asks its prototype (obj), where it finally finds a. If such was the case that it still couldn’t find a, the search would continue up the prototype chain until it reaches the last link, Object.prototype.

On the other hand, if a was defined on obj2, it would override all other as if defined on any of its prototypes. Similar to how .btn-solid overrode .btn's color and background-color properties. This is called property overshadowing.

But what about the length of prototype chain? Is there a limit?

There’s no limit to the length of prototype chain. There also aren’t any limits on branching. This means you can create multiple instances with Object.prototype, obj, or obj2 as prototype.

So how will this new knowledge of prototypes and prototypal chaining help you write better code?

Writing better code with Prototypes

The goal of this article was to explain to you what prototypes are, and how prototypal inheritance works. I hope I’ve succeeded in this.

For this last section, I’ll allow myself to go on a little rant. I hope you don’t mind.

If you look at the JavaScript code available online — whether in open source projects on Github or in pens on Codepen — you’ll find that a majority of them use the constructor pattern for creating objects.

function Circle(radius) { this.radius = radius;}
Circle.prototype.area = function() { return Math.PI * this.radius * this.radius;}
// Constructor pattern for creating new objectslet circ = new Circle(5);

The constructor pattern looks like classes. In the early days, when JavaScript was far less popular than what it is today, the new keyword was added as a marketing strategy.

This indirection was intended to make JavaScript seem more familiar to classically trained programmers. Though it’s debatable how successful it was in doing so, it unintentionally also obscured the true prototypal nature of the language.

The reality is that although constructors look like classes, they don’t behave like classes at all. In JavaScript, there are objects, and objects extending from other objects. Constructors and classes never come into picture. The constructor pattern unnecessarily complicates things, there’s a lot that happens behind the scenes.

I implore you — now that you have a solid understanding of prototypes — to stop using the constructor pattern.

Why not do this instead?

let Circle = { create(radius) { // Creating prototypal linkage using Object.create let obj = Object.create(this); obj.radius = radius; return obj; }, area() { return Math.PI * this.radius * this.radius; }};
let circ = Circle.create(5);

I hope this analogy has helped you better understand prototypes, prototypal chaining and prototypal inheritance with Object.create. Now you can write better code, and stop using pretentious classes.

Thanks for reading! If my article was helpful, click the little green heart below to recommend it, and please share this with your fellow devs.

And for further reading, check out Aadit Shah’s Why Prototypal Inheritance Matters.

Looking for more? I publish regularly on my blog at nashvail.me. See you there, have a good one!