JavaScript's prototypiske arv forklaret ved hjælp af CSS

Prototypisk arv er uden tvivl det mindst forståede aspekt af JavaScript. Den gode nyhed er, at hvis du forstår, hvordan CSS fungerer, kan du også forstå, hvordan JavaScript-prototyper fungerer.

Det er smukt, når noget simpelt er i stand til at forklare noget tilsyneladende komplekst, en analogi - ligesom en stablet driver driver en pol dybt ned i jorden, driver en analogi punktet hjem.

Jeg er en elsker af analogier, en analogfil.

Nu sker det.

Prototyper i CSS-knapper

Se de to knapper ovenfor? Vi skal designe dem i CSS.

Lad os gå videre og hurtigt skrive stilarter til disse to knapper, begyndende med .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;}

Det er en rimelig simpel blok af CSS-kode.

Lad os nu gå til typografierne for .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;}

Som du måske allerede har bemærket, bortset fra de dristige, er alle de andre stilarter .btn-solididentiske med den for .btn. Og hvis du er fortrolig med Sass, ved du måske, at .btn-solidstilarter kan omskrives i SASS sådan:

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

Som du kan se, .btn-solidarver stilarter fra .btnog tilsidesætter derefter nogle af dem (skrifttype og baggrundsfarve) for at skabe sig selv. Hvilket fører os til den konklusion, at.btn-solider en specialudgave af .btn. Eller med andre ord, .btn-solider .btn, men med forskellige skrifttyper og baggrundsfarver. Det giver mening, ikke? Men vent der er mere.

Lad os sige, at vi vil oprette en større knap .btn-lg. Vi bruger .btnsom en prototype til at levere basestilarter. Derefter, i lighed med hvordan vi ændrede baggrunds- og skrifttypefarver for at oprette .btn-solid, ændrer vi fontstørrelsesegenskaben til en større værdi for at oprette en større knap.

Begge .btn-lgog .btn-solider specialiserede versioner af .btn. .btnleverer basestilarter til .btn-lgog .btn-solidsom derefter overskriver nogle af basestilarterne for at skabe sig selv. Dette fortæller os, at en enkelt knap, som vi beslutter - .btni vores tilfælde - kan bruges som leverandør af basisstile til flere varer.

I dette afsnit forsøgte vi at definere begrebet prototyper til CSS-knapper. En prototype er en enhed, der leverer basisformater, som kan udvides til at skabe forskellige forekomster af knapper. Denne definition af en prototype er meget tæt på, hvad prototyper faktisk betyder i programmeringsbetingelser.

I programmeringsbetingelser er en prototype et objekt, der leverer basisadfærd til et andet objekt. Det andet objekt udvider derefter denne basisadfærd til at danne sin egen specialisering. Lad os se i det næste afsnit, hvordan vores viden om prototyper i CSS-knapper kortlægges til JavaScript.

Prototyper i JavaScript

Overvej følgende JavaScript-objekt:

let obj = { a: 1};

Vi ved, at værdien af akan tilgås af obj.a, givet det aklart er en egenskab af obj. Men der er mere, du kan også ringe for obj.hasOwnProperty('a')at kontrollere, om der objfaktisk er en ejendom med navnet a.

Vent nu et øjeblik - fra hvad vi kan se, objer der ikke hasOwnPropertydefineret nogen egenskab kaldet på det. Hvor hasOwnPropertykom der fra? For at besvare dette spørgsmål bliver vi nødt til at gå tilbage til de knapper, vi lige er færdige med at oprette.

.btn-solidhar bare defineret baggrunds- og skrifttypefarver. Hvor kommer det for eksempel border-radiusfra? Vi ved, at .btn-solider en specialisering af .btn, så vi kan se, at .btn-solidder er få stilarter som border-radius, width, height, og paddingfra .btn. Kunne det være det samme med obj?

Ligesom .btn-solidog .btn-lgfå deres basisstile fra .btn, objeller ethvert andet JavaScript-objekt for den sags skyld, modtage deres basisadfærd fra et andet objekt -Object.prototype . Og dette Object.prototypehar hasOwnPropertydefineret det. Og som et resultat, dette giver objadgang til den hasOwnPropertymetode - ligesom .btn-solidhaft adgang til .btn's border-radiusejendom.

Dette - et objekt (obj), der arver dets egenskaber og baseadfærd fra et andet objekt (Object.prototype) - er det, vi kalder prototypisk arv. Bemærk, at der ikke er nogen classinvolveret i samspillet.

The actual inner workings of JavaScript prototypes and our CSS “prototypes” are way different. But for the purpose of our analogy, we can ignore how they work behind the scenes.

Object.prototype isn’t the only prototype available in JavaScript. There’s Array.prototype, Function.prototype, Number.prototype and several others. The job of all these prototypes is to supply base behavior or utility methods to their instances.

For example every array declared in JavaScript has access to .push, .sort, .forEach, and .map only because of the prototypal linkage. And for the same reason every function has access to .call, .apply, .bind.

Prototyper og prototypearv er ikke JavaScript-specifikke. De er en konstruktion, som JavaScript bruger internt, der giver os mulighed for at bruge den i vores egne programmer. Før vi ser på, hvordan vi nøjagtigt kan gøre det, er vi nødt til at forstå, hvad prototypeforbindelse er.

Prototypisk lænkning

Vi bliver nødt til at komme tilbage til knappeanalogien en gang. Lad os sige, at jeg vil oprette en stor solid knap .btn-solid-lg:

Basestilene, .btn-solid-lgder leveres af .btn-solid, og .btn-solid-lgoverskriver egenskaben for skriftstørrelse for at oprette sig selv.

Se dog nærmere på. .btn-solidhar kun to stilarter baggrundsfarve og farve (skrifttype) defineret på den. Dette betyder .btn-solid-lghar kun 3 stilarter for sig selv: baggrundsfarve, farve og skriftstørrelse. Hvor width, height, border-radiuskommer fra?

Ok, her er et tip:

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.

Og for yderligere læsning, se Aadit Shahs Why Prototypal Arv Matters.

Leder du efter mere? Jeg offentliggør regelmæssigt på min blog på nashvail.me. Vi ses der, har en god!