Zoeken…


Syntaxis

  • klasse Foo {}
  • class Foo verlengt Bar {}
  • class Foo {constructor () {}}
  • class Foo {myMethod () {}}
  • class Foo {haal myProperty () {}}
  • class Foo {set myProperty (newValue) {}}
  • class Foo {static myStaticMethod () {}}
  • class Foo {static get myStaticProperty () {}}
  • const Foo = class Foo {};
  • const Foo = class {};

Opmerkingen

class ondersteuning is alleen aan JavaScript toegevoegd als onderdeel van de standaard van 2015.

Javascript-klassen zijn syntactische suiker boven JavaScript's reeds bestaande prototype-gebaseerde overerving. Deze nieuwe syntaxis introduceert geen nieuw objectgeoriënteerd overervingsmodel in JavaScript, maar een eenvoudigere manier om met objecten en overerving om te gaan. Een class declaratie is in wezen een afkorting voor handmatig definiëren constructeur function en eigenschappen toevoegen aan het prototype van de constructeur. Een belangrijk verschil is dat functies direct kunnen worden aangeroepen (zonder het new trefwoord), terwijl een direct geroepen klasse een uitzondering genereert.

class someClass {
    constructor () {}
    someMethod () {}
}
 
console.log(typeof someClass);               
console.log(someClass);
console.log(someClass === someClass.prototype.constructor);                         
console.log(someClass.prototype.someMethod);
 
// Output:
// function
// function someClass() { "use strict"; }
// true
// function () { "use strict"; }

Als u een eerdere versie van JavaScript gebruikt, hebt u een transpiler zoals of closing nodig om de code te compileren naar een versie die het doelplatform kan begrijpen.

Klasse Constructor

Het fundamentele deel van de meeste klassen is de constructor, die de initiële status van elke instantie instelt en alle parameters verwerkt die zijn doorgegeven bij het aanroepen van new .

Het is gedefinieerd in een class blok alsof je een methode met de naam definiëren constructor , al is het in feite behandeld als een speciaal geval.

class MyClass {
    constructor(option) {
        console.log(`Creating instance using ${option} option.`);
        this.option = option;
    }
}

Voorbeeld gebruik:

const foo = new MyClass('speedy'); // logs: "Creating instance using speedy option"

Een klein ding om op te merken is dat een klassenbouwer niet statisch kan worden gemaakt via het static trefwoord, zoals hieronder beschreven voor andere methoden.

Statische methoden

Statische methoden en eigenschappen worden gedefinieerd op de klasse / constructor zelf , niet op instantieobjecten. Deze worden gespecificeerd in een klassedefinitie met behulp van het static trefwoord.

class MyClass {
    static myStaticMethod() {
        return 'Hello';
    }

    static get myStaticProperty() {
        return 'Goodbye';
    }
}

console.log(MyClass.myStaticMethod()); // logs: "Hello"
console.log(MyClass.myStaticProperty); // logs: "Goodbye"

We kunnen zien dat statische eigenschappen niet zijn gedefinieerd op objectinstanties:

const myClassInstance = new MyClass();

console.log(myClassInstance.myStaticProperty); // logs: undefined

Ze zijn echter gedefinieerd op subklassen:

class MySubClass extends MyClass {};

console.log(MySubClass.myStaticMethod()); // logs: "Hello"
console.log(MySubClass.myStaticProperty); // logs: "Goodbye"

Getters en Setters

Met getters en setters kunt u aangepast gedrag definiëren voor het lezen en schrijven van een bepaalde eigenschap in uw klas. Voor de gebruiker zien ze er hetzelfde uit als elke typische eigenschap. Intern wordt echter een door u opgegeven aangepaste functie gebruikt om de waarde te bepalen wanneer de eigenschap wordt gebruikt (de getter) en om eventuele noodzakelijke wijzigingen voor te voeren wanneer de eigenschap wordt toegewezen (de setter).

In een class definitie wordt een getter geschreven als een no-argument methode voorafgegaan door de get trefwoord. Een setter is vergelijkbaar, behalve dat deze één argument accepteert (de nieuwe waarde die wordt toegewezen) en in plaats daarvan het set sleutelwoord wordt gebruikt.

Hier is een voorbeeldklasse die een getter en een setter biedt voor de eigenschap .name . Elke keer dat het wordt toegewezen, nemen we de nieuwe naam op in een interne .names_ array. Elke keer dat het wordt geopend, retourneren we de nieuwste naam.

class MyClass {
    constructor() {
        this.names_ = [];
    }

    set name(value) {
        this.names_.push(value);
    }

    get name() {
        return this.names_[this.names_.length - 1];
    }
}

const myClassInstance = new MyClass();
myClassInstance.name = 'Joe';
myClassInstance.name = 'Bob';

console.log(myClassInstance.name); // logs: "Bob"
console.log(myClassInstance.names_); // logs: ["Joe", "Bob"]

Als u alleen een setter definieert, zal een poging om toegang te krijgen tot de eigenschap altijd undefined terugkeren.

const classInstance = new class {
    set prop(value) {
        console.log('setting', value);
    }
};

classInstance.prop = 10; // logs: "setting", 10

console.log(classInstance.prop); // logs: undefined

Als u alleen een getter definieert, heeft een poging om de eigenschap toe te wijzen geen effect.

const classInstance = new class {
    get prop() {
        return 5;
    }
};

classInstance.prop = 10;

console.log(classInstance.prop); // logs: 5

Klasse-overerving

Overerving werkt net als in andere objectgeoriënteerde talen: methoden die in de superklasse zijn gedefinieerd, zijn toegankelijk in de uitbreidende subklasse.

Als de subklasse zijn eigen constructor aangeeft, moet deze de constructor van de ouders aanroepen via super() voordat deze hier toegang toe this .

class SuperClass {

    constructor() {
        this.logger = console.log;
    }

    log() {
        this.logger(`Hello ${this.name}`);
    }

}

class SubClass extends SuperClass {

    constructor() {
        super();
        this.name = 'subclass';
    }

}

const subClass = new SubClass();

subClass.log(); // logs: "Hello subclass"

Privé leden

JavaScript ondersteunt technisch geen privéleden als taalfunctie. Privacy - beschreven door Douglas Crockford - wordt in plaats daarvan geëmuleerd via sluitingen (behouden functiebereik) die elk bij elke instantiation-aanroep van een constructorfunctie worden gegenereerd.

Het voorbeeld van de Queue laat zien hoe, met constructorfuncties, de lokale status kan worden behouden en ook toegankelijk kan worden gemaakt via bevoorrechte methoden.

class Queue {

  constructor () {                    // - does generate a closure with each instantiation.

    const list = [];                  // - local state ("private member").

    this.enqueue = function (type) {  // - privileged public method
                                      //   accessing the local state
      list.push(type);                //   "writing" alike.
      return type;
    };
    this.dequeue = function () {      // - privileged public method
                                      //   accessing the local state
      return list.shift();            //   "reading / writing" alike.
    };
  }
}


var q = new Queue;            //
                              //
q.enqueue(9);                 // ... first in ...
q.enqueue(8);                 //
q.enqueue(7);                 //
                              //
console.log(q.dequeue());     // 9 ... first out.
console.log(q.dequeue());     // 8
console.log(q.dequeue());     // 7
console.log(q);               // {}
console.log(Object.keys(q));  // ["enqueue","dequeue"]

Bij elke instantiatie van een Queue genereert de constructor een afsluiting.

Dus zowel van een Queue eigen methoden soort van enqueue en dequeue (zie Object.keys(q) ) nog steeds toegang hebben tot list die nog steeds leeft in zijn omsluitende ruimte die op bouwtijd, is bewaard gebleven.

Gebruikmakend van dit patroon - het emuleren van privé-leden via bevoorrechte openbare methoden - moet men er rekening mee houden dat bij elke instantie extra geheugen zal worden verbruikt voor elke eigen eigenschapsmethode (het is code die niet kan worden gedeeld / hergebruikt). Hetzelfde geldt voor de hoeveelheid / grootte van de staat die binnen een dergelijke sluiting wordt opgeslagen.

Namen van dynamische methoden

Er is ook de mogelijkheid om uitdrukkingen te evalueren bij het benoemen van methoden vergelijkbaar met hoe u toegang kunt krijgen tot de eigenschappen van een object met [] . Dit kan handig zijn voor het hebben van dynamische eigenschapsnamen, maar wordt vaak gebruikt in combinatie met symbolen.

let METADATA = Symbol('metadata');

class Car {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
  
  // example using symbols
  [METADATA]() {
    return {
      make: this.make,
      model: this.model
    };
  }

  // you can also use any javascript expression

  // this one is just a string, and could also be defined with simply add()
  ["add"](a, b) {
    return a + b;
  }

  // this one is dynamically evaluated
  [1 + 2]() {
    return "three";
  }
}

let MazdaMPV = new Car("Mazda", "MPV");
MazdaMPV.add(4, 5); // 9
MazdaMPV[3](); // "three"
MazdaMPV[METADATA](); // { make: "Mazda", model: "MPV" }

methoden

Methoden kunnen in klassen worden gedefinieerd om een functie uit te voeren en optioneel een resultaat te retourneren.
Ze kunnen argumenten van de beller ontvangen.

class Something {
    constructor(data) {
        this.data = data
    }

    doSomething(text) {
        return {
            data: this.data,
            text
        }
    }
}

var s = new Something({})
s.doSomething("hi") // returns: { data: {}, text: "hi" }

Privégegevens beheren met klassen

Een van de meest voorkomende obstakels bij het gebruik van klassen is het vinden van de juiste aanpak voor het omgaan met privé-staten. Er zijn 4 algemene oplossingen voor het omgaan met privéstaten:

Symbolen gebruiken

Symbolen zijn nieuw primitief type geïntroduceerd in ES2015, zoals gedefinieerd bij MDN

Een symbool is een uniek en onveranderlijk gegevenstype dat kan worden gebruikt als een identificatie voor objecteigenschappen.

Wanneer het symbool als een eigenschappencode wordt gebruikt, is het niet opsombaar.

Als zodanig worden ze niet onthuld met behulp van for var in of Object.keys .

Zo kunnen we symbolen gebruiken om privégegevens op te slaan.

const topSecret = Symbol('topSecret'); // our private key; will only be accessible on the scope of the module file
export class SecretAgent{
    constructor(secret){
        this[topSecret] = secret; // we have access to the symbol key (closure)
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(topSecret[topSecret]); // we have access to topSecret
        };
    }
}

Omdat symbols uniek zijn, moeten we verwijzen naar het oorspronkelijke symbool om toegang te krijgen tot het privé-eigendom.

import {SecretAgent} from 'SecretAgent.js'
const agent = new SecretAgent('steal all the ice cream');
// ok lets try to get the secret out of him!
Object.keys(agent); // ['coverStory'] only cover story is public, our secret is kept.
agent[Symbol('topSecret')]; // undefined, as we said, symbols are always unique, so only the original symbol will help us to get the data.

Maar het is niet 100% privé; laten we die agent afbreken! We kunnen de methode Object.getOwnPropertySymbols gebruiken om de symbolen van het object op te halen.

const secretKeys = Object.getOwnPropertySymbols(agent);
agent[secretKeys[0]] // 'steal all the ice cream' , we got the secret.

WeakMaps gebruiken

WeakMap is een nieuw type object dat is toegevoegd voor es6.

Zoals gedefinieerd op MDN

Het WeakMap-object is een verzameling sleutel / waarde-paren waarin naar de sleutels zwak wordt verwezen. De sleutels moeten objecten zijn en de waarden kunnen willekeurige waarden zijn.

Een ander belangrijk kenmerk van WeakMap is, zoals gedefinieerd op MDN .

De sleutel in een WeakMap wordt zwak vastgehouden. Dit betekent dat, als er geen andere sterke verwijzingen naar de sleutel zijn, het hele item door de vuilnisman uit de WeakMap wordt verwijderd.

Het idee is om de WeakMap te gebruiken, als een statische kaart voor de hele klasse, om elke instantie als sleutel te houden en de privégegevens te behouden als een waarde voor die instantie-sleutel.

Dus alleen binnen de klas hebben we toegang tot de WeakMap collectie.

Laten we onze agent eens proberen, met WeakMap :

const topSecret = new WeakMap(); // will hold all private data of all instances.
export class SecretAgent{
    constructor(secret){
        topSecret.set(this,secret); // we use this, as the key, to set it on our instance private data
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(topSecret.get(this)); // we have access to topSecret
        };
    }
}

Omdat de const topSecret is gedefinieerd in onze module-afsluiting en omdat we deze niet hebben gekoppeld aan onze instantie-eigenschappen, is deze aanpak volledig privé en kunnen we de agent topSecret niet bereiken.

Definieer alle methoden in de constructor

Het idee hier is gewoon om al onze methoden en leden in de constructor te definiëren en de sluiting te gebruiken om toegang te krijgen tot privé-leden zonder ze this toe te wijzen.

   export class SecretAgent{
        constructor(secret){
            const topSecret = secret;
            this.coverStory = 'just a simple gardner';
            this.doMission = () => {
                figureWhatToDo(topSecret); // we have access to topSecret
            };
        }
    }

Ook in dit voorbeeld zijn de gegevens 100% privé en kunnen ze niet buiten de klas worden bereikt, dus onze agent is veilig.

Naamgevingsconventies gebruiken

We zullen beslissen dat elke eigenschap die privé is, wordt voorafgegaan door _ .

Merk op dat voor deze aanpak de gegevens niet echt privé zijn.

export class SecretAgent{
    constructor(secret){
        this._topSecret = secret; // it private by convention
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(this_topSecret); 
        };
    }
}

Klasse Naam bindend

De naam van ClassDeclaration is op verschillende manieren gebonden in verschillende bereiken -

  1. Het bereik waarin de klasse is gedefinieerd - let bindend
  2. Het bereik van de klasse zelf - binnen { en } in class {} - const bindend
class Foo {
  // Foo inside this block is a const binding
}
// Foo here is a let binding

Bijvoorbeeld,

class A {
  foo() {
    A = null; // will throw at runtime as A inside the class is a `const` binding
  }
}
A = null; // will NOT throw as A here is a `let` binding

Dit is niet hetzelfde voor een functie -

function A() {
  A = null; // works
}
A.prototype.foo = function foo() {
  A = null; // works
}
A = null; // works


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow