OOJS: Obiektowo zorientowany JavaSamouczek skryptu z przykładem

⚡ Inteligentne podsumowanie

Zorientowany obiektowo JavaSkrypt modeluje rzeczywiste byty jako obiekty, które zawierają właściwości i metody. Ta strona wyjaśnia literały obiektów, funkcje konstruktorów, prototypy, klasy ES6, dziedziczenie i pola prywatne z przykładami, które można uruchomić i zweryfikować.

  • 🧱 Obiekty: Obiekt łączy powiązane właściwości i metody, dzięki czemu jedna zmienna może modelować ucznia, konto lub dom.
  • 🏗️. Konstruktorzy: Funkcja konstruktora z to tworzy wiele instancji z jednego, wielokrotnego użytku projektu.
  • 🔁 Wyliczenie: Pętla for/in przechodzi przez każdą wyliczalną właściwość, wyświetlając klucze i wartości bez konieczności zapisywania nazw na stałe.
  • 📦 Klasy ES6: Słowo kluczowe class jest składnią; metody nadal istnieją w udostępnionym obiekcie prototypowym.
  • 🧬 Dziedzictwo: automatycznie rozszerza prototypy łączy i wywołuje konstruktor nadrzędny lub nadpisaną metodę.
  • 🔒 Kapsułkowanie: Pola poprzedzone znakiem # pozostają prywatne, ukryte przed Object.keys i JSON.stringify.
  • ⚙️ Ćwiczyć: Każdy przykład na tej stronie przedstawia zweryfikowane wyniki, dzięki czemu możesz porównywać wyniki w miarę zdobywania wiedzy.

Zorientowany obiektowo JavaKoncepcje skryptów (OOJS) z obiektami, konstruktorami i klasami

W czym jest koncepcja OOPS JavaScenariusz?

W wielu przypadkach zmienne lub tablice nie wystarczą do symulacji rzeczywistych sytuacji. JavaSkrypt pozwala tworzyć obiekty, które zachowują się jak obiekty z życia realnego. Student lub dom mogą być obiektami o wielu unikalnych cechach. Możesz tworzyć właściwości i metody dla swoich obiektów, aby ułatwić programowanie. Jeśli Twoim obiektem jest student, będzie on posiadał właściwości takie jak imię, nazwisko i identyfikator, a także metody takie jak calculatorRank lub changeAddress. Jeśli Twoim obiektem jest dom, będzie posiadał właściwości takie jak liczba pokoi, kolor farby i lokalizacja, a także metody takie jak calculatorArea lub changeOwner.

Programowanie obiektowe w JavaSkrypt opiera się zatem na czterech ideach, które spotkasz na tej stronie: grupaping stan i zachowanie w obiekcie, ponowne wykorzystanie projektu za pomocą konstruktora, współdzielenie metod za pośrednictwem prototypu i ograniczenie bezpośredniego dostępu do danych wewnętrznych. Jeśli dopiero zaczynasz przygodę z językiem, zacznij od tego. wprowadzenie do JavaScenariusz a potem wróć tutaj.

Jak utworzyć obiekt

Możesz stworzyć taki obiekt:

var objName = new Object();
objName.property1 = value1;
objName.property2 = value2;
objName.method1 = function()
{
  // line of code
}

LUB, używając krótszej formy obiektu literałowego:

var objName = { property1: value1, property2: value2, method1: function()
{
  // lines of code
} };

Uzyskaj dostęp do właściwości i metod obiektu

Dostęp do właściwości obiektu można uzyskać za pomocą notacji kropkowej, a metodę można wywołać, dodając nawiasy:

objectname.propertyname;   // read a property
objectname.methodname();   // call a method
objectname["propertyname"]; // bracket notation, useful for dynamic keys

Wypróbuj sam ten przykład:

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

Wyjście:

Student Id: 5
Name: John Smith
Average Marks: 87.33333333333333

💡 Wskazówka: Powyższy przykład wykorzystuje dokument.zapisz ponieważ działa w edytorze na tej stronie. W rzeczywistych projektach preferuję konsola.log do debugowania lub metod DOM, takich jak Treść tekstowa, Ponieważ dokument.zapisz czyści stronę, gdy zostanie wywołana po zakończeniu ładowania.

Konstruktor OOPS

Tworzenie obiektów pojedynczo nie jest zbyt przydatne, ponieważ wymagałoby powtarzania tego samego kodu dla każdego ucznia. W tym miejscu pojawia się konstruktor obiektów. Konstruktor to zwykła funkcja wywoływana za pomocą nowych Słowo kluczowe; definiuje typ obiektu, który można ponownie wykorzystać w każdej pojedynczej instancji.

Wewnątrz konstruktora, to odnosi się do nowego obiektu, który jest budowany, więc każda instancja otrzymuje własną kopię przekazanych wartości. Powiązane wartości, takie jak znaczniki, są często przechowywane w szyk tak, aby można je było zsumować za jednym razem.

Wypróbuj sam ten przykład:

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

Wyjście:

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

Pętla przez właściwości obiektu

Składnia:

for (variablename in objectname)
{
  // lines of code to be executed
}

Dla/w pętla jest zazwyczaj używany do przeglądania właściwości obiektu. Możesz nadać mu dowolną nazwę. zmienna, ale nazwa obiektu musi pasować do obiektu, który już istnieje. W każdym przejściu zmienna przechowuje właściwość klucz jako ciąg znaków, więc wartość jest odczytywana w notacji nawiasowej, takiej jak pracownik[x].

Wypróbuj sam ten przykład:

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

Wyjście:

Using for/in loops
first: John
last: Doe
department: Accounts

Warto pamiętać o dwóch kwestiach. Pętla for/in również przegląda wyliczalne właściwości dziedziczone w łańcuchu prototypów, więc strażnik, taki jak Obiekt.prototype.hasOwnProperty.call(pracownik, x) jest często dodawany. W przypadku zwykłych obiektów danych, Obiekt.keys(pracownik) or Obiekt.wpisy(pracownik) jest zwykle bardziej przejrzyste, ponieważ oba zwracają tylko właściwości obiektu.

Jaka jest składnia klasy ES6? JavaScenariusz?

ECMAScript 2015, powszechnie nazywany ES6, dodał klasa Słowo kluczowe. Klasa to składniowy lukier dla systemu prototypów, z którego korzysta już powyższa funkcja konstruktora: nie wprowadzono żadnego nowego modelu obiektu. Uruchamianie typ studenta w klasie nadal zwraca ciąg „funkcja”, a każda metoda zapisana w ciele klasy jest instalowana Prototyp studenta, co oznacza, że ​​wszystkie instancje współdzielą jedną kopię tej metody zamiast tego, aby każdy obiekt miał swoją własną kopię.

Składnia eliminuje również pewne niedociągnięcia. Ciała klas zawsze są wykonywane w trybie ścisłym, wywołując klasę bez nowych zgłasza błąd TypeError zamiast po cichu zaśmiecać obiekt globalny, a metody getter, setter i elementy statyczne mają dedykowane miejsce do przechowywania. Rezultatem jest krótszy kod, który wyraża tę samą intencję.

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);

Wyjście:

Student Id: 15
Name: John Smith
Average Marks: 84.67
function
true

Ostatnie dwa wiersze dowodzą, że klasa jest nadal funkcją, a metoda na instancji jest dokładnie tym samym obiektem funkcji, który jest przechowywany w prototypie.

Funkcja konstruktora a klasa ES6

WYGLĄD Funkcja konstruktora Klasa ES6
Deklaracja funkcja Student(…) { } klasa Student { konstruktor(…) { } }
Umieszczenie metody Przypisane wewnątrz funkcji, więc każda instancja otrzymuje własną kopię, chyba że napiszesz do Prototyp studenta Napisane w treści klasy i umieszczone automatycznie w prototypie
Dzwonienie bez nowego Działa cicho i to jest niezdefiniowany lub obiekt globalny Natychmiast zgłasza błąd TypeError
Podnoszenie Deklaracje funkcji są podnoszone i gotowe do użycia przed definicją W martwej strefie czasowej do czasu oceny
Tryb ścisły Podąża za otaczającym scenariuszem Zawsze surowy
Dziedzictwo Manual: Obiekt.utwórz plus wezwanie do rodzica rozciąga się oraz wielki
Dane prywatne Zamknięcia lub konwencja nazewnictwa, taka jak _balansować Prawdziwe prywatne pola z #
Model bazowy prototypy Prototypy (identyczne w czasie wykonywania)

Jak wdrożyć dziedziczenie za pomocą extends i super

Dziedziczenie pozwala typowi specjalistycznemu ponownie wykorzystać właściwości i metody typu bardziej ogólnego. Przed ES6 oznaczało to ręczne łączenie łańcucha prototypów w trzech oddzielnych krokach, a łatwo było o jednym z nich zapomnieć.

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);

Wyjście:

I am Kevin White
true

rozciąga się słowo kluczowe wykonuje wszystkie trzy kroki za Ciebie i wielki zapewnia czysty sposób dotarcia do rodzica. Istnieją dwa różne zastosowania wielki:

  • super(…) jako wezwanie uruchamia konstruktor nadrzędny. Działa on tylko wewnątrz konstruktora pochodnego i musi zostać uruchomiony przed dotknięciem to, w przeciwnym wypadku zgłaszany jest błąd ReferenceError.
  • super.metoda() ponieważ wyszukiwanie właściwości wywołuje wersję nadrzędną metody, którą metoda podrzędna nadpisała, w ten sposób rozszerzasz zachowanie zamiast je zastępować.
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);

Wyjście:

I am Hannah Turner, roll number 23
true true
true

Trzecia linijka jest ważna. Potwierdza, że rozciąga się Wygenerowano dokładnie taki sam prototypowy link, jak w wersji ręcznej, więc klasy nie zastąpiły łączenia prototypów. Po prostu ukryto szablon. Kiedy właściwość jest odczytywana z stsilnik sprawdza instancję, a następnie Prototyp studenta, następnie Osoba.prototyp, następnie Obiekt.prototypi zatrzymuje się na pierwszym dopasowaniu; ta ścieżka wyszukiwania jest łańcuchem prototypów.

⚠️ Ostrzeżenie: Nigdy nie przypisuj Uczeń.prototyp = Osoba.prototyp. Dzięki temu oba typy współdzielą jeden obiekt, więc każda metoda dodana do obiektu potomnego jest również dodawana do obiektu nadrzędnego. Użyj Obiekt.utwórz(Osoba.prototyp)lub po prostu użyj rozciąga się.

Jak osiągnąć hermetyzację przy użyciu pól klas prywatnych

Enkapsulacja oznacza zachowanieping stan wewnętrzny poza zasięgiem, tak że można go zmienić tylko za pomocą kontrolowanych przez siebie metod. Przez lata JavaTwórcy skryptów fałszowali to za pomocą podkreślenia na początku, ale nazwa taka jak _balansować to tylko konwencja i każdy kod może ją nadpisać. Nowoczesne JavaSkrypt zapewnia prawdziwą prywatność: pole, którego nazwa zaczyna się od # jest dostępny tylko w obrębie ciała klasy, w którym jest deklarowany.

Prywatne boiska przynoszą trzy konkretne korzyści:

  • Muszą zostać zadeklarowane w ciele klasy przed użyciem, co dokumentuje wewnętrzny stan typu w jednym miejscu.
  • Są dla nich niewidzialne Obiekt.klucze, JSON.stringifyoraz for/in, dzięki czemu stan wewnętrzny nie wycieka do wyjścia szeregowego.
  • Próba dostępu do prywatnego pola spoza klasy jest błędem składniowym wychwytywanym jeszcze przed uruchomieniem kodu, a nie ukrytym błędem.
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));

Wyjście:

John balance: 750
Deposit must be positive
Read via getter: 750
Own keys: owner
JSON: {"owner":"John"}

Saldo nigdy się nie pojawia Obiekt.klucze lub w ciągu JSON, a ujemny depozyt jest odrzucany przez metodę zamiast uszkadzać pole. uzyskaj saldo() Akcesor udostępnia widok tylko do odczytu, co jest typowym sposobem bezpiecznego publikowania stanu prywatnego.

Skoro obiekty, prototypy, klasy, dziedziczenie i hermetyzacja mają już sens, kontynuuj tworzenie. Ćwicz podstawy z tymi praktyczny JavaPrzykłady kodu skryptu, przepracować interaktywne JavaĆwiczenia skryptowe, zastosuj klasy do algorytmów takich jak szybkie sortowanie w JavaScenariusz, uporządkuj wyjście za pomocą JavaFormatowanie ciągu skryptui zobacz jak statyczny jestping zmienia projekt obiektu w TypeScript.

FAQ

Tworzy pusty obiekt, łączy go z prototypem konstruktora, uruchamia konstruktor z elementem „this” powiązanym z tym obiektem i zwraca go. Jeśli konstruktor zwróci obiekt jawnie, zwracana jest ta wartość.

Użyj metody statycznej, gdy logika należy do typu, a nie do pojedynczej instancji, na przykład fabryki budującej obiekt z JSON lub funkcji pomocniczej porównującej dwie instancje. Elementy statyczne są wywoływane w samej klasie.

Ponieważ „to” jest ustalane przez sposób wywołania metody, a nie przez miejsce jej zapisu. Przekazanie metody jako wywołania zwrotnego powoduje utratę odbiorcy. Można to naprawić, wiążąc metodę w konstruktorze lub deklarując ją jako pole klasy z funkcją strzałkową.

Nie. Deklaracja dwóch metod o tej samej nazwie powoduje zachowanie tylko ostatniej. Symulację przeciążenia można przeprowadzić, sprawdzając arguments.length, używając domyślnych wartości parametrów lub akceptując pojedynczy obiekt opcji z nazwanymi właściwościami.

Tak. Asystenci kodowania AI dobrze sobie radzą z tą refaktoryzacją, ponieważ mapaping jest mechaniczny. Zawsze sprawdzaj wynik, ponieważ asystenci mogą pominąć metody dołączone do prototypu poza konstruktorem lub porzucić wywołanie konstruktora nadrzędnego.

Tak. Wygenerowany kod nadal musi zostać odczytany, sprawdzony i debugowany. Wiedza o zachowaniu prototypów, pola „this” i pól prywatnych pozwala na wykrycie nieprawidłowej sugestii, zanim trafi ona do produkcji.

Podsumuj ten post następująco: