Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork3
Mixins#188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Merged
Uh oh!
There was an error while loading.Please reload this page.
Merged
Mixins#188
Changes fromall commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
204 changes: 102 additions & 102 deletions1-js/09-classes/07-mixins/article.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,208 +1,208 @@ | ||
#Mixiny | ||
V JavaScriptu můžeme dědit jen z jednoho objektu. Objekt může mít jen jeden `[[Prototype]]`a třída může rozšiřovat pouze jednu jinou třídu. | ||
To se však někdy může zdát omezující. Máme například třídu `ZametačUlic` a třídu `Bicykl` a chtěli bychom vytvořit jejich směs: `ZametacíBicykl`. | ||
Nebo máme třídu `Uživatel` a třídu `GenerátorUdálostí`, která implementuje generování událostí, a rádi bychom přidali funkcionalitu třídy `GenerátorUdálostí` do třídy `Uživatel`,aby naši uživatelé mohli generovat události. | ||
Koncept, který nám s tím pomůže, existuje a nazývá se „mixiny“. | ||
Jak je definováno ve Wikipedii,[mixin](https://cs.wikipedia.org/wiki/Mixin)je třída obsahující metody, které mohou být používány v jiných třídách, aniž by z ní tyto třídy musely být zděděny. | ||
Jinými slovy,*mixin*poskytuje metody, které implementují určité chování, ale nepoužíváme ho samostatně, nýbrž přidáváme jeho chování do jiných tříd. | ||
##Příklad mixinu | ||
Nejjednodušší způsob, jak implementovatmixinv JavaScriptu, je vytvořit objekt s užitečnými metodami, abychom je mohli snadno připojit do prototypu libovolné třídy. | ||
Například zde je použit mixin `mixinŘekniAhoj`, aby do třídy `Uživatel` přidal nějaké „mluvení“: | ||
```js run | ||
*!* | ||
// mixin | ||
*/!* | ||
letmixinŘekniAhoj = { | ||
řekniAhoj() { | ||
alert(`Ahoj ${this.jméno}`); | ||
}, | ||
řekniNashle() { | ||
alert(`Nashle ${this.jméno}`); | ||
} | ||
}; | ||
*!* | ||
//použití: | ||
*/!* | ||
classUživatel { | ||
constructor(jméno) { | ||
this.jméno =jméno; | ||
} | ||
} | ||
//zkopírujeme metody | ||
Object.assign(Uživatel.prototype,mixinŘekniAhoj); | ||
//nyní Uživatel může říci ahoj | ||
newUživatel("Jan").řekniAhoj(); //Ahoj Jan | ||
``` | ||
Není tady žádná dědičnost, ale jen prosté kopírování metod. `Uživatel` tedy může dědit z jiné třídy a současně zahrnout mixin, aby „přimíchal“ („mix-in“) další metody, například: | ||
```js | ||
classUživatel extendsOsoba { | ||
// ... | ||
} | ||
Object.assign(Uživatel.prototype,mixinŘekniAhoj); | ||
``` | ||
Mixiny mohou využívat dědičnost mezi sebou. | ||
Například zde `mixinŘekniAhoj` dědí z `mixinŘekni`: | ||
```js run | ||
letmixinŘekni = { | ||
řekni(věta) { | ||
alert(věta); | ||
} | ||
}; | ||
letmixinŘekniAhoj = { | ||
__proto__:mixinŘekni, // (nebo můžeme k nastavení prototypu použítObject.setPrototypeOf) | ||
řekniAhoj() { | ||
*!* | ||
//volání rodičovské metody | ||
*/!* | ||
super.řekni(`Ahoj ${this.jméno}`); // (*) | ||
}, | ||
řekniNashle() { | ||
super.řekni(`Nashle ${this.jméno}`); // (*) | ||
} | ||
}; | ||
classUživatel { | ||
constructor(jméno) { | ||
this.jméno =jméno; | ||
} | ||
} | ||
//zkopírujeme metody | ||
Object.assign(Uživatel.prototype,mixinŘekniAhoj); | ||
//nyní Uživatel může říci ahoj | ||
newUživatel("Jan").řekniAhoj(); //Ahoj Jan | ||
``` | ||
Prosíme všimněte si, že volání rodičovské metody`super.řekni()`z `mixinŘekniAhoj` (na řádcích označených`(*)`)hledá metodu v prototypu onoho mixinu, ne této třídy. | ||
Zde je diagram (viz pravou část): | ||
 | ||
Je to proto, že metody `řekniAhoj` a `řekniNashle` byly původně vytvořeny v `mixinŘekniAhoj`.I když jsou tedy zkopírovány, jejich interní vlastnost`[[HomeObject]]`se odkazuje na `mixinŘekniAhoj`,jak je vidět na uvedeném obrázku. | ||
Když `super`hledá rodičovské metody v`[[HomeObject]].[[Prototype]]`,znamená to, že prohledává `mixinŘekniAhoj.[[Prototype]]`. | ||
##MixinUdálosti | ||
Vytvořme nynímixinpro skutečný život. | ||
Důležitou vlastností mnoha objektů prohlížeče (například) je, že mohou generovat události. Události jsou skvělý způsob, jak „vysílat informaci“ každému, kdo ji chce. Vytvořme tedy mixin, který nám umožní snadno přidat funkce vztažené k události do jakékoli třídy nebo objektu. | ||
-Mixin bude poskytovat metodu `.spusť(název, [...data])`, která bude „generovat událost“, když se stane něco důležitého. Argument `název` je název události, za nímž mohou následovat další argumenty s daty události. | ||
-Dále metodu `.zapni(název, handler)`, která přidá funkci`handler`jako posluchače událostí se zadaným názvem. Funkce `handler` bude volána, když se spustí událost se zadaným názvem `název`, a převezme argumenty z volání `.spusť`. | ||
- ...A metodu `.vypni(název, handler)`, která odstraní posluchače `handler`. | ||
Po přidání mixinu bude objekt `uživatel` moci generovat událost `"přihlášen"`, když se uživatel přihlásí. A jiný objekt, třeba `kalendář`, bude moci takovým událostem naslouchat, aby pak načetl kalendář pro přihlášenou osobu. | ||
Nebo`menu`může generovat událost `"vybrán"`, když je vybrán jeho prvek, a jiné objekty mohou přiřazovat handlery, které budou na tuto událost reagovat. A tak dále. | ||
Zde je kód: | ||
```js run | ||
letmixinUdálosti = { | ||
/** | ||
*Přihlášení k naslouchání události, použití: | ||
* menu.zapni('vybrán', function(prvek) { ... }) | ||
*/ | ||
zapni(názevUdálosti, handler) { | ||
if (!this._handleryUdálostí) this._handleryUdálostí = {}; | ||
if (!this._handleryUdálostí[názevUdálosti]) { | ||
this._handleryUdálostí[názevUdálosti] = []; | ||
} | ||
this._handleryUdálostí[názevUdálosti].push(handler); | ||
}, | ||
/** | ||
*Odhlášení z naslouchání události, použití: | ||
* menu.vypni('vybrán', handler) | ||
*/ | ||
vypni(názevUdálosti, handler) { | ||
lethandlery = this._handleryUdálostí?.[názevUdálosti]; | ||
if (!handlery) return; | ||
for (let i = 0; i <handlery.length; i++) { | ||
if (handlery[i] === handler) { | ||
handlery.splice(i--, 1); | ||
} | ||
} | ||
}, | ||
/** | ||
*Generování události se zadaným názvem a daty | ||
* this.spusť('vybrán', data1, data2); | ||
*/ | ||
spusť(názevUdálosti, ...argumenty) { | ||
if (!this._handleryUdálostí?.[názevUdálosti]) { | ||
return; //pro událost s tímto názvem nejsou žádné handlery | ||
} | ||
//volání handlerů | ||
this._handleryUdálostí[názevUdálosti].forEach(handler => handler.apply(this,argumenty)); | ||
} | ||
}; | ||
``` | ||
- `.zapni(názevUdálosti, handler)` --přiřadí funkci `handler`, která se má spustit vždy, když nastane událost s uvedeným názvem. Technicky je zde vlastnost `_handleryUdálostí`, do níž se ukládá pole handlerů pro každý název události, a funkce je prostě přidána do tohoto seznamu. | ||
- `.vypni(názevUdálosti, handler)` --odstraní funkci ze seznamu handlerů. | ||
- `.spusť(názevUdálosti, ...argumenty)` --generuje událost: všechny handlery z `_handleryUdálostí[názevUdálosti]`jsou volány se seznamem argumentů`...argumenty`. | ||
Použití: | ||
```js run | ||
//Vytvoříme třídu | ||
class Menu { | ||
vyber(hodnota) { | ||
this.spusť("vybrán",hodnota); | ||
} | ||
} | ||
//Přidámemixins metodami vztahujícími se k událostem | ||
Object.assign(Menu.prototype,mixinUdálosti); | ||
let menu = new Menu(); | ||
//přidámehandler,který bude volán při výběru: | ||
*!* | ||
menu.zapni("vybrán",hodnota => alert(`Vybrána hodnota: ${hodnota}`)); | ||
*/!* | ||
//spustí událost=>výše uvedenýhandlerse spustí a zobrazí: | ||
//Vybrána hodnota: 123 | ||
menu.vyber("123"); | ||
``` | ||
Kdybychom nyní chtěli přidat jakýkoli kód, který bude reagovat na výběr z menu, můžeme mu naslouchat pomocí`menu.zapni(...)`. | ||
A mixin `mixinUdálosti` usnadňuje přidání takového chování do tolika tříd, do kolika bychom chtěli, aniž bychom narušovali řetězec dědičnosti. | ||
##Shrnutí | ||
*Mixin* --je generický pojem objektově orientovaného programování: třída, která obsahuje metody pro jiné třídy. | ||
Některé jiné jazyky umožňují vícenásobnou dědičnost. JavaScriptji nepodporuje, ale mixiny mohou být implementovány zkopírováním metod do prototypu. | ||
Mixiny můžeme používat jako způsob rozšiřování třídy přidáváním dalšího chování, například ošetřování událostí, jak jsme viděli výše. | ||
Mixiny se mohou stát příčinou konfliktu, jestliže náhodou přepíší již existující metody třídy. Obecně bychom si tedy měli dobře rozmyslet názvy metod v mixinu, abychom minimalizovali pravděpodobnost, že se tak stane. |
47 changes: 23 additions & 24 deletions1-js/09-classes/07-mixins/head.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,42 @@ | ||
<script> | ||
let mixinUdálosti = { | ||
/** | ||
*Přihlášení k naslouchání události, použití: | ||
* menu.zapni('vybrán', function(prvek) { ... }) | ||
*/ | ||
zapni(názevUdálosti, handler) { | ||
if (!this._handleryUdálostí) this._handleryUdálostí = {}; | ||
if (!this._handleryUdálostí[názevUdálosti]) { | ||
this._handleryUdálostí[názevUdálosti] = []; | ||
} | ||
this._handleryUdálostí[názevUdálosti].push(handler); | ||
}, | ||
/** | ||
*Odhlášení z naslouchání události, použití: | ||
* menu.vypni('vybrán', handler) | ||
*/ | ||
vypni(názevUdálosti, handler) { | ||
lethandlery = this._handleryUdálostí?.[názevUdálosti]; | ||
if (!handlery) return; | ||
for(let i = 0; i <handlery.length; i++) { | ||
if (handlery[i]=== handler) { | ||
handlery.splice(i--, 1); | ||
} | ||
} | ||
}, | ||
/** | ||
*Generování události se zadaným názvem a daty | ||
* this.spusť('vybrán', data1, data2); | ||
*/ | ||
spusť(názevUdálosti, ...argumenty) { | ||
if (!this._handleryUdálostí?.[názevUdálosti]) { | ||
return; //pro událost s tímto názvem nejsou žádné handlery | ||
} | ||
//volání handlerů | ||
this._handleryUdálostí[názevUdálosti].forEach(handler => handler.apply(this,argumenty)); | ||
} | ||
}; | ||
</script> |
Oops, something went wrong.
Uh oh!
There was an error while loading.Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.