Skip to content

Commit 6d7e882

Browse files
authored
Merge pull request #273 from ockley/master
Mixins
2 parents bc0adc1 + 793c6bb commit 6d7e882

2 files changed

Lines changed: 94 additions & 95 deletions

File tree

Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
11
# Mixins
22

3-
In JavaScript we can only inherit from a single object. There can be only one `[[Prototype]]` for an object. And a class may extend only one other class.
3+
I JavaScript kan vi kun nedarve fra et enkelt objekt. Der kan kun være en `[[Prototype]]` for et objekt. Og en klasse kan kun udvide en anden klasse.
44

5-
But sometimes that feels limiting. For instance, we have a class `StreetSweeper` and a class `Bicycle`, and want to make their mix: a `StreetSweepingBicycle`.
5+
Men nogle gange føles det begrænset. For eksempel har vi en klasse `StreetSweeper` og en klasse `Bicycle`, og vi vil lave deres kombination: en `StreetSweepingBicycle`.
66

7-
Or we have a class `User` and a class `EventEmitter` that implements event generation, and we'd like to add the functionality of `EventEmitter` to `User`, so that our users can emit events.
7+
Eller vi har en klasse `User` og en klasse `EventEmitter` som implementerer oprettelse af begivenheder, og vi vil gerne tilføje funktionaliteten af `EventEmitter` til `User`, så vores brugere kan sende begivenheder.
88

9-
There's a concept that can help here, called "mixins".
9+
Der er et koncept der kan hjælpe her, kaldet "mixins".
1010

11-
As defined in Wikipedia, a [mixin](https://en.wikipedia.org/wiki/Mixin) is a class containing methods that can be used by other classes without a need to inherit from it.
11+
Fra Wikipedia står der, at [mixin](https://en.wikipedia.org/wiki/Mixin) er en klasse der indeholder metoder, der kan bruges af andre klasser uden at skulle nedarve fra den.
1212

13-
In other words, a *mixin* provides methods that implement a certain behavior, but we do not use it alone, we use it to add the behavior to other classes.
13+
Med andre ord, en *mixin* leverer metoder, der implementerer en bestemt adfærd, men vi bruger den ikke alene, vi bruger den til at tilføje adfærd til andre klasser.
1414

15-
## A mixin example
15+
## Et mixin eksempel
1616

17-
The simplest way to implement a mixin in JavaScript is to make an object with useful methods, so that we can easily merge them into a prototype of any class.
17+
Den nemmeste måde at implementere en mixin i JavaScript er at lave et objekt med et par nyttige metoder, som vi så nemt kan flette dem ind i en prototype af enhver klasse.
1818

19-
For instance here the mixin `sayHiMixin` is used to add some "speech" for `User`:
19+
Her er for eksempel en mixin kaldet `sayHiMixin` der kan bruges til at tilføje noget "tale" for klassen `User`:
2020

2121
```js run
2222
*!*
2323
// mixin
2424
*/!*
2525
let sayHiMixin = {
2626
sayHi() {
27-
alert(`Hello ${this.name}`);
27+
alert(`Hej, ${this.name}!`);
2828
},
2929
sayBye() {
30-
alert(`Bye ${this.name}`);
30+
alert(`Farvel, ${this.name}!`);
3131
}
3232
};
3333

@@ -40,14 +40,14 @@ class User {
4040
}
4141
}
4242

43-
// copy the methods
43+
// kopier metoderne
4444
Object.assign(User.prototype, sayHiMixin);
4545

46-
// now User can say hi
47-
new User("Dude").sayHi(); // Hello Dude!
46+
// nu kan User sige hej
47+
new User("Karsten").sayHi(); // Hej, Karsten!
4848
```
4949

50-
There's no inheritance, but a simple method copying. So `User` may inherit from another class and also include the mixin to "mix-in" the additional methods, like this:
50+
Der er ingen nedarvning - kun simpel kopiering af metoder. Så `User` kan nedarve fra en anden klasse og også inkludere mixin'en for at "mikse" de yderligere metoder, som dette:
5151

5252
```js
5353
class User extends Person {
@@ -57,9 +57,9 @@ class User extends Person {
5757
Object.assign(User.prototype, sayHiMixin);
5858
```
5959

60-
Mixins can make use of inheritance inside themselves.
60+
Mixins kan gøre brug af nedarvning inden i sig selv.
6161

62-
For instance, here `sayHiMixin` inherits from `sayMixin`:
62+
For eksempel, her nedarver `sayHiMixin` fra `sayMixin`:
6363

6464
```js run
6565
let sayMixin = {
@@ -69,16 +69,16 @@ let sayMixin = {
6969
};
7070

7171
let sayHiMixin = {
72-
__proto__: sayMixin, // (or we could use Object.setPrototypeOf to set the prototype here)
72+
__proto__: sayMixin, // (eller vi kan bruge Object.setPrototypeOf til at sætte prototype her)
7373

7474
sayHi() {
7575
*!*
76-
// call parent method
76+
// kald forældermetoden
7777
*/!*
78-
super.say(`Hello ${this.name}`); // (*)
78+
super.say(`Hej ${this.name}`); // (*)
7979
},
8080
sayBye() {
81-
super.say(`Bye ${this.name}`); // (*)
81+
super.say(`Farvel ${this.name}`); // (*)
8282
}
8383
};
8484

@@ -88,43 +88,43 @@ class User {
8888
}
8989
}
9090

91-
// copy the methods
91+
// kopier metoderne
9292
Object.assign(User.prototype, sayHiMixin);
9393

94-
// now User can say hi
95-
new User("Dude").sayHi(); // Hello Dude!
94+
// nu kan User sige hej
95+
new User("Karsten").sayHi(); // Hej, Karsten!
9696
```
9797

98-
Please note that the call to the parent method `super.say()` from `sayHiMixin` (at lines labelled with `(*)`) looks for the method in the prototype of that mixin, not the class.
98+
Bemærk at kaldet til forældremetoden `super.say()` fra `sayHiMixin` (på linjer mærket med `(*)`) leder efter metoden i prototypen for den mixin, ikke klassen.
9999

100-
Here's the diagram (see the right part):
100+
Her er et diagram over det (se den højre del):
101101

102102
![](mixin-inheritance.svg)
103103

104-
That's because methods `sayHi` and `sayBye` were initially created in `sayHiMixin`. So even though they got copied, their `[[HomeObject]]` internal property references `sayHiMixin`, as shown in the picture above.
104+
Det er fordi metoderne `sayHi` og `sayBye` indledningsvis blev oprettet i `sayHiMixin`. Så selvom de blev kopieret, henviser deres `[[HomeObject]]` interne egenskab til `sayHiMixin`, som vist i billedet ovenfor.
105105

106-
As `super` looks for parent methods in `[[HomeObject]].[[Prototype]]`, that means it searches `sayHiMixin.[[Prototype]]`.
106+
Da `super` leder efter forældremetoder i `[[HomeObject]].[[Prototype]]`, betyder det, at det søger i `sayHiMixin.[[Prototype]]`.
107107

108108
## EventMixin
109109

110-
Now let's make a mixin for real life.
110+
Lad os nu oprette en mixin der bruges i virkeligheden.
111111

112-
An important feature of many browser objects (for instance) is that they can generate events. Events are a great way to "broadcast information" to anyone who wants it. So let's make a mixin that allows us to easily add event-related functions to any class/object.
112+
En vigtig mulighed for mange browserobjekter (for eksempel) er, at de kan generere hændelser. Hændelser er en fantastisk måde at "kommunisere information" til enhver, der ønsker det. Så lad os lave en mixin, der gør det nemt at tilføje event-relaterede funktioner til enhver klasse/objekt.
113113

114-
- The mixin will provide a method `.trigger(name, [...data])` to "generate an event" when something important happens to it. The `name` argument is a name of the event, optionally followed by additional arguments with event data.
115-
- Also the method `.on(name, handler)` that adds `handler` function as the listener to events with the given name. It will be called when an event with the given `name` triggers, and get the arguments from the `.trigger` call.
116-
- ...And the method `.off(name, handler)` that removes the `handler` listener.
114+
- Denne mixin vil tilbyde en metode `.trigger(name, [...data])` til at "generere en hændelse" når noget vigtigt sker med den. `name`-argumentet er et navn på hændelsen. Det næste argument er valgfrit og giver mulighed for at sende data med hændelsen.
115+
- Metoden `.on(name, handler)` tilføjer `handler`-funktionen som lytter til hændelser med det givne navn. Den vil blive kaldt når en hændelse med det givne `name` udløses, og får argumenterne fra kaldet til `.trigger`.
116+
- ...Endelig er der `.off(name, handler)` som fjerner `handler`-lytteren.
117117

118-
After adding the mixin, an object `user` will be able to generate an event `"login"` when the visitor logs in. And another object, say, `calendar` may want to listen for such events to load the calendar for the logged-in person.
118+
Efter at have tilføjet mixin'en, vil et objekt `user` være i stand til at generere en hændelse `"login"` når en besøgende logger ind. Et andet objekt, for eksempel `calendar` kan lytte efter sådan en hændelse for at indlæse kalenderen for den person der er logget ind.
119119

120-
Or, a `menu` can generate the event `"select"` when a menu item is selected, and other objects may assign handlers to react on that event. And so on.
120+
Eller en `menu`, kan generere hændelsen `"select"` når et menu-element er valgt, og andre objekter kan tildele funktioner (kaldet handlers) til at reagere på hændelsen. Og så videre.
121121

122-
Here's the code:
122+
Her er koden til sådan en mixin:
123123

124124
```js run
125125
let eventMixin = {
126126
/**
127-
* Subscribe to event, usage:
127+
* Abonner på en hændelse, brug:
128128
* menu.on('select', function(item) { ... }
129129
*/
130130
on(eventName, handler) {
@@ -136,7 +136,7 @@ let eventMixin = {
136136
},
137137

138138
/**
139-
* Cancel the subscription, usage:
139+
* Annuller abonnering, brug:
140140
* menu.off('select', handler)
141141
*/
142142
off(eventName, handler) {
@@ -150,59 +150,59 @@ let eventMixin = {
150150
},
151151

152152
/**
153-
* Generate an event with the given name and data
153+
* Opret en hændelse med det givne navn og data
154154
* this.trigger('select', data1, data2);
155155
*/
156156
trigger(eventName, ...args) {
157157
if (!this._eventHandlers?.[eventName]) {
158-
return; // no handlers for that event name
158+
return; // ingen handlers der abonnerer på den hændelse
159159
}
160160

161-
// call the handlers
161+
// kald gemte handlers
162162
this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
163163
}
164164
};
165165
```
166166
167167
168-
- `.on(eventName, handler)` -- assigns function `handler` to run when the event with that name occurs. Technically, there's an `_eventHandlers` property that stores an array of handlers for each event name, and it just adds it to the list.
169-
- `.off(eventName, handler)` -- removes the function from the handlers list.
170-
- `.trigger(eventName, ...args)` -- generates the event: all handlers from `_eventHandlers[eventName]` are called, with a list of arguments `...args`.
168+
- `.on(eventName, handler)` -- tildeler funktionen `handler` opgaven at køre når en hændelse med det givne navn opstår. Teknisk set, er der en `_eventHandlers`-egenskab, der gemmer en liste af handlers for hvert hændelsesnavn, og den tilføjer bare funktionen til listen.
169+
- `.off(eventName, handler)` -- fjerner funktionen fra handler-listen.
170+
- `.trigger(eventName, ...args)` -- genererer hændelsen: alle handlers fra `_eventHandlers[eventName]` kaldes med en liste af argumenter `...args`.
171171
172-
Usage:
172+
Brug af mixin'en er simpel:
173173
174174
```js run
175-
// Make a class
175+
// Opret en klasse der bruger mixin'en
176176
class Menu {
177177
choose(value) {
178178
this.trigger("select", value);
179179
}
180180
}
181-
// Add the mixin with event-related methods
181+
// Tilføj mixin'en med event-relaterede metoder
182182
Object.assign(Menu.prototype, eventMixin);
183183

184184
let menu = new Menu();
185185

186-
// add a handler, to be called on selection:
186+
// tilføj en handler, der skal kaldes ved valg:
187187
*!*
188-
menu.on("select", value => alert(`Value selected: ${value}`));
188+
menu.on("select", value => alert(`Valgte værdi: ${value}`));
189189
*/!*
190190

191-
// triggers the event => the handler above runs and shows:
192-
// Value selected: 123
191+
// trigger en hændelse => handleren ovenfor kører og viser:
192+
// Valgte værdi: 123
193193
menu.choose("123");
194194
```
195195
196-
Now, if we'd like any code to react to a menu selection, we can listen for it with `menu.on(...)`.
196+
Nu, hvis vi vil have kode til at reagere på et menuvalg, kan vi lytte efter det med `menu.on(...)`.
197197
198-
And `eventMixin` mixin makes it easy to add such behavior to as many classes as we'd like, without interfering with the inheritance chain.
198+
Derudover gør `eventMixin` mixin nemt at tilføje sådan adfærd til så mange klasser som vi ønsker, uden at påvirke arvekæden.
199199
200-
## Summary
200+
## Opsummering
201201
202-
*Mixin* -- is a generic object-oriented programming term: a class that contains methods for other classes.
202+
*Mixin* -- er en generisk objektorienteret programmeringsterm: en klasse, der indeholder metoder for andre klasser.
203203
204-
Some other languages allow multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying methods into prototype.
204+
Nogle andre sprog tillader nedarvning fra flere klasser. Det gør JavaScript ikke, men mixins kan implementere noget der minder om det ved at kopiere metoder ind i en prototype.
205205
206-
We can use mixins as a way to augment a class by adding multiple behaviors, like event-handling as we have seen above.
206+
Vi kan bruge mixins som en måde at udvide en klasses adfærd, som f. eks. event-handling som vi har set ovenfor.
207207
208-
Mixins may become a point of conflict if they accidentally overwrite existing class methods. So generally one should think well about the naming methods of a mixin, to minimize the probability of that happening.
208+
Mixins kan blive et problem, hvis de tilfældigt overskriver eksisterende klassemetoder. Så det er en god idé at tænke godt over navngivningen af metoderne i en mixin, for at minimere sandsynligheden for, at det sker.
Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,42 @@
11
<script>
2-
let eventMixin = {
3-
4-
/**
5-
* Subscribe to event, usage:
6-
* menu.on('select', function(item) { ... }
7-
*/
8-
on(eventName, handler) {
9-
if (!this._eventHandlers) this._eventHandlers = {};
10-
if (!this._eventHandlers[eventName]) {
11-
this._eventHandlers[eventName] = [];
12-
}
13-
this._eventHandlers[eventName].push(handler);
14-
},
2+
let eventMixin = {
3+
/**
4+
* Abonner på en hændelse, brug:
5+
* menu.on('select', function(item) { ... }
6+
*/
7+
on(eventName, handler) {
8+
if (!this._eventHandlers) this._eventHandlers = {};
9+
if (!this._eventHandlers[eventName]) {
10+
this._eventHandlers[eventName] = [];
11+
}
12+
this._eventHandlers[eventName].push(handler);
13+
},
1514

16-
/**
17-
* Cancel the subscription, usage:
18-
* menu.off('select', handler)
19-
*/
20-
off(eventName, handler) {
21-
let handlers = this._eventHandlers?.[eventName];
22-
if (!handlers) return;
23-
for(let i = 0; i < handlers.length; i++) {
24-
if (handlers[i] == handler) {
25-
handlers.splice(i--, 1);
15+
/**
16+
* Annuller abonnering, brug:
17+
* menu.off('select', handler)
18+
*/
19+
off(eventName, handler) {
20+
let handlers = this._eventHandlers?.[eventName];
21+
if (!handlers) return;
22+
for (let i = 0; i < handlers.length; i++) {
23+
if (handlers[i] == handler) {
24+
handlers.splice(i--, 1);
25+
}
2626
}
27-
}
28-
},
27+
},
2928

30-
/**
31-
* Generate the event and attach the data to it
32-
* this.trigger('select', data1, data2);
33-
*/
34-
trigger(eventName, ...args) {
35-
if (!this._eventHandlers || !this._eventHandlers[eventName]) {
36-
return; // no handlers for that event name
37-
}
29+
/**
30+
* Opret en hændelse og tilføj data til den
31+
* this.trigger('select', data1, data2);
32+
*/
33+
trigger(eventName, ...args) {
34+
if (!this._eventHandlers || !this._eventHandlers[eventName]) {
35+
return; // ingen handlers der abonnerer på den hændelse
36+
}
3837

39-
// call the handlers
40-
this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
41-
}
42-
};
38+
// kald de gemte handlers
39+
this._eventHandlers[eventName].forEach((handler) => handler.apply(this, args));
40+
},
41+
};
4342
</script>

0 commit comments

Comments
 (0)