Αντικειμενοστραφής προγραμματισμός σε JavaScript - Εξηγείται με παραδείγματα
Το JavaScript δεν είναι μια αντικειμενοστρεφής γλώσσα που βασίζεται στην τάξη. Αλλά εξακολουθεί να διαθέτει τρόπους χρήσης αντικειμενοστρεφούς προγραμματισμού (OOP).
Σε αυτό το σεμινάριο, θα εξηγήσω το OOP και θα σας δείξω πώς να το χρησιμοποιήσετε.
Σύμφωνα με τη Wikipedia, ο προγραμματισμός βάσει τάξεων είναι
ένα στυλ αντικειμενοστραφής προγραμματισμού (OOP) στο οποίο η κληρονομιά εμφανίζεται μέσω καθορισμού κατηγοριών αντικειμένων, αντί της κληρονομιάς που συμβαίνει μόνο μέσω των αντικειμένων
Το πιο δημοφιλές μοντέλο του OOP είναι βασισμένο στην τάξη.
Αλλά όπως ανέφερα, το JavaScript δεν είναι μια ταξινομημένη γλώσσα - είναι μια γλώσσα που βασίζεται σε πρωτότυπα.
Σύμφωνα με την τεκμηρίωση του Mozilla:
Μια γλώσσα που βασίζεται σε πρωτότυπο έχει την έννοια ενός πρωτότυπου αντικειμένου, ένα αντικείμενο που χρησιμοποιείται ως πρότυπο από το οποίο μπορείτε να λάβετε τις αρχικές ιδιότητες για ένα νέο αντικείμενο.
Ρίξτε μια ματιά σε αυτόν τον κωδικό:
let names = { fname: "Dillion", lname: "Megida" } console.log(names.fname); console.log(names.hasOwnProperty("mname")); // Expected Output // Dillion // false
Η μεταβλητή αντικειμένου names
έχει μόνο δύο ιδιότητες - fname
και lname
. Δεν υπάρχουν καθόλου μέθοδοι.
Από που hasOwnProperty
προέρχεται λοιπόν ;
Λοιπόν, προέρχεται από το Object
πρωτότυπο.
Δοκιμάστε να καταγράψετε τα περιεχόμενα της μεταβλητής στην κονσόλα:
console.log(names);
Όταν επεκτείνετε τα αποτελέσματα στην κονσόλα, θα λάβετε αυτό:

Παρατηρήστε την τελευταία ιδιότητα - __proto__
; Δοκιμάστε να το επεκτείνετε:

Θα δείτε ένα σύνολο ιδιοτήτων κάτω από τον Object
κατασκευαστή. Όλες αυτές οι ιδιότητες προέρχονται από το παγκόσμιο Object
πρωτότυπο. Αν κοιτάξετε προσεκτικά, θα παρατηρήσετε επίσης το κρυφό μας hasOwnProperty
.
Με άλλα λόγια, όλα τα αντικείμενα έχουν πρόσβαση στο Object
πρωτότυπο του. Δεν διαθέτουν αυτές τις ιδιότητες, αλλά τους παρέχεται πρόσβαση στις ιδιότητες του πρωτοτύπου.
Το __proto__
ακίνητο
Αυτό δείχνει το αντικείμενο που χρησιμοποιείται ως πρωτότυπο.
Αυτή είναι η ιδιότητα σε κάθε αντικείμενο που της δίνει πρόσβαση στην Object prototype
ιδιότητα.
Κάθε αντικείμενο έχει αυτήν την ιδιότητα από προεπιλογή, η οποία αναφέρεται στην Object Protoype
εξαίρεση, εκτός εάν έχει ρυθμιστεί διαφορετικά (δηλαδή, όταν το αντικείμενο __proto__
είναι στραμμένο σε άλλο πρωτότυπο).
Τροποποίηση της __proto__
ιδιότητας
Αυτή η ιδιότητα μπορεί να τροποποιηθεί δηλώνοντας ρητά ότι πρέπει να αναφέρεται σε άλλο πρωτότυπο. Οι ακόλουθες μέθοδοι χρησιμοποιούνται για να επιτευχθεί αυτό:
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);
Στην κονσόλα, αυτό θα έχετε:

Παρατηρήστε την __proto__
ιδιότητα και τη speak
μέθοδο;
Object.create
χρησιμοποιεί το όρισμα που του δόθηκε για να γίνει το πρωτότυπο.
new
λέξη-κλειδί
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__
property is directed to DogObject
's prototype. But remember, DogObject
's prototype is an object (key and value pair), hence it also has a __proto__
property which refers to the global Object
protoype.
This technique is referred to as PROTOTYPE CHAINING.
Note that: the new
keyword approach does the same thing as Object.create()
but only makes it easier as it does some things automatically for you.
And so...
Every object in Javascript has access to the Object
's prototype by default. If configured to use another prototype, say prototype2
, then prototype2
would also have access to the Object's prototype by default, and so on.
Object + Function Combination
You are probably confused by the fact that DogObject
is a function (function DogObject(){}
) and it has properties accessed with a dot notation. This is referred to as a function object combination.
When functions are declared, by default they are given a lot of properties attached to it. Remember that functions are also objects in JavaScript data types.
Now, Class
JavaScript introduced the class
keyword in ECMAScript 2015. It makes JavaScript seem like an OOP language. But it is just syntatic sugar over the existing prototyping technique. It continues its prototyping in the background but makes the outer body look like OOP. We'll now look at how that's possible.
The following example is a general usage of a class
in JavaScript:
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);
This is the result in the console:

The __proto__
references the Animals
prototype (which in turn references the Object
prototype).
From this, we can see that the constructor defines the major features while everything outside the constructor (sing()
and dance()
) are the bonus features (prototypes).
In the background, using the new
keyword approach, the above translates to:
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");
Subclassing
This is a feature in OOP where a class inherits features from a parent class but possesses extra features which the parent doesn't.
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
.
Ως εκ τούτου, οι απλοί τα ζώα έχουν πρόσβαση μόνο στην animalConstructor
, αλλά οι γάτες έχουν πρόσβαση στο catConstructor
και το animalConstructor
.
Τυλίγοντας
Η JavaScript αξιοποιεί το πρωτότυπο χαρακτήρα της για να καλωσορίσει τους προγραμματιστές OOP στο οικοσύστημά της. Παρέχει επίσης εύκολους τρόπους για τη δημιουργία πρωτοτύπων και την οργάνωση σχετικών δεδομένων.
Οι πραγματικές γλώσσες OOP δεν εκτελούν πρωτότυπο στο παρασκήνιο - απλώς λάβετε υπόψη αυτό.
Ευχαριστώ πολύ για το μάθημα του Will Sentance για το Frontend Masters - JavaScript: Τα σκληρά μέρη του αντικειμενοστραφούς JavaScript. Έμαθα όλα όσα βλέπετε σε αυτό το άρθρο (συν μια επιπλέον έρευνα) από την πορεία του. πρέπει να το ελέγξεις.
Μπορείτε να με χτυπήσετε στο Twitter στο iamdillion για οποιεσδήποτε ερωτήσεις ή συνεισφορές.
Ευχαριστώ για την ανάγνωση : )
Χρήσιμοι πόροι
- Αντικειμενοστραφής JavaScript για αρχάριους
- Εισαγωγή στον αντικειμενικό προγραμματισμό σε JavaScript