diff --git a/1-js/09-classes/05-extend-natives/article.md b/1-js/09-classes/05-extend-natives/article.md index 28b4c6eb6..683f077c4 100644 --- a/1-js/09-classes/05-extend-natives/article.md +++ b/1-js/09-classes/05-extend-natives/article.md @@ -1,12 +1,11 @@ +# Udvid indbyggede klasser -# Extending built-in classes +Indbyggede klasser som Array, Map kan også udvides. -Built-in classes like Array, Map and others are extendable also. - -For instance, here `PowerArray` inherits from the native `Array`: +I dette eksempel, arver `PowerArray` fra den indbyggede `Array`: ```js run -// add one more method to it (can do more) +// tilføj en ekstra metode til den (nu kan den lidt mere) class PowerArray extends Array { isEmpty() { return this.length === 0; @@ -21,20 +20,21 @@ alert(filteredArr); // 10, 50 alert(filteredArr.isEmpty()); // false ``` -Please note a very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type `PowerArray`. Their internal implementation uses the object's `constructor` property for that. +Bemærk noget interessant. Indbyggede metoder som `filter`, `map` og andre -- returnerer nye objekter af præcis den arvede type `PowerArray`. Deres interne implementering bruger objektets `constructor`-egenskab til det. + +I eksemplet ovenfor, -In the example above, ```js arr.constructor === PowerArray ``` -When `arr.filter()` is called, it internally creates the new array of results using exactly `arr.constructor`, not basic `Array`. That's actually very cool, because we can keep using `PowerArray` methods further on the result. +Når `arr.filter()` kaldes, vil den internt oprette det nye array af resultater ved hjælp af denne `arr.constructor`, ikke fra den underliggende `Array`. Det er praktisk, fordi vi kan fortsætte med at bruge `PowerArray`-metoderne på yderligere oprettede objekter. -Even more, we can customize that behavior. +Vi kan også tilpasse adfærden. -We can add a special static getter `Symbol.species` to the class. If it exists, it should return the constructor that JavaScript will use internally to create new entities in `map`, `filter` and so on. +Vi kan tilføje en speciel statisk getter `Symbol.species` til klassen. Hvis den eksisterer, skal den returnere konstruktøren, som JavaScript vil bruge internt til at oprette nye entiteter i `map`, `filter` og så videre. -If we'd like built-in methods like `map` or `filter` to return regular arrays, we can return `Array` in `Symbol.species`, like here: +Hvis vi gerne vil have, at indbyggede metoder som `map` eller `filter` skal returnere almindelige arrays, kan vi returnere `Array` i `Symbol.species`, som her: ```js run class PowerArray extends Array { @@ -43,7 +43,7 @@ class PowerArray extends Array { } *!* - // built-in methods will use this as the constructor + // indyggede metoder vil bruge denne som konstruktør static get [Symbol.species]() { return Array; } @@ -53,37 +53,37 @@ class PowerArray extends Array { let arr = new PowerArray(1, 2, 5, 10, 50); alert(arr.isEmpty()); // false -// filter creates new array using arr.constructor[Symbol.species] as constructor +// filter opretter nyt array ved hjælp af arr.constructor[Symbol.species] som konstruktør let filteredArr = arr.filter(item => item >= 10); *!* -// filteredArr is not PowerArray, but Array +// filteredArr er ikke PowerArray, men Array */!* alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function ``` -As you can see, now `.filter` returns `Array`. So the extended functionality is not passed any further. +Som det ses vil `.filter` nu returnere `Array`. Så den udvidede funktionalitet bliver ikke videregivet. -```smart header="Other collections work similarly" -Other collections, such as `Map` and `Set`, work alike. They also use `Symbol.species`. +```smart header="Andre samlinger arbejder på samme måde" +Andre samlinger, såsom `Map` og `Set`, arbejder på samme måde. De bruger også `Symbol.species`. ``` -## No static inheritance in built-ins +## Ingen statisk nedarvning i indbyggede objekter -Built-in objects have their own static methods, for instance `Object.keys`, `Array.isArray` etc. +Indbyggede objekter har deres egne statiske metoder, for eksempel `Object.keys`, `Array.isArray` etc. -As we already know, native classes extend each other. For instance, `Array` extends `Object`. +Som vi allerede ved så udvider indbyggede klasser hinanden. For eksempel, `Array` udvider `Object`. -Normally, when one class extends another, both static and non-static methods are inherited. That was thoroughly explained in the article [](info:static-properties-methods#statics-and-inheritance). +Normalt, når en klasse udvider en anden, arves både statiske og ikke-statiske metoder. Dette blev grundigt forklaret i artiklen [](info:static-properties-methods#statics-and-inheritance). -But built-in classes are an exception. They don't inherit statics from each other. +Men indbyggede klasser er en undtagelse. De arver ikke statiske metoder fra hinanden. -For example, both `Array` and `Date` inherit from `Object`, so their instances have methods from `Object.prototype`. But `Array.[[Prototype]]` does not reference `Object`, so there's no, for instance, `Array.keys()` (or `Date.keys()`) static method. +For eksempel nedarver både `Array` og `Date` fra `Object`, så deres instanser har metoder fra `Object.prototype`. Men `Array.[[Prototype]]` refererer ikke til `Object`, så der er ingen, for eksempel, `Array.keys()` (eller `Date.keys()`) statisk metode. -Here's the picture structure for `Date` and `Object`: +Her er billedet for `Date` og `Object`: ![](object-date-inheritance.svg) -As you can see, there's no link between `Date` and `Object`. They are independent, only `Date.prototype` inherits from `Object.prototype`. +Som du ser her er der ingen sammenhæng mellem `Date` og `Object`. De er uafhængige, kun `Date.prototype` arver fra `Object.prototype`. -That's an important difference of inheritance between built-in objects compared to what we get with `extends`. +Det er en vigtig forskel i arv mellem indbyggede objekter og hvad vi får med `extends`. diff --git a/1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md b/1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md index d41d90edf..185543890 100644 --- a/1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md +++ b/1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md @@ -1,7 +1,7 @@ -Yeah, looks strange indeed. +Ja, det er lidt mærkeligt. -But `instanceof` does not care about the function, but rather about its `prototype`, that it matches against the prototype chain. +Men `instanceof` bryder sig ikke om funktionen, men snarere om dens `prototype`, som den matcher mod prototype-kæden. -And here `a.__proto__ == B.prototype`, so `instanceof` returns `true`. +Og her er `a.__proto__ == B.prototype`, så `instanceof` returnerer `true`. -So, by the logic of `instanceof`, the `prototype` actually defines the type, not the constructor function. +Så, hvis man følger logikken for `instanceof`, så definerer `prototype` faktisk typen, ikke constructor-funktionen. diff --git a/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md b/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md index 5b8dc7de3..810592865 100644 --- a/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md +++ b/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Strange instanceof +# Underlig instanceof -In the code below, why does `instanceof` return `true`? We can easily see that `a` is not created by `B()`. +Se på koden nedenfor. Hvorfor returnerer `instanceof` værdien `true`? Vi kan tydelig se, at `a` ikke er oprettet af `B()`. ```js run function A() {} diff --git a/1-js/09-classes/06-instanceof/article.md b/1-js/09-classes/06-instanceof/article.md index 00aee3376..5a2cf3de0 100644 --- a/1-js/09-classes/06-instanceof/article.md +++ b/1-js/09-classes/06-instanceof/article.md @@ -1,42 +1,42 @@ -# Class checking: "instanceof" +# Tjek en klasse: "instanceof" -The `instanceof` operator allows to check whether an object belongs to a certain class. It also takes inheritance into account. +Operatoren `instanceof` tillader at tjekke om et objekt tilhører en bestemt klasse. Den tager også arv i betragtning. -Such a check may be necessary in many cases. For example, it can be used for building a *polymorphic* function, the one that treats arguments differently depending on their type. +Sådan en tjek kan være nødvendig i mange tilfælde. For eksempel kan den bruges til at bygge en *polymorfisk* funktion, som behandler argumenter forskelligt afhængigt af deres type. -## The instanceof operator [#ref-instanceof] +## instanceof operatoren [#ref-instanceof] -The syntax is: +Syntaksen er: ```js obj instanceof Class ``` -It returns `true` if `obj` belongs to the `Class` or a class inheriting from it. +Den returnerer `true` hvis `obj` tilhører `Class` eller en klasse som arver fra den. -For instance: +For eksempel: ```js run class Rabbit {} let rabbit = new Rabbit(); -// is it an object of Rabbit class? +// er det et objekt der er skabt af Rabbit klassen? *!* alert( rabbit instanceof Rabbit ); // true */!* ``` -It also works with constructor functions: +Det virker også med constructor funktioner: ```js run *!* -// instead of class +// istedet for class function Rabbit() {} */!* alert( new Rabbit() instanceof Rabbit ); // true ``` -...And with built-in classes like `Array`: +... og med indbyggede klasser som `Array`: ```js run let arr = [1, 2, 3]; @@ -44,19 +44,19 @@ alert( arr instanceof Array ); // true alert( arr instanceof Object ); // true ``` -Please note that `arr` also belongs to the `Object` class. That's because `Array` prototypically inherits from `Object`. +Bemærk at `arr` også tilhører `Object`-klassen. Det er fordi `Array` prototypisk arver fra `Object`. -Normally, `instanceof` examines the prototype chain for the check. We can also set a custom logic in the static method `Symbol.hasInstance`. +Normalt undersøger `instanceof` prototype-kæden for tjekket. Vi kan også sætte en tilpasset logik i den statiske metode `Symbol.hasInstance`. -The algorithm of `obj instanceof Class` works roughly as follows: +Algoritmen for `obj instanceof Class` fungerer cirka sådan: -1. If there's a static method `Symbol.hasInstance`, then just call it: `Class[Symbol.hasInstance](obj)`. It should return either `true` or `false`, and we're done. That's how we can customize the behavior of `instanceof`. +1. Hvis der er en statisk metode `Symbol.hasInstance`, så kald den: `Class[Symbol.hasInstance](obj)`. Den skal returnere enten `true` eller `false`, og så er vi færdige. På den måde kan vi tilpasse adfærden for `instanceof`. - For example: + For eksempel: ```js run - // set up instanceof check that assumes that - // anything with canEat property is an animal + // sæt et instanceof tjek der regner med + // at alt med en canEat egenskab er et dyr class Animal { static [Symbol.hasInstance](obj) { if (obj.canEat) return true; @@ -65,24 +65,24 @@ The algorithm of `obj instanceof Class` works roughly as follows: let obj = { canEat: true }; - alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) is called + alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) kaldes ``` -2. Most classes do not have `Symbol.hasInstance`. In that case, the standard logic is used: `obj instanceof Class` checks whether `Class.prototype` is equal to one of the prototypes in the `obj` prototype chain. +2. De fleste klasser har ikke `Symbol.hasInstance`. I det tilfælde bruges standardlogikken: `obj instanceof Class` tjekker om `Class.prototype` er lig med en af prototyperne i `obj`'s prototypekæde. - In other words, compare one after another: + Med andre ord, sammenlign en efter en: ```js obj.__proto__ === Class.prototype? obj.__proto__.__proto__ === Class.prototype? obj.__proto__.__proto__.__proto__ === Class.prototype? ... - // if any answer is true, return true - // otherwise, if we reached the end of the chain, return false + // hvis en af svarene er true, returner true + // ellers, hvis vi nåede enden af kæden, returner false ``` - In the example above `rabbit.__proto__ === Rabbit.prototype`, so that gives the answer immediately. + I eksemplet ovenfor `rabbit.__proto__ === Rabbit.prototype`, så det giver svaret øjeblikkeligt. - In the case of an inheritance, the match will be at the second step: + I tilfældet af nedarvning vil matchningen være på det andet trin: ```js run class Animal {} @@ -99,70 +99,70 @@ The algorithm of `obj instanceof Class` works roughly as follows: */!* ``` -Here's the illustration of what `rabbit instanceof Animal` compares with `Animal.prototype`: +Her er en illustration af hvad`rabbit instanceof Animal` sammenligner med `Animal.prototype`: ![](instanceof.svg) -By the way, there's also a method [objA.isPrototypeOf(objB)](mdn:js/object/isPrototypeOf), that returns `true` if `objA` is somewhere in the chain of prototypes for `objB`. So the test of `obj instanceof Class` can be rephrased as `Class.prototype.isPrototypeOf(obj)`. +Forresten er der også en metode kaldet [objA.isPrototypeOf(objB)](mdn:js/object/isPrototypeOf), der returnerer `true` hvis `objA` er et eller andet sted i kæden af prototyper for `objB`. Så testen af `obj instanceof Class` kan omformuleres som `Class.prototype.isPrototypeOf(obj)`. -It's funny, but the `Class` constructor itself does not participate in the check! Only the chain of prototypes and `Class.prototype` matters. +Det er lidt morsomt, men `Class` konstruktøren deltager ikke selve i checket! Det er kun kæden af prototyper og `Class.prototype` der har betydning. -That can lead to interesting consequences when a `prototype` property is changed after the object is created. +Dette kan føre til interessante konsekvenser når en `prototype` egenskab ændres efter objektet er oprettet. -Like here: +Lige som her: ```js run function Rabbit() {} let rabbit = new Rabbit(); -// changed the prototype +// ændret prototypen Rabbit.prototype = {}; -// ...not a rabbit any more! +// ...ikke en "Rabbit" mere! *!* alert( rabbit instanceof Rabbit ); // false */!* ``` -## Bonus: Object.prototype.toString for the type +## Bonus: Object.prototype.toString for typen -We already know that plain objects are converted to string as `[object Object]`: +Vi ved allerede at rene objekter er konverteret til streng som `[object Object]`: ```js run let obj = {}; alert(obj); // [object Object] -alert(obj.toString()); // the same +alert(obj.toString()); // det samme: [object Object] ``` -That's their implementation of `toString`. But there's a hidden feature that makes `toString` actually much more powerful than that. We can use it as an extended `typeof` and an alternative for `instanceof`. +Det er deres implementering af `toString`. Men der er en skjult feature der faktisk gør `toString` mere kraftfuld end det. Vi kan bruge den som en udvidet `typeof` og et alternativ til `instanceof`. -Sounds strange? Indeed. Let's demystify. +Lyder det underligt? Ja, det gør! Men lad os afmystificere det. -By [specification](https://tc39.github.io/ecma262/#sec-object.prototype.tostring), the built-in `toString` can be extracted from the object and executed in the context of any other value. And its result depends on that value. +I [specifikationen](https://tc39.github.io/ecma262/#sec-object.prototype.tostring) står der, at den indbyggede `toString` kan trækkes fra objektet og udføres i konteksten af en anden værdi. Og dens resultat afhænger af den værdi. -- For a number, it will be `[object Number]` -- For a boolean, it will be `[object Boolean]` +- For et tal: `[object Number]` +- For en boolean: `[object Boolean]` - For `null`: `[object Null]` - For `undefined`: `[object Undefined]` - For arrays: `[object Array]` - ...etc (customizable). -Let's demonstrate: +Lad os demonstrere: ```js run -// copy toString method into a variable for convenience +// kopier toString til en variabel for nemhedens skyld let objectToString = Object.prototype.toString; -// what type is this? +// hvad type er den? let arr = []; alert( objectToString.call(arr) ); // [object *!*Array*/!*] ``` -Here we used [call](mdn:js/function/call) as described in the chapter [](info:call-apply-decorators) to execute the function `objectToString` in the context `this=arr`. +Her bruger vi [call](mdn:js/function/call) som beskrevet i kapitlet [](info:call-apply-decorators) til at udføre funktionen `objectToString` i konteksten `this=arr`. -Internally, the `toString` algorithm examines `this` and returns the corresponding result. More examples: +Internt undersøger `toString` algoritmen `this` og returnerer det tilsvarende resultat. Flere eksempler kunne være: ```js run let s = Object.prototype.toString; @@ -186,10 +186,10 @@ let user = { alert( {}.toString.call(user) ); // [object User] ``` -For most environment-specific objects, there is such a property. Here are some browser specific examples: +For de fleste miljøspecifikke objekter, er der en sådan egenskab. Her er nogle browser-specifikke eksempler: ```js run -// toStringTag for the environment-specific object and class: +// toStringTag til miljøspecifikke objekter og klasser: alert( window[Symbol.toStringTag]); // Window alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest @@ -197,22 +197,22 @@ alert( {}.toString.call(window) ); // [object Window] alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest] ``` -As you can see, the result is exactly `Symbol.toStringTag` (if exists), wrapped into `[object ...]`. +Som du kan se, er resultatet præcis `Symbol.toStringTag` (hvis den eksisterer), omskrevet til `[object ...]`. -At the end we have "typeof on steroids" that not only works for primitive data types, but also for built-in objects and even can be customized. +I enden har vi "typeof on steroids" som ikke kun virker for primitive data typer, men også for indbyggede objekter og endda kan tilpasses. -We can use `{}.toString.call` instead of `instanceof` for built-in objects when we want to get the type as a string rather than just to check. +Vi kan bruge `{}.toString.call` i stedet for `instanceof` for indbyggede objekter, når vi vil have typen som en streng i stedet for blot at tjekke. -## Summary +## Opsummering -Let's summarize the type-checking methods that we know: +Lad os opsummere de type-tjekker, vi kender: -| | works for | returns | +| | virker for | returnerer | |---------------|-------------|---------------| -| `typeof` | primitives | string | -| `{}.toString` | primitives, built-in objects, objects with `Symbol.toStringTag` | string | -| `instanceof` | objects | true/false | +| `typeof` | primitiver | string | +| `{}.toString` | primitiver, indbyggede objekter, objekter med `Symbol.toStringTag` | streng | +| `instanceof` | objekter | true/false | -As we can see, `{}.toString` is technically a "more advanced" `typeof`. +Som vi kan se er `{}.toString` teknisk set en "mere avanceret" `typeof`. -And `instanceof` operator really shines when we are working with a class hierarchy and want to check for the class taking into account inheritance. +Og `instanceof` operatoren kommer virkelig til sin ret når vi arbejder med en klassehierarki og vil tjekke for klassen med hensyn til arv. diff --git a/1-js/09-classes/07-mixins/article.md b/1-js/09-classes/07-mixins/article.md index 526b832ef..d68d32d32 100644 --- a/1-js/09-classes/07-mixins/article.md +++ b/1-js/09-classes/07-mixins/article.md @@ -1,22 +1,22 @@ # Mixins -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. +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. -But sometimes that feels limiting. For instance, we have a class `StreetSweeper` and a class `Bicycle`, and want to make their mix: a `StreetSweepingBicycle`. +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`. -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. +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. -There's a concept that can help here, called "mixins". +Der er et koncept der kan hjælpe her, kaldet "mixins". -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. +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. -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. +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. -## A mixin example +## Et mixin eksempel -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. +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. -For instance here the mixin `sayHiMixin` is used to add some "speech" for `User`: +Her er for eksempel en mixin kaldet `sayHiMixin` der kan bruges til at tilføje noget "tale" for klassen `User`: ```js run *!* @@ -24,10 +24,10 @@ For instance here the mixin `sayHiMixin` is used to add some "speech" for `User` */!* let sayHiMixin = { sayHi() { - alert(`Hello ${this.name}`); + alert(`Hej, ${this.name}!`); }, sayBye() { - alert(`Bye ${this.name}`); + alert(`Farvel, ${this.name}!`); } }; @@ -40,14 +40,14 @@ class User { } } -// copy the methods +// kopier metoderne Object.assign(User.prototype, sayHiMixin); -// now User can say hi -new User("Dude").sayHi(); // Hello Dude! +// nu kan User sige hej +new User("Karsten").sayHi(); // Hej, Karsten! ``` -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: +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: ```js class User extends Person { @@ -57,9 +57,9 @@ class User extends Person { Object.assign(User.prototype, sayHiMixin); ``` -Mixins can make use of inheritance inside themselves. +Mixins kan gøre brug af nedarvning inden i sig selv. -For instance, here `sayHiMixin` inherits from `sayMixin`: +For eksempel, her nedarver `sayHiMixin` fra `sayMixin`: ```js run let sayMixin = { @@ -69,16 +69,16 @@ let sayMixin = { }; let sayHiMixin = { - __proto__: sayMixin, // (or we could use Object.setPrototypeOf to set the prototype here) + __proto__: sayMixin, // (eller vi kan bruge Object.setPrototypeOf til at sætte prototype her) sayHi() { *!* - // call parent method + // kald forældermetoden */!* - super.say(`Hello ${this.name}`); // (*) + super.say(`Hej ${this.name}`); // (*) }, sayBye() { - super.say(`Bye ${this.name}`); // (*) + super.say(`Farvel ${this.name}`); // (*) } }; @@ -88,43 +88,43 @@ class User { } } -// copy the methods +// kopier metoderne Object.assign(User.prototype, sayHiMixin); -// now User can say hi -new User("Dude").sayHi(); // Hello Dude! +// nu kan User sige hej +new User("Karsten").sayHi(); // Hej, Karsten! ``` -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. +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. -Here's the diagram (see the right part): +Her er et diagram over det (se den højre del): ![](mixin-inheritance.svg) -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. +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. -As `super` looks for parent methods in `[[HomeObject]].[[Prototype]]`, that means it searches `sayHiMixin.[[Prototype]]`. +Da `super` leder efter forældremetoder i `[[HomeObject]].[[Prototype]]`, betyder det, at det søger i `sayHiMixin.[[Prototype]]`. ## EventMixin -Now let's make a mixin for real life. +Lad os nu oprette en mixin der bruges i virkeligheden. -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. +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. -- 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. -- 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. -- ...And the method `.off(name, handler)` that removes the `handler` listener. +- 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. +- 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`. +- ...Endelig er der `.off(name, handler)` som fjerner `handler`-lytteren. -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. +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. -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. +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. -Here's the code: +Her er koden til sådan en mixin: ```js run let eventMixin = { /** - * Subscribe to event, usage: + * Abonner på en hændelse, brug: * menu.on('select', function(item) { ... } */ on(eventName, handler) { @@ -136,7 +136,7 @@ let eventMixin = { }, /** - * Cancel the subscription, usage: + * Annuller abonnering, brug: * menu.off('select', handler) */ off(eventName, handler) { @@ -150,59 +150,59 @@ let eventMixin = { }, /** - * Generate an event with the given name and data + * Opret en hændelse med det givne navn og data * this.trigger('select', data1, data2); */ trigger(eventName, ...args) { if (!this._eventHandlers?.[eventName]) { - return; // no handlers for that event name + return; // ingen handlers der abonnerer på den hændelse } - // call the handlers + // kald gemte handlers this._eventHandlers[eventName].forEach(handler => handler.apply(this, args)); } }; ``` -- `.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. -- `.off(eventName, handler)` -- removes the function from the handlers list. -- `.trigger(eventName, ...args)` -- generates the event: all handlers from `_eventHandlers[eventName]` are called, with a list of arguments `...args`. +- `.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. +- `.off(eventName, handler)` -- fjerner funktionen fra handler-listen. +- `.trigger(eventName, ...args)` -- genererer hændelsen: alle handlers fra `_eventHandlers[eventName]` kaldes med en liste af argumenter `...args`. -Usage: +Brug af mixin'en er simpel: ```js run -// Make a class +// Opret en klasse der bruger mixin'en class Menu { choose(value) { this.trigger("select", value); } } -// Add the mixin with event-related methods +// Tilføj mixin'en med event-relaterede metoder Object.assign(Menu.prototype, eventMixin); let menu = new Menu(); -// add a handler, to be called on selection: +// tilføj en handler, der skal kaldes ved valg: *!* -menu.on("select", value => alert(`Value selected: ${value}`)); +menu.on("select", value => alert(`Valgte værdi: ${value}`)); */!* -// triggers the event => the handler above runs and shows: -// Value selected: 123 +// trigger en hændelse => handleren ovenfor kører og viser: +// Valgte værdi: 123 menu.choose("123"); ``` -Now, if we'd like any code to react to a menu selection, we can listen for it with `menu.on(...)`. +Nu, hvis vi vil have kode til at reagere på et menuvalg, kan vi lytte efter det med `menu.on(...)`. -And `eventMixin` mixin makes it easy to add such behavior to as many classes as we'd like, without interfering with the inheritance chain. +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. -## Summary +## Opsummering -*Mixin* -- is a generic object-oriented programming term: a class that contains methods for other classes. +*Mixin* -- er en generisk objektorienteret programmeringsterm: en klasse, der indeholder metoder for andre klasser. -Some other languages allow multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying methods into prototype. +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. -We can use mixins as a way to augment a class by adding multiple behaviors, like event-handling as we have seen above. +Vi kan bruge mixins som en måde at udvide en klasses adfærd, som f. eks. event-handling som vi har set ovenfor. -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. +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. diff --git a/1-js/09-classes/07-mixins/head.html b/1-js/09-classes/07-mixins/head.html index 20e3a6354..469a06571 100644 --- a/1-js/09-classes/07-mixins/head.html +++ b/1-js/09-classes/07-mixins/head.html @@ -1,43 +1,42 @@