OOJS: Objektorienteret JavaScriptvejledning med eksempel

⚡ Smart opsummering

Objektorienteret JavaScript modellerer virkelige enheder som objekter, der bærer egenskaber og metoder. Denne side forklarer objektliteraler, konstruktørfunktioner, prototyper, ES6-klasser, arv og private felter med eksempler, der kan køres og verificeres.

  • 🧱 objekter: Et objekt samler relaterede egenskaber og metoder, så én variabel kan modellere en elev, en konto eller et hjem.
  • 🏗️ Konstruktører: En konstruktørfunktion med denne opretter mange instanser fra en enkelt genanvendelig blueprint.
  • 🔁 Optælling: For/in-løkken gennemgår alle optællelige egenskaber og udskriver nøgler og værdier uden at hardcode navne.
  • 📦 ES6 klasser: Klassenøgleordet er syntaktisk sukker; metoder findes stadig på det delte prototypeobjekt.
  • 🧬 Arv: udvider linker prototyper automatisk, og super kalder forældrekonstruktøren eller en overskrevet metode.
  • 🔒 Indkapsling: Felter med præfikset # forbliver private og skjult for Object.keys og JSON.stringify.
  • 🇧🇷 Øve sig: Hvert eksempel på denne side viser verificeret output, så du kan sammenligne resultaterne, mens du lærer.

Objektorienteret JavaScriptkoncepter (OOJS) med objekter, konstruktører og klasser

Hvad er OOPS Concept i JavaManuskript?

Mange gange er variabler eller arrays ikke tilstrækkelige til at simulere virkelige situationer. JavaScripts giver dig mulighed for at oprette objekter, der fungerer som virkelige objekter. En studerende eller et hjem kan være et objekt, der har mange unikke egenskaber. Du kan oprette egenskaber og metoder på dine objekter for at gøre programmering nemmere. Hvis dit objekt er en studerende, vil det have egenskaber som fornavn, efternavn og id, plus metoder som calculateRank eller changeAddress. Hvis dit objekt er et hjem, vil det have egenskaber som antal værelser, malingfarve og placering, plus metoder som calculateArea eller changeOwner.

Objektorienteret programmering i JavaManuskriptet hviler derfor på fire ideer, som du vil møde på denne side: gruppeping tilstand og adfærd i et objekt, genbrug af en blueprint gennem en konstruktør, deling af metoder gennem prototypen og begrænsning af direkte adgang til interne data. Hvis du er ny til sproget, så start med dette introduktion til JavaScript og så vende tilbage hertil.

Sådan opretter du et objekt

Du kan oprette et objekt som dette:

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

ELLER ved at bruge den kortere objektbogstavelige form:

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

Adgang til objektegenskaber og -metoder

Du kan tilgå en egenskab for et objekt med punktumnotation, og du kan kalde en metode ved at tilføje parenteser:

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

Prøv selv dette eksempel:

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

Output:

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

💡 Tip: Eksemplet ovenfor bruger dokument.skrivning fordi det kører inde i editoren på denne side. I virkelige projekter foretrækkes konsol log til fejlfinding eller DOM-metoder som f.eks. tekstindhold, Fordi dokument.skrivning sletter siden, når den kaldes efter indlæsningen er færdig.

OOPS konstruktør

Det er ikke så nyttigt at oprette objekter ét ad gangen, fordi man så skal gentage den samme kode for hver elev. Det er her, objektkonstruktøren kommer ind i billedet. En konstruktør er en almindelig funktion, der kaldes med ny nøgleord; det definerer en objekttype, der kan genbruges for hver enkelt instans.

Inde i en konstruktør, denne refererer til det nye objekt, der bygges, så hver instans modtager sin egen kopi af de værdier, der sendes ind. Relaterede værdier, såsom mærker, gemmes ofte i en matrix så de kan tælles i én omgang.

Prøv selv dette eksempel:

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

Output:

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

Loop gennem et objekts egenskaber

Syntaks:

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

For/i loop bruges normalt til at gennemgå et objekts egenskaber. Du kan give det et hvilket som helst navn variabel, men objektnavnet skal matche et objekt, der allerede findes. Ved hver gennemgang holder variablen egenskaben nøgle som en streng, så værdien læses med parentesnotation, f.eks. medarbejder[x].

Prøv selv dette eksempel:

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

Output:

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

To punkter er værd at huske på. En for/in-løkke besøger også optællelige egenskaber, der er nedarvet gennem prototypekæden, så en vagt som f.eks. Objekt.prototype.harEgenEgenskab.kald(medarbejder, x) tilføjes ofte. For almindelige dataobjekter, Objekt.nøgler(medarbejder) or Objekt.poster(medarbejder) er normalt tydeligere, fordi begge kun returnerer objektets egne egenskaber.

Hvad er ES6-klassen syntaks i JavaManuskript?

ECMAScript 2015, almindeligvis kaldet ES6, tilføjede klasse nøgleord. En klasse er syntaktisk sukker over det prototypesystem, som konstruktørfunktionen ovenfor allerede bruger: ingen ny objektmodel blev introduceret. Kører type af studerende returnerer stadig strengen "function" i en klasse, og alle metoder, der er skrevet i klassens brødtekst, installeres på Student.prototype, hvilket betyder, at alle instanser deler én kopi af den metode i stedet for at hvert objekt bærer sin egen.

Syntaksen fjerner også nogle ujævnheder. Klassekroppe udføres altid i strict mode og kalder en klasse uden ny kaster en TypeError i stedet for lydløst at forurene det globale objekt, og gettere, settere og statiske medlemmer har et dedikeret sted at bo. Resultatet er kortere kode, der udtrykker den samme intention.

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

Output:

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

De sidste to linjer beviser pointen: klassen er stadig en funktion, og metoden på instansen er det samme funktionsobjekt, der er gemt på prototypen.

Konstruktørfunktion vs. ES6-klasse

Aspect Konstruktørfunktion ES6-klasse
Erklæring funktion Studerende(…) { } klasse Studerende { konstruktør(…) { } }
Metodeplacering Tildelt inde i funktionen, så hver instans får sin egen kopi, medmindre du skriver til Student.prototype Skrevet i klassens brødtekst og placeret automatisk på prototypen
Opkald uden nyt Kører lydløst og denne er udefineret eller det globale objekt Kaster en TypeError med det samme
hejsning Funktionsdeklarationer er hævet og kan bruges før definitionen I den tidsmæssige dødzone indtil evaluering
Streng tilstand Følger det omgivende skrift Altid streng
Arv Manual: Objekt.opret plus en ringe til forælderen udvider og super
Private data Lukninger eller en navngivningskonvention som f.eks. _balance Ægte private felter med #
Underliggende model prototyper Prototyper (identiske under kørsel)

Sådan implementerer du arv ved hjælp af extends og super

Arv tillader en specialiseret type at genbruge egenskaberne og metoderne fra en mere generel type. Før ES6 betød dette at forbinde prototypekæden manuelt i tre separate trin, og det var let at glemme et af dem.

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

Output:

I am Kevin White
true

udvider søgeordet udfører alle tre trin for dig, og super giver en nem måde at nå forælderen på. Der er to forskellige anvendelser af super:

  • super(…) som et opkald kører den overordnede konstruktør. Den er kun gyldig i en afledt konstruktør, og den skal køre, før du berører denne, ellers udløses en ReferenceError.
  • super.metode() da et egenskabsopslag kalder den overordnede version af en metode, som den underordnede har tilsidesat, hvilket er sådan du udvider adfærd i stedet for at erstatte den.
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);

Output:

I am Hannah Turner, roll number 23
true true
true

Den tredje linje er den vigtige. Den bekræfter det. udvider producerede præcis prototypen, der linkede til den manuelle version, der blev bygget i hånden, så klasser erstattede ikke prototypekæder. De skjulte blot standardteksten. Når en egenskab læses fra st, motoren kigger på instansen, og derefter Student.prototype, derefter Person.prototype, derefter Objekt.prototype, og stopper ved det første match; den søgesti er prototypekæden.

⚠️ Advarsel: Tildel aldrig Student.prototype = Person.prototypeDet gør, at begge typer deler ét objekt, så enhver metode, du tilføjer til det underordnede objekt, tilføjes også til det overordnede objekt. Objekt.opret(Person.prototype), eller blot bruge udvider.

Sådan opnår du indkapsling med private klassefelter

Indkapsling betyder keeping indre tilstand uden for rækkevidde, så den kun kan ændres gennem metoder, du kontrollerer. I årevis JavaScriptudviklere forfalskede det med et indledende understregningstegn, men et navn som f.eks. _balance er kun en konvention, og enhver kode kan stadig overskrive den. Moderne JavaScript tilbyder ægte privatliv: et felt, hvis navn begynder med # er kun tilgængelig inde i den klassekropp, der deklarerer den.

Private marker medfører tre konkrete fordele:

  • De skal deklareres i klassens brødtekst før brug, hvilket dokumenterer typens interne tilstand ét sted.
  • De er usynlige for Objektnøgler, JSON.stringify, og for/in, så den interne tilstand ikke lækker ind i serialiseret output.
  • At berøre et privat felt uden for klassen er en syntaksfejl, der opdages før koden overhovedet kører, snarere end en stille fejl.
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));

Output:

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

Balancen vises aldrig i Objektnøgler eller i JSON-strengen, og den negative indbetaling afvises af metoden i stedet for at beskadige feltet. få balance() accessor eksponerer en skrivebeskyttet visning, hvilket er den sædvanlige måde at publicere privat tilstand sikkert på.

Nu hvor objekter, prototyper, klasser, arv og indkapsling giver mening, så fortsæt med at bygge. Øv dig i det grundlæggende med disse praktisk JavaEksempler på scriptkode, arbejde igennem interaktiv JavaScriptøvelser, anvende klasser på algoritmer såsom hurtigsortering i JavaScript, rydd op i outputtet ved hjælp af JavaFormatering af scriptstrenge, og se hvor statisk typing ændrer objektdesign i TypeScript.

Ofte Stillede Spørgsmål

Den opretter et tomt objekt, linker det til konstruktørens prototype, kører konstruktøren med "dette" bundet til det objekt og returnerer det. Hvis konstruktøren returnerer et objekt eksplicit, returneres den værdi i stedet.

Brug en statisk metode, når logikken tilhører typen snarere end en enkelt instans, såsom en fabrik, der bygger et objekt fra JSON, eller en hjælper, der sammenligner to instanser. Statiske medlemmer kaldes på selve klassen.

Fordi "dette" bestemmes af, hvordan en metode kaldes, ikke hvor den er skrevet. Hvis en metode sendes som et callback, mistes receiveren. Ret det ved at binde metoden i konstruktøren eller ved at deklarere den som et klassefelt med en pilefunktion.

Nej. Deklarering af to metoder med samme navn bevarer kun den sidste. Simuler overbelastning ved at inspicere arguments.length, ved at bruge standardparameterværdier eller ved at acceptere et enkelt optionsobjekt med navngivne egenskaber.

Ja. AI-kodningsassistenter håndterer den refaktorering godt, fordi kortetping er mekanisk. Gennemgå altid resultatet, da assistenter kan overse metoder, der er knyttet til prototypen, uden for konstruktøren eller droppe et kald til den overordnede konstruktør.

Ja. Genereret kode skal stadig læses, gennemgås og fejlrettes. Ved at vide, hvordan prototyper, "dette" og private felter opfører sig, kan du få øje på et forkert forslag, før det når produktionsstart.

Opsummer dette indlæg med: