OOJS: Обектно-ориентиран JavaУрок за скриптове с пример

⚡ Умно обобщение

Обектно ориентиран JavaСкриптът моделира обекти от реалния свят като обекти, които носят свойства и методи. Тази страница обяснява литералите на обекти, конструкторските функции, прототипите, ES6 класовете, наследяването и частните полета с изпълними примери, които можете да изпълните и проверите.

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

Обектно ориентиран JavaКонцепции на скриптове (OOJS) с обекти, конструктори и класове

Какво представлява концепцията OOPS JavaСценарий?

Много пъти променливите или масивите не са достатъчни за симулиране на ситуации от реалния живот. JavaСкриптът ви позволява да създавате обекти, които действат като обекти от реалния живот. Студент или дом могат да бъдат обекти с много уникални характеристики. Можете да създавате свойства и методи на обектите си, за да улесните програмирането. Ако обектът ви е студент, той ще има свойства като собствено име, фамилия и идентификатор, както и методи като calculateRank или changeAddress. Ако обектът ви е дом, той ще има свойства като брой стаи, цвят на боята и местоположение, както и методи като calculateArea или changeOwner.

Обектно-ориентирано програмиране в JavaСледователно скриптът се основава на четири идеи, с които ще се срещнете на тази страница: групаping състояние и поведение в обект, повторно използване на план чрез конструктор, споделяне на методи чрез прототипа и ограничаване на директния достъп до вътрешни данни. Ако сте начинаещ в езика, започнете с това въведение в JavaСценарий и след това се върнете тук.

Как да създадете обект

Можете да създадете обект като този:

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

💡 Съвет: В горния пример се използва документ.запис защото се изпълнява в редактора на тази страница. В реални проекти предпочитайте конзолен дневник за дебъгване или DOM методи, като например textContent, Защото документ.запис изтрива страницата, когато бъде извикана след завършване на зареждането.

OOPS Конструктор

Създаването на обекти един по един не е толкова полезно, защото ще трябва да повтаряте един и същ код за всеки ученик. Тук се намесва конструкторът на обекти. Конструкторът е обикновена функция, която се извиква с на нови ключова дума; тя определя тип обект, който може да се използва повторно за всеки отделен екземпляр.

Вътре в конструктор, това се отнася до новия обект, който се изгражда, така че всеки екземпляр получава свое собствено копие на предадените стойности. Свързани стойности, като например маркери, често се съхраняват в масив така че да могат да бъдат сумирани с едно преминаване.

Опитайте сами този пример:

<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“ и всеки метод, написан в тялото на класа, е инсталиран на Студентски прототип, което означава, че всички екземпляри споделят едно копие на този метод, вместо всеки обект да носи свое собствено.

Синтаксисът също така премахва някои груби черти. Телата на класовете винаги се изпълняват в строг режим, извиквайки клас без на нови хвърля TypeError, вместо тихо да замърсява глобалния обект, а getter-ите, setter-ите и статичните членове имат специално място за живеене. Резултатът е по-кратък код, който изразява същото намерение.

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 клас
Декларация функция Студент(…) { } клас Студент { конструктор(…) { } }
Разположение на метода Присвоява се вътре във функцията, така че всеки екземпляр получава свое собствено копие, освен ако не пишете в Студентски прототип Написано в тялото на класа и автоматично поставено върху прототипа
Обаждане без ново Работи безшумно и това е недефиниран или е глобален обект Веднага хвърля TypeError
Подемно Декларациите на функции се повдигат и могат да се използват преди дефиницията. Във времевата мъртва зона до оценката
Строг режим Следва заобикалящия сценарий Винаги строг
наследяване Ръчно: Object.create плюс повикване на родителя простира намлява супер
Лични данни Затваряния или конвенция за именуване, като например баланс Истински частни полета с #
Базов модел прототипи Прототипи (идентични по време на изпълнение)

Как да имплементираме наследяване, използвайки 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, двигателят разглежда екземпляра, след което Студентски прототип, тогава Лице.прототип, тогава Обект.прототипи спира на първото съвпадение; този път на търсене е веригата от прототипи.

⚠️ Предупреждение: Никога не възлагайте Студент.прототип = Лице.прототипТова кара и двата типа да споделят един обект, така че всеки метод, който добавите към детето, се добавя и към родителя. Използвайте Object.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 низа, и отрицателният депозит се отхвърля от метода, вместо да повреди полето. получаване на баланс() accessor предоставя изглед само за четене, което е обичайният начин за безопасно публикуване на частно състояние.

Сега, когато обектите, прототипите, класовете, наследяването и капсулирането имат смисъл, продължете да изграждате. Упражнявайте основите с тези... практически JavaПримери за скриптов код, преработи интерактивен JavaУпражнения със скриптове, прилагайте класове към алгоритми като бързо сортиране в JavaСценарий, подредете изхода, използвайки JavaФорматиране на низове в скриптаи вижте как статично еping променя дизайна на обекта в TypeScript.

Въпроси и Отговори

Той създава празен обект, свързва го с прототипа на конструктора, изпълнява конструктора с „this“, обвързан с този обект, и го връща. Ако конструкторът върне обект изрично, вместо това се връща тази стойност.

Използвайте статичен метод, когато логиката принадлежи на типа, а не на отделен екземпляр, като например фабрика, която изгражда обект от JSON, или помощник, който сравнява два екземпляра. Статичните членове се извикват от самия клас.

Защото „това“ се определя от това как се извиква методът, а не от това къде е написан. Предаването на метод като обратно извикване губи получателя. Поправете го, като обвържете метода в конструктора или като го декларирате като поле на клас със стрелкова функция.

Не. Декларирането на два метода с едно и също име запазва само последния. Симулирайте претоварване, като проверите arguments.length, като използвате стойности на параметрите по подразбиране или като приемете един обект options с именувани свойства.

Да. Асистентите за кодиране с изкуствен интелект се справят добре с този рефакторинг, защото картатаping е механично. Винаги преглеждайте резултата, тъй като асистентите могат да пропуснат методи, прикачени към прототипа извън конструктора, или да прекъснат извикване към родителския конструктор.

Да. Генерираният код все още трябва да бъде прочетен, прегледан и отстранен за грешки. Познаването на това как се държат прототипите, „това“ и частните полета е това, което ви позволява да забележите неправилно предложение, преди то да достигне до продукция.

Обобщете тази публикация с: