Objektumorientált programozás JavaScript-ben - példákkal elmagyarázva

A JavaScript nem osztályalapú objektum-orientált nyelv. De még mindig vannak módjai az objektum-orientált programozás (OOP) használatára.

Ebben az oktatóanyagban elmagyarázom az OOP-t és megmutatom, hogyan kell használni.

A Wikipedia szerint az osztályalapú programozás az

az objektum-orientált programozás (OOP) stílusa, amelyben az öröklődés az objektumok osztályainak meghatározásán keresztül történik, ahelyett, hogy az öröklés csak az objektumokon keresztül történne

Az OOP legnépszerűbb modellje az osztályalapú.

De mint említettem, a JavaScript nem osztályozott alapú langauge, hanem prototípus-alapú langauge.

A Mozilla dokumentációja szerint:

A prototípus-alapú nyelv egy prototípusos objektum fogalmát képezi, egy olyan objektumot, amelyet sablonként használnak, és amelyből megkapja az új objektum kezdeti tulajdonságait.

Vessen egy pillantást erre a kódra:

let names = { fname: "Dillion", lname: "Megida" } console.log(names.fname); console.log(names.hasOwnProperty("mname")); // Expected Output // Dillion // false 

Az objektumváltozónak namescsak két tulajdonsága van - fnameés lname. Nincsenek módszerek.

Tehát honnan hasOwnPropertyszármazik?

Nos, ez a Objectprototípusból származik .

Próbálja meg naplózni a változó tartalmát a konzolon:

console.log(names); 

Ha kibővíti az eredményeket a konzolon, akkor ezt kapja:

Figyelje meg az utolsó tulajdonságot - __proto__? Próbáld kibővíteni:

A Objectkonstruktor alatt megjelenik egy sor tulajdonság . Mindezek a tulajdonságok a globális Objectprototípusból származnak . Ha alaposan megnézi, észreveszi a rejtetteket is hasOwnProperty.

Más szavakkal, minden objektum hozzáférhet a Objectprototípushoz. Nem rendelkeznek ezekkel a tulajdonságokkal, de hozzáférést kapnak a prototípus tulajdonságaihoz.

Az __proto__ingatlan

Ez a prototípusként használt objektumra mutat.

Ez az ingatlan minden objektumon, amely hozzáférést biztosít az Object prototypeingatlanhoz.

Alapértelmezés szerint minden objektumnak ez a tulajdonsága van, amely a kivételre vonatkozik, Object Protoypekivéve, ha másként van beállítva (vagyis amikor az objektum __proto__egy másik prototípusra mutat).

Az __proto__ingatlan módosítása

Ez a tulajdonság úgy módosítható, hogy kifejezetten kijelenti, hogy egy másik prototípusra kell hivatkoznia. Ennek elérésére a következő módszereket alkalmazzák:

Object.create()

function DogObject(name, age) { let dog = Object.create(constructorObject); dog.name = name; dog.age = age; return dog; } let constructorObject = { speak: function(){ return "I am a dog" } } let bingo = DogObject("Bingo", 54); console.log(bingo); 

A konzolban ez van:

Figyelje meg a __proto__tulajdonságot és a speakmódszert?

Object.create a neki átadott érvet használja prototípussá.

new kulcsszó

function DogObject(name, age) { this.name = name; this.age = age; } DogObject.prototype.speak = function() { return "I am a dog"; } let john = new DogObject("John", 45); 

john„s __proto__tulajdon irányul DogObject” s prototípus. De ne feledje, DogObjecthogy a prototípus egy objektum ( kulcs és értékpár ), ezért rendelkezik egy __proto__tulajdonsággal, amely a globális Objectprototípusra utal .

Ezt a technikát prototípus láncolásnak nevezik .

Vegye figyelembe, hogy: a newkulcsszó megközelítés ugyanazt csinálja, mint, Object.create()de csak megkönnyíti, mivel egyes dolgokat automatikusan elvégez az Ön számára.

És aztán...

A Javascript minden objektuma Objectalapértelmezés szerint hozzáfér a prototípushoz. Ha például egy másik prototípus használatára van beállítva prototype2, akkor prototype2alapértelmezés szerint hozzáférhet az Object prototípusához is, és így tovább.

Objektum + Funkció kombináció

Valószínűleg megzavarja az a tény, hogy DogObjectez egy függvény ( function DogObject(){}), és tulajdonságai pontszerű jelöléssel érhetők el . Ezt függvényobjektum-kombinációnak nevezzük .

A függvények deklarálásakor alapértelmezés szerint sok tulajdonságot kapnak hozzá. Ne feledje, hogy a függvények a JavaScript adattípusainak objektumai is.

Most, osztály

A JavaScript classaz ECMAScript 2015-ben vezette be a kulcsszót. A JavaScript OOP nyelvnek tűnik. De ez csak a szintatikus cukor a meglévő prototípus-készítési technikához képest. A háttérben folytatja a prototípus-készítést, de a külső test OOP-hoz hasonlít. Most megvizsgáljuk, hogy ez hogyan lehetséges.

Az alábbi példa az a általános használata a classJavaScript-ben:

class Animals { constructor(name, specie) { this.name = name; this.specie = specie; } sing() { return `${this.name} can sing`; } dance() { return `${this.name} can dance`; } } let bingo = new Animals("Bingo", "Hairy"); console.log(bingo); 

Ez a konzol eredménye:

A prototípusra __proto__hivatkozik Animals(amely viszont a Objectprototípusra hivatkozik ).

Ebből láthatjuk, hogy a konstruktor meghatározza a főbb jellemzőket, míg a konstruktoron ( sing()és dance()) kívül minden a bónusz tulajdonság ( prototípus ).

A háttérben, a newkulcsszó megközelítés alkalmazásával, a fentiek fordítása:

function Animals(name, specie) { this.name = name; this.specie = specie; } Animals.prototype.sing = function(){ return `${this.name} can sing`; } Animals.prototype.dance = function() { return `${this.name} can dance`; } let Bingo = new Animals("Bingo", "Hairy"); 

Alosztályozás

Ez az OOP egyik olyan jellemzője, ahol egy osztály örökli a tulajdonságokat a szülő osztálytól, de rendelkezik olyan extra tulajdonságokkal, amelyek a szülők nem.

The idea here is, for example, say you want to create a cats class. Instead of creating the class from scratch - stating the name, age and species property afresh, you'd inherit those properties from the parent animals class.

This cats class can then have extra properties like color of whiskers.

Let's see how subclasses are done with class.

Here, we need a parent which the subclass inherits from. Examine the following code:

class Animals { constructor(name, age) { this.name = name; this.age = age; } sing() { return `${this.name} can sing`; } dance() { return `${this.name} can dance`; } } class Cats extends Animals { constructor(name, age, whiskerColor) { super(name, age); this.whiskerColor = whiskerColor; } whiskers() { return `I have ${this.whiskerColor} whiskers`; } } let clara = new Cats("Clara", 33, "indigo"); 

With the above, we get the following outputs:

console.log(clara.sing()); console.log(clara.whiskers()); // Expected Output // "Clara can sing" // "I have indigo whiskers" 

When you log the contents of clara out in the console, we have:

You'll notice that clara has a __proto__ property which references the constructor Cats and gets access to the whiskers() method. This __proto__ property also has a __proto__ property which references the constructor Animals thereby getting access to sing() and dance(). name and age are properties that exist on every object created from this.

Using the Object.create method approach, the above translates to:

function Animals(name, age) { let newAnimal = Object.create(animalConstructor); newAnimal.name = name; newAnimal.age = age; return newAnimal; } let animalConstructor = { sing: function() { return `${this.name} can sing`; }, dance: function() { return `${this.name} can dance`; } } function Cats(name, age, whiskerColor) { let newCat = Animals(name, age); Object.setPrototypeOf(newCat, catConstructor); newCat.whiskerColor = whiskerColor; return newCat; } let catConstructor = { whiskers() { return `I have ${this.whiskerColor} whiskers`; } } Object.setPrototypeOf(catConstructor, animalConstructor); const clara = Cats("Clara", 33, "purple"); clara.sing(); clara.whiskers(); // Expected Output // "Clara can sing" // "I have purple whiskers" 

Object.setPrototypeOf is a method which takes in two arguments - the object (first argument) and the desired prototype (second argument).

From the above, the Animals function returns an object with the animalConstructor as prototype. The Cats function returns an object with catConstructor as it's prototype. catConstructor on the other hand, is given a prototype of animalConstructor.

Therefore, ordinary animals only have access to the animalConstructor but cats have access to the catConstructor and the animalConstructor.

Wrapping Up

JavaScript leverages its prototype nature to welcome OOP developers to its ecosystem. It also provides easy ways to creating prototypes and organize related data.

True OOP languages do not perform prototyping in the background - just take note of that.

A big thanks to Will Sentance's course on Frontend Masters - JavaScript: The Hard Parts of Object Oriented JavaScript. I learned everything you see in this article (plus a little extra research) from his course. You should check it out.

You can hit me up on Twitter at iamdillion for any questions or contributions.

Thanks for reading : )

Useful Resources

  • Object-oriented JavaScript for beginners
  • Introduction to Object Oriented Programming in JavaScript