OOJS: Об'єктно-орієнтований JavaПідручник зі скриптів із прикладом

⚡ Розумний підсумок

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

  • 🧱 Об'єкти: Об'єкт об'єднує пов'язані властивості та методи, тому одна змінна може моделювати студента, обліковий запис або дім.
  • 🏗️ Конструктори: Функція-конструктор з це створює багато екземплярів з одного багаторазового креслення.
  • 🔁 Перерахування: Цикл for/in проходить кожну перелічувану властивість, виводячи ключі та значення без жорстко кодованих імен.
  • 📦 Класи ES6: Ключове слово класу — це синтаксичний цукор; методи все ще знаходяться на спільному об'єкті прототипу.
  • 🧬 успадкування: автоматично розширює прототипи посилань, а super викликає батьківський конструктор або перевизначений метод.
  • 🔒 Інкапсуляція: Поля з префіксом # залишаються приватними, прихованими від Object.keys та JSON.stringify.
  • Практика: Кожен приклад на цій сторінці показує перевірений результат, тож ви можете порівнювати результати під час навчання.

Об'єктно-орієнтований JavaКонцепції скриптів (OOJS) з об'єктами, конструкторами та класами

У чому полягає концепція 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.

Поширені запитання

Він створює порожній об'єкт, пов'язує його з прототипом конструктора, запускає конструктор з "this", прив'язаним до цього об'єкта, та повертає його. Якщо конструктор явно повертає об'єкт, то повертається це значення.

Використовуйте статичний метод, коли логіка належить до типу, а не до якогось окремого екземпляра, наприклад, фабрика, яка будує об'єкт з JSON, або допоміжна функція, яка порівнює два екземпляри. Статичні члени викликаються для самого класу.

Оскільки «це» визначається тим, як викликається метод, а не де він написаний. Передача методу як зворотного виклику втрачає отримувача. Виправте це, прив'язавши метод у конструкторі або оголосивши його як поле класу зі стрілковою функцією.

Ні. Оголошення двох методів з однаковим ім'ям зберігає лише останній. Імітуйте перевантаження, перевіряючи arguments.length, використовуючи значення параметрів за замовчуванням або приймаючи один об'єкт options з іменованими властивостями.

Так. Помічники ШІ-кодування добре справляються з цим рефакторингом, оскільки картаping є механічним. Завжди переглядайте результат, оскільки помічники можуть пропустити методи, приєднані до прототипу поза конструктором, або скинути виклик батьківського конструктора.

Так. Згенерований код все одно потрібно прочитати, перевірити та налагодити. Знання того, як поводяться прототипи, поля «this» та приватні поля, дозволяє виявити неправильну пропозицію, перш ніж вона потрапить у продакшн.

Підсумуйте цей пост за допомогою: