OOJS: Об'єктно-орієнтований JavaПідручник зі скриптів із прикладом
⚡ Розумний підсумок
Об'єктно-орієнтований JavaСкрипт моделює реальні сутності як об'єкти, що несуть властивості та методи. На цій сторінці пояснюються літерали об'єктів, функції-конструктори, прототипи, класи ES6, успадкування та приватні поля з прикладами, які ви можете виконати та перевірити.

У чому полягає концепція OOPS JavaСценарій?
Часто змінних або масивів недостатньо для моделювання ситуацій у реальному житті. JavaСкрипти дозволяють створювати об'єкти, які діють як реальні об'єкти. Студент або будинок можуть бути об'єктом, який має багато унікальних характеристик. Ви можете створювати властивості та методи для своїх об'єктів, щоб спростити програмування. Якщо ваш об'єкт є студентом, він матиме такі властивості, як ім'я, прізвище та ідентифікатор, а також методи, такі як calculateRank або changeAddress. Якщо ваш об'єкт є будинком, він матиме такі властивості, як кількість кімнат, колір фарби та розташування, а також методи, такі як calculateArea або changeOwner.
Об'єктно-орієнтоване програмування в JavaОтже, сценарій базується на чотирьох ідеях, з якими ви зустрінетеся на цій сторінці: групаping стан і поведінку в об'єкт, повторне використання креслення через конструктор, спільне використання методів через прототип та обмеження прямого доступу до внутрішніх даних. Якщо ви новачок у цій мові, почніть з цього вступ до JavaScript а потім повернутися сюди.
Як створити об'єкт
Ви можете створити такий об’єкт:
var objName = new Object(); objName.property1 = value1; objName.property2 = value2; objName.method1 = function() { // line of code }
АБО, використовуючи коротшу форму літерала об'єкта:
var objName = { property1: value1, property2: value2, method1: function() { // lines of code } };
Доступ до властивостей і методів об’єктів
Ви можете отримати доступ до властивості об'єкта за допомогою крапкової нотації, а викликати метод можна, додавши дужки:
objectname.propertyname; // read a property objectname.methodname(); // call a method objectname["propertyname"]; // bracket notation, useful for dynamic keys
Спробуйте цей приклад самостійно:
<html>
<head>
<title>Objects!!!</title>
<script type="text/javascript">
var student = new Object();
student.fName = "John";
student.lName = "Smith";
student.id = 5;
student.markE = 76;
student.markM = 99;
student.markS = 87;
student.calculateAverage = function()
{
return (student.markE + student.markM + student.markS)/3;
};
student.displayDetails = function()
{
document.write("Student Id: " + student.id + "<br />");
document.write("Name: " + student.fName + " " + student.lName + "<br />");
var avg = student.calculateAverage();
document.write("Average Marks: " + avg);
};
student.displayDetails();
</script>
</head>
<body>
</body>
</html>
вихід:
Student Id: 5 Name: John Smith Average Marks: 87.33333333333333
💡 Порада: У наведеному вище прикладі використовується document.write оскільки він працює всередині редактора на цій сторінці. У реальних проектах перевагу надайте console.log для налагодження або методів DOM, таких як textContent, Так як document.write стирає сторінку, коли її викликають після завершення завантаження.
Конструктор OOPS
Створення об'єктів по одному за раз не є таким корисним, оскільки вам довелося б повторювати один і той самий код для кожного студента. Саме тут на допомогу приходить конструктор об'єктів. Конструктор - це звичайна функція, яка викликається разом з new ключове слово; воно визначає тип об'єкта, який можна повторно використовувати для кожного окремого екземпляра.
Всередині конструктора, це посилається на новий об'єкт, що створюється, тому кожен екземпляр отримує власну копію переданих значень. Пов'язані значення, такі як позначки, часто зберігаються в масив щоб їх можна було підсумувати за один прохід.
Спробуйте цей приклад самостійно:
<html>
<head>
<script type="text/javascript">
function Student(first, last, id, english, maths, science)
{
this.fName = first;
this.lName = last;
this.id = id;
this.markE = english;
this.markM = maths;
this.markS = science;
this.calculateAverage = function()
{
return (this.markE + this.markM + this.markS)/3;
}
this.displayDetails = function()
{
document.write("Student Id: " + this.id + "<br />");
document.write("Name: " + this.fName + " " + this.lName + "<br />");
var avg = this.calculateAverage();
document.write("Average Marks: " + avg + "<br /><br />");
}
}
var st1 = new Student("John", "Smith", 15, 85, 79, 90);
var st2 = new Student("Hannah", "Turner", 23, 75, 80, 82);
var st3 = new Student("Kevin", "White", 4, 93, 89, 90);
var st4 = new Student("Rose", "Taylor", 11, 55, 63, 45);
st1.displayDetails();
st2.displayDetails();
st3.displayDetails();
st4.displayDetails();
</script>
</head>
<body>
</body>
</html>
вихід:
Student Id: 15 Name: John Smith Average Marks: 84.66666666666667 Student Id: 23 Name: Hannah Turner Average Marks: 79 Student Id: 4 Name: Kevin White Average Marks: 90.66666666666667 Student Id: 11 Name: Rose Taylor Average Marks: 54.333333333333336
Перегляд властивостей об’єкта
Синтаксис:
for (variablename in objectname) { // lines of code to be executed }
Для/в петля зазвичай використовується для перегляду властивостей об'єкта. Ви можете дати йому будь-яке ім'я зміннаале ім'я об'єкта має збігатися з об'єктом, який вже існує. На кожному проході змінна містить властивість ключ як рядок, тому значення читається з використанням дужок, таких як співробітник[x].
Спробуйте цей приклад самостійно:
<html>
<head>
<script type="text/javascript">
var employee={first:"John", last:"Doe", department:"Accounts"};
var details = "";
document.write("<b>Using for/in loops </b><br />");
for (var x in employee)
{
details = x + ": " + employee[x];
document.write(details + "<br />");
}
</script>
</head>
<body>
</body>
</html>
вихід:
Using for/in loops first: John last: Doe department: Accounts
Варто пам'ятати два моменти. Цикл for/in також відвідує перелічувані властивості, успадковані через ланцюжок прототипів, тому такий захист, як Object.prototype.hasOwnProperty.call(співробітник, x) часто додається. Для простих об'єктів даних, Object.keys(співробітник) or Object.entries(співробітник) зазвичай зрозуміліше, оскільки обидва повертають лише власні властивості об'єкта.
Який синтаксис класу ES6 у JavaСценарій?
ECMAScript 2015, який зазвичай називають ES6, додав клас ключове слово. Клас — це синтаксичний цукор над системою прототипів, яку вже використовує функція-конструктор вище: жодної нової об'єктної моделі не було введено. Виконання тип студента на класі все ще повертає рядок «function», і кожен метод, написаний у тілі класу, встановлюється на Студент.прототип, що означає, що всі екземпляри мають одну спільну копію цього методу, а не кожен об'єкт має свою власну.
Синтаксис також усуває деякі гострі сторони. Тіла класів завжди виконуються в строгому режимі, викликаючи клас без new викидає TypeError замість того, щоб непомітно забруднювати глобальний об'єкт, а геттери, сеттери та статичні члени мають окреме місце для проживання. Результатом є коротший код, який виражає той самий намір.
class Student { constructor(first, last, id, english, maths, science) { this.fName = first; this.lName = last; this.id = id; this.marks = [english, maths, science]; } calculateAverage() { const total = this.marks.reduce((sum, m) => sum + m, 0); return total / this.marks.length; } displayDetails() { console.log("Student Id: " + this.id); console.log("Name: " + this.fName + " " + this.lName); console.log("Average Marks: " + this.calculateAverage().toFixed(2)); } } const st1 = new Student("John", "Smith", 15, 85, 79, 90); st1.displayDetails(); console.log(typeof Student); console.log(st1.calculateAverage === Student.prototype.calculateAverage);
вихід:
Student Id: 15 Name: John Smith Average Marks: 84.67 function true
Останні два рядки доводять цю думку: клас все ще є функцією, а метод на екземплярі — це той самий об'єкт функції, що зберігається на прототипі.
Функція-конструктор проти класу ES6
| Аспект | Функція-конструктор | Клас ES6 |
|---|---|---|
| Декларація | функція Студент(…) { } | клас Student { конструктор(…) { } } |
| Розміщення методу | Присвоюється всередині функції, тому кожен екземпляр отримує свою власну копію, якщо ви не записуєте до Студент.прототип | Записано в тілі класу та автоматично розміщено на прототипі |
| Дзвін без новин | Працює безшумно та це невизначений або глобальний об'єкт | Негайно викидає помилку TypeError |
| Підйом | Оголошення функцій піднімаються та можуть бути використані до їх визначення. | У часовій мертвій зоні до оцінки |
| Строгий режим | Дотримується навколишнього сценарію | Завжди суворий |
| Спадкування | Інструкція з експлуатації: Object.create плюс call батькам | продовжується та супер |
| Приватні дані | Замикання або правила іменування, такі як _баланс | Справжні приватні поля з # |
| Базова модель | прототипи | Прототипи (ідентичні під час виконання) |
Як реалізувати спадкування за допомогою extends та super
Успадкування дозволяє спеціалізованому типу повторно використовувати властивості та методи більш загального типу. До ES6 це означало ручне створення ланцюжка прототипів у три окремі кроки, і один з них було легко забути.
function Person(fName, lName) { this.fName = fName; this.lName = lName; } Person.prototype.introduce = function () { return "I am " + this.fName + " " + this.lName; }; function Student(fName, lName, id) { Person.call(this, fName, lName); // 1. borrow the parent constructor this.id = id; } Student.prototype = Object.create(Person.prototype); // 2. link the chain Student.prototype.constructor = Student; // 3. repair constructor var st = new Student("Kevin", "White", 4); console.log(st.introduce()); console.log(st instanceof Person);
вихід:
I am Kevin White true
Команда продовжується ключове слово виконує всі три кроки за вас, і супер надає простий спосіб зв'язку з батьківським елементом. Існує два різних способи використання супер:
- супер(…) як дзвінок запускає батьківський конструктор. Він дійсний лише всередині похідного конструктора та має бути запущений до того, як ви торкнетеся це, інакше виникає помилка ReferenceError.
- супер.метод() оскільки пошук властивості викликає батьківську версію методу, який дочірній метод перевизначив, саме так ви розширюєте поведінку, а не замінюєте її.
class Person { constructor(fName, lName) { this.fName = fName; this.lName = lName; } introduce() { return "I am " + this.fName + " " + this.lName; } } class Student extends Person { constructor(fName, lName, id) { super(fName, lName); // must run before "this" is used this.id = id; } introduce() { return super.introduce() + ", roll number " + this.id; } } const st = new Student("Hannah", "Turner", 23); console.log(st.introduce()); console.log(st instanceof Student, st instanceof Person); console.log(Object.getPrototypeOf(Student.prototype) === Person.prototype);
вихід:
I am Hannah Turner, roll number 23 true true true
Третій рядок є важливим. Він підтверджує, що продовжується створив саме той прототип, що й ручна версія, побудована вручну, тому класи не замінили ланцюжок прототипів. Вони просто приховали шаблон. Коли властивість зчитується з st, двигун переглядає екземпляр, а потім Студент.прототип, То Прототип людини, То Об'єкт.прототип, і зупиняється на першому збігу; цей шлях пошуку є ланцюжком прототипів.
⚠️ Попередження: Ніколи не призначати Student.prototype = Person.prototypeЦе означає, що обидва типи мають один спільний об'єкт, тому будь-який метод, який ви додаєте до дочірнього об'єкта, також додається до батьківського. Використовуйте Об'єкт.create(Person.prototype)або просто використовуйте продовжується.
Як досягти інкапсуляції за допомогою полів приватних класів
Інкапсуляція означає збереженняping внутрішній стан поза досяжністю, щоб він міг змінюватися лише методами, які ви контролюєте. Роками JavaРозробники скриптів підробили це, використовуючи початкове підкреслення, але таке ім'я, як _баланс це лише умовність, і будь-який код все ще може її перезаписати. Сучасний JavaСкрипт пропонує справжню конфіденційність: поле, ім'я якого починається з # доступний лише всередині тіла класу, який його оголошує.
Приватні поля мають три конкретні переваги:
- Перед використанням їх необхідно оголосити в тілі класу, що документує внутрішній стан типу в одному місці.
- Вони невидимі для Object.keys, JSON.stringify, та for/in, щоб внутрішній стан не просочувався в серіалізований вивід.
- Дотик до приватного поля ззовні класу є синтаксичною помилкою, виявленою ще до запуску коду, а не тихою помилкою.
class BankAccount { #balance = 0; constructor(owner, opening) { this.owner = owner; this.#balance = opening; } deposit(amount) { if (amount <= 0) { return "Deposit must be positive"; } this.#balance += amount; return this.owner + " balance: " + this.#balance; } get balance() { return this.#balance; } } const acc = new BankAccount("John", 500); console.log(acc.deposit(250)); console.log(acc.deposit(-40)); console.log("Read via getter: " + acc.balance); console.log("Own keys: " + Object.keys(acc)); console.log("JSON: " + JSON.stringify(acc));
вихід:
John balance: 750
Deposit must be positive
Read via getter: 750
Own keys: owner
JSON: {"owner":"John"}
Баланс ніколи не відображається в Object.keys або в рядку JSON, і негативний депозит відхиляється методом замість пошкодження поля. отримати баланс() Метод доступу надає доступ лише для читання, що є звичайним способом безпечної публікації приватного стану.
Тепер, коли об'єкти, прототипи, класи, успадкування та інкапсуляція мають сенс, продовжуйте збирати. Практикуйте основи з цими... практичний JavaПриклади коду скриптів, опрацювати інтерактивний JavaВправи зі сценаріями, застосовувати класи до алгоритмів, таких як швидке сортування в JavaScript, упорядкуйте вивід за допомогою JavaФорматування рядка скрипта, і подивіться, як статична тиping змінює дизайн об'єкта в TypeScript.
