JavaScript אינו שפה מונחית עצמים מבוססת כיתות. אך עדיין יש דרכים להשתמש בתכנות מונחה עצמים (OOP).
במדריך זה אסביר את OOP ואראה לך כיצד להשתמש בו.
על פי ויקיפדיה, תכנות מבוסס כיתות הוא
סגנון של תכנות מונחה עצמים (OOP) בו הירושה מתרחשת באמצעות הגדרת מחלקות של אובייקטים, במקום שהירושה מתרחשת באמצעות האובייקטים בלבד.
המודל הפופולרי ביותר של OOP מבוסס על כיתות.
אבל כפי שציינתי, JavaScript אינו שפה מבוססת סיווג - היא שפה מבוססת אב-טיפוס.
על פי התיעוד של מוזילה:
לשפה מבוססת אב-טיפוס יש מושג של אובייקט אב טיפוס, אובייקט המשמש כתבנית שממנה ניתן לקבל את המאפיינים הראשוניים של אובייקט חדש.
התבונן בקוד זה:
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
של __proto__
הנכס מופנה DogObject
'אב טיפוס של. אך זכרו, DogObject
האב-טיפוס שלו הוא אובייקט ( צמד מפתחות וערכים ), ומכאן שיש לו גם __proto__
מאפיין המתייחס Object
לפרוטויפ הגלובלי .
טכניקה זו מכונה שרשרת פרוטוטיפ .
שים לב:new
גישת המילה עושה את אותו הדבר כמו Object.create()
אלא רק מקלה כפי שהיא עושה כמה דברים באופן אוטומטי עבורך.
וכך ...
לכל אובייקט ב- Javascript יש גישה Object
לפרוטוטיפ של ברירת המחדל. אם מוגדר להשתמש באב טיפוס אחר, נניח prototype2
, אז prototype2
תהיה לו גם גישה לאב הטיפוס של האובייקט כברירת מחדל, וכן הלאה.
שילוב אובייקט + פונקציות
אתה כנראה מבולבל מהעובדה DogObject
שהיא פונקציה ( function DogObject(){}
) ויש לה מאפיינים אליהם ניתן להגיע עם סימון נקודה . זה מכונה שילוב של אובייקט פונקציה .
כאשר פונקציות מוכרזות, כברירת מחדל הם מקבלים הרבה מאפיינים המצורפים אליו. זכור כי פונקציות הן גם אובייקטים בסוגי נתונים של JavaScript.
עכשיו, מחלקה
JavaScript הציג את class
מילת המפתח ב- ECMAScript 2015. זה גורם ל- JavaScript להיראות כמו שפת OOP. אבל זה רק סוכר סינטטי על פני טכניקת האב טיפוס הקיימת. זה ממשיך את אבות הטיפוס שלו ברקע אבל גורם לגוף החיצוני להיראות כמו OOP. כעת נבדוק כיצד זה אפשרי.
הדוגמה הבאה היא שימוש כללי class
ב- 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);
זו התוצאה במסוף:

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
.
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