Prototypová dědičnost a třídy v JavaScriptu

Ahoj všichni, v tomto článku bychom diskutovali:

  • funkce konstruktoru,
  • Prototypy,
  • Dědičnost, třídy v JS

Toto jsou velmi důležité pojmy a pomohou vám porozumět objektově orientovanému programování (OOP) v JS. Takže pokračujte ve čtení až do konce a doufám, že se z toho něco naučíte.

Objekty

Již dříve jsme diskutovali o tom, jak vytvářet objekty a pracovat s nimi pomocí doslovného zápisu.

const phone = {
    RAM: "8GB",
    OS: "Andriod"
}

V praxi často potřebujeme vytvořit mnoho podobných objektů, jako je seznam telefonů, zaměstnanců atd. Můžeme toho dosáhnout vytvořením vlastních typů v JS pomocí funkce konstruktoru a poté z něj vytvořit více objektů. V jiných programovacích jazycích obecně používáme class k definování tohoto druhu vlastního typu, ale v JS je systém tříd postaven přímo pomocí funkcí.

Takže namísto přímého používání tříd v JS se můžeme naučit, jak udělat totéž pomocí constructor functions což je základ objektově orientovaného programování v JS.

Funkce konstruktoru

Funkce konstruktoru jsou jako běžné funkce s některými konvencemi:

  • Tyto funkce by měly být vyvolány pomocí new operátor.
  • Pojmenování těchto funkcí se zapisuje v jazyce CamelCase (začínající velkým písmenem, např. Zaměstnanec) podle konvence
  • Tyto funkce by neměly mít explicitní návratovou hodnotu
function Employee(name){
      this.name = name;
      this.role = "Developer";
}

A to je vytvořit objekt pomocí této funkce konstruktoru

const employee = new Employee("Souvik");
console.log(employee); // Employee {name: "Souvik", role: "Developer"}

this uvnitř definice funkce ukazuje na objekt, který byl vytvořen pomocí new klíčové slovo před funkcí konstruktoru při jejím vyvolání.

Takže co když nepoužijeme new klíčové slovo při volání funkce?

V takovém případě by byla funkce vyvolána jako běžná funkce, NEBYL vytvořen a vrácen nový objekt. Pochopme tuto část vyvoláním výše uvedené funkce bez new operátor:

const employee = Employee();
console.log(employee); // undefined

Jak můžete vidět, undefined by bylo vráceno, což ve výchozím nastavení vrací jakákoli běžná funkce. Také this by odkazovalo na globální objekt window protože funkce konstruktoru byla vyvolána jako běžná funkce.

Toto jsou následující new klíčové slovo je zodpovědné za při vyvolávání funkce konstruktoru:

  • Vytvořte nový objekt a přiřaďte jej this

  • Přidejte vlastnosti k objektu s danou hodnotou

  • Vraťte nově vytvořený objekt

this klíčové slovo v JS

Mluvili jsme o this klíčové slovo před a zjistil this se chová odlišně na základě implementace. Existují 4 způsoby volání funkce a this odkazuje v každém případě na jiný objekt.

  • Pokud voláte funkci konstruktoru, pak this nastaví na nově vytvořený objekt

  • Vyvolání funkce, která patří k objektu, by nastavilo this na samotný objekt, což se nazývá Implicitní vazba .

  • Pouhé vyvolání běžné funkce by nastavilo this na globální objekt window .

  • Poslední způsob vyvolání funkce nám umožňuje nastavit this sami pomocí call() , apply() a bind() metody – to je známé jako Explicitní vazba , mluvili o tom zde také dříve.

Prototypová dědičnost

Problém s funkcí konstruktoru je v tom, že pokud je ve funkci konstruktoru přítomna nějaká metoda, bude vytvořena pro každou instanci vytvořenou pomocí funkce konstruktoru.

function Employee(name){
    this.name = name;
    this.role = "Developer";
    this.printDetails = function (){
        console.log(`${this.name} works as a ${this.role}`)
    }
}

Abychom zefektivnili paměť, můžeme do prototype přidat metody vlastnost funkce konstruktoru, takže všechny instance funkce konstruktoru mohou sdílet stejné metody.

function Employee(name){
    this.name = name;
    this.role = "Developer";
}

Employee.prototype.printDetails = function (){
    console.log(`${this.name} works as a ${this.role}`)
}

const employee = new Employee("Souvik");
employee.printDetails(); // Souvik works as a Developer

Takže, co je prototyp?

Prototyp je pouze objekt a všechny objekty vytvořené funkcí konstruktoru jsou tajně propojeny s prototypem.

Prototyp také uchovává odkaz na svůj vlastní prototypový objekt. A prototyp prototypu je také spojen s jeho vlastním prototypem a tak dále. Takto tvoří prototypový řetězec .

JavaScript používá toto propojení mezi objektem a jeho prototypem k implementaci dědičnosti, která je známá jako Prototypální dědičnost .

Když se pokoušíme získat přístup k vlastnosti nebo metodě objektu,

  • snaží se to najít ve vlastních vlastnostech objektu. Jakékoli vlastnosti nebo metody definované v samotném objektu mají nejvyšší prioritu před definováním stejného jinde, stejně jako stínování proměnných v řetězci rozsahu, o kterém se zde diskutuje.

  • Pokud to nedostane ve vlastnostech objektu, pokusí se to najít v prototypu konstruktoru objektu.

  • Pokud tam není ani v prototypovém objektu, JavaScript engine bude pokračovat ve vyhledávání prototypového řetězce, aby získal hodnotu. Na konci řetězce je Object() objekt, nadřazený objekt nejvyšší úrovně – pokud vlastnost není nalezena ani tam, pak je vlastnost undefined .

Stále však vyvstává jedna otázka, jak je objekt vytvořený funkcí konstruktoru tajně spojen se svým prototypem?

Odpověď je, že jakýkoli objekt vytvořený funkcí konstruktoru je propojen se svým prototypem pomocí __proto__ vlastnost, která je vytvořena funkcí konstruktoru a přímo odkazuje na prototyp funkce konstruktoru.

console.log(employee.__proto__ === Employee.prototype); // true

Pokud potřebujeme zkontrolovat prototyp objektu, můžeme použít Object.getPrototypeOf() metoda pro totéž, která vezme objekt jako argument a vrátí prototyp tohoto objektu.

console.log(Employee.prototype === Object.getPrototypeOf(employee)); // true

Object.create()

Jak jsme probrali, pomocí __proto__ vlastnost není dobrým zvykem pro použití v kódu, takže totéž by se nemělo používat k implementaci dědičnosti nebo vytváření prototypového řetězce.

To je důvod, proč ES5 představil Object.create() metoda implementace prototypové dědičnosti .

Object.create() vezme objekt jako argument a vrátí nový objekt s jeho __proto__ nastaveno na objekt, který byl předán jako argument do Object.create() .

const person = {
    name: "Souvik",
    greet: function(){
        console.log(`Hi, I’m ${this.name}`);
    }
}

const teacher = Object.create(person);

teacher.teach = function (subject) {
    console.log(`I can teach ${subject}`);
}

teacher.greet(); // Hi, I'm Souvik
teacher.teach("JavaScript"); // I can teach JavaScript
console.log(Object.getPrototypeOf(teacher) === person); // true

Můžeme využít Object.create() následující způsob implementace dědičnosti.

function Animal(name){
    this.name = name;
}

Animal.prototype.walk = function (){
    console.log(`${this.name} can walk`);
}

function Dog(name, lifetime){
    Animal.call(this, name); // calling parent constructor function to initialize parent properties for child objects
    this.lives = lifetime;
} 

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.details = function(){
    console.log(`${this.name} can live for ~${this.lives} years`);
}

const dog = new Dog("Dobby", 10);
dog.walk(); // Dobby can walk
dog.details(); // Dobby can live for ~10 years

Tímto způsobem Pes přebírá vlastnosti a metody z Animal pomocí prototypové dědičnosti . Ale to je trochu složité a podrobné.

To je důvod, proč ES6 zavádí class a extends klíčové slovo pro zjednodušení implementace dědičnosti v JS. Třídy v JS jsou speciální funkce. A stejná implementace pomocí class by vypadal takto:

class Animal{
    constructor(name){
        this.name = name;
    }

    walk(){
        console.log(`${this.name} walks`);
    }
}

class Dog extends Animal{
    constructor(name, lifetime){
        super(name);
        this.lives = lifetime;
    }

    details(){
        console.log(`${this.name} can live for ~${this.lives} years`);  
    }
}

const dog = new Dog("Dobby", 10);
dog.walk(); // Dobby can walk
dog.details(); // Dobby can live for ~10 years
console.log(typeof Animal); // function

To je vše 😀. Děkuji, že jste dočetli až do teď🙏.

Pokud si o nich chcete přečíst více, podívejte se na OOP v JS MDN, Objektové prototypy MDN, Dědičnost v JS MDN, Třídy MDN

Sdílejte tento blog se svou sítí, pokud to považujete za užitečné, a pokud máte o tématu nějaké pochybnosti, neváhejte se vyjádřit.

Můžete se se mnou spojit 👋 na GitHubu, Twitteru, Linkedinu