class: center, middle, lnu-background-yellow ### Grundläggande programmering (1DV021) # Föreläsning 6 --- class: lnu-background-cc ### Upphovsrätt för detta verk Detta verk är framtaget av Mats Loock i anslutning till kursen Grundläggande programmering (1DV021) vid Linnéuniversitetet. Allt innehåll i detta verk förutom fotografier, ikoner, bild på kurslitteraturen samt Linnéuniversitetets logotyp och symbol, är licensierad under en
Creative Commons Erkännande 4.0 Internationell Licens
. #### Du får använda detta verk så här - kopiera hela eller delar av innehållet - sprida hela eller delar av innehållet - visa hela eller delar av innehållet offentligt och digitalt - konvertera innehållet till annat format - du får även göra om innehållet Om du förändrar innehållet så ta inte med fotografier, ikoner, bild på kurslitteraturen samt Linnéuniversitetets logotyp och symbol i din nya version! Vid all användning måste du ange källan: ”Linnéuniversitetet – Grundläggande programmering (1DV021)” och en länk till https://coursepress.lnu.se/kurs/grundlaggande-programmering och till Creative Common-licensen här ovan. --- # Vad är avsikten med koden egentligen? ``` let value const getNumber = function () { return Math.floor(Math.random() * 6) + 1 } value = getNumber() console.log(value) // OUTPUT: 4 ``` - Att skriva större program bestående av kod med fristående variabler och funktioner blir ohållbart i längden. - Vi behöver ett nytt sätt att organisera vår kod så att den blir tydligare! --- #Hur kan vi skriva tydligare kod?
Die
(
tärning
)
``` let value const getNumber = function () { return Math.floor(Math.random() * 6) + 1 } value = getNumber() console.log(value) // OUTPUT: 4 ```
- Genom att på något sätt kapsla in variabler och funktioner blir koden tydligare. -
Objekt kan
användas till att
kapsla in variabler och funktioner
; objekt kan både innehålla variabler (egenskaper) och funktioner (metoder). --- # Skapa ett objekt ``` const die = { faceValue: undefined, roll: function () { this.faceValue = Math.floor(Math.random() * 6) + 1 } } die.roll() console.log(die.faceValue) // OUTPUT: 4 ``` - Ett Object-objekt används till att kapsla in... - ...tärningens aktuella värde (egenskapen `faceValue`). - ...att tärningen kan kastas (metoden `roll`), vilket ändrar tärningens värde. - Vanligt sätt att skapa __ett__ objekt, MEN... - ...behöver flera objekt skapas måste koden dupliceras! --- # this ``` const roll = function () { this.faceValue = Math.floor(Math.random() * 6) + 1 } const die = { faceValue: undefined } const die2 = { faceValue: undefined, roll } roll.call(die) // i roll() kommer this att referera till samma objekt som die die2.roll() // i roll() kommer this att referera till samma objekt som die2 console.log(die.faceValue) // OUTPUT: 4 console.log(die2.faceValue) // OUTPUT: 2 ``` - `this` refererar till objektet som funktionen anropades för. --- # Funktion som använder `this` ``` // Skapar objekt som anropar funktion som använder this. const smallTalk = function (greet = 'Hej!') { console.log(greet + ' Jag, ' + this.name + ', är ' + this.age + ' år.') } const simplePerson = { name: 'Nisse', age: 42, talk: smallTalk } simplePerson.talk() // OUTPUT: Hej! Jag, Nisse, är 42 år. const anotherSimplePerson = { name: 'Ellen', age: 7 } smallTalk.apply(anotherSimplePerson, ['Tjabba!']) // OUTPUT: Tjabba! Jag, Ellen, är 7 år. smallTalk.call(anotherSimplePerson, 'Hallå!') // OUTPUT: Hallå! Jag, Ellen, är 7 år. ``` - `Function.apply()` kräver att andra parametern är en array. Syntax: `func.apply(thisArg, [argsArray])` - `Function.call()` anropas med kommaseparerade argument. Syntax: `function.call(thisArg, arg1, arg2, ...)` --- # Designmönstret "_Factory_" - Funktionen `createDie` löser problemet med att skapa flera liknande objekt, MEN... - ...av vilken typ är objekten som skapas? Jo, `Object`! Inte `Die`! ``` const createDie = function () { const obj = { faceValue: undefined, roll: function () { this.faceValue = Math.floor(Math.random() * 6) + 1 } } return obj } const die = createDie() die.roll() console.log(die.faceValue) // OUTPUT: 3 console.log(typeof die) // OUTPUT: object console.log(die.constructor) // OUTPUT: [Function: Object] console.log(Object.getOwnPropertyNames(die)) // OUTPUT: [ 'faceValue', 'roll' ] ``` --- # Designmönstret "_Constructor_" - Används nyckleordet `new` före en funktion som anropas betraktas funktionen som en konstruktorfunktion. - Ett objekt som skapas med `new` sägs vara en __instans__ av dess konstruktor. ``` function Die () { this.faceValue = undefined this.roll = function () { this.faceValue = Math.floor(Math.random() * 6) + 1 } } const die = new Die() // die refererar till en instans av Die die.roll() console.log(die.faceValue) // OUTPUT: 4 console.log(typeof die) // OUTPUT: object console.log(die.constructor) // OUTPUT: [Function: Die] console.log(Object.getOwnPropertyNames(die)) // OUTPUT: [ 'faceValue', 'roll' ] ``` --- # Konstruktorfunktion med parametrar ``` function SimplePerson (name, age) { this.name = name this.age = age this.talk = function () { console.log('Jag, ' + this.name + ', är ' + this.age + ' år.') } } const simplePerson1 = new SimplePerson('Nisse', 42) const simplePerson2 = new SimplePerson('Ellen', 7) simplePerson1.talk() // OUTPUT: Jag, Nisse, är 42 år. simplePerson2.talk() // OUTPUT: Jag, Ellen, är 7 år. console.log(Object.getOwnPropertyNames(simplePerson1)) // OUTPUT: [ 'name', 'age', 'talk' ] ``` - Två identiska objekt skapas, så när på värdena egenskaperna `name` och `age` har. - __OBS!__ Metoden `talk` är en del av varje instans av `SimplePerson`; det finns alltså två exakt lika upplagor av metoden. --- # Designmönstret "_Constructor/Prototype_" ``` function Die () { this.faceValue = undefined } Die.prototype.roll = function () { this.faceValue = Math.floor(Math.random() * 6) + 1 } const die = new Die() die.roll() console.log(die.faceValue) // OUTPUT: 4 console.log(typeof die) // OUTPUT: object console.log(die.constructor) // OUTPUT: [Function: Die] console.log(Object.getOwnPropertyNames(die)) // OUTPUT: [ 'faceValue' ] console.log(Object.getOwnPropertyNames(Die.prototype)) // OUTPUT: [ 'constructor', 'roll' ] ``` - Konstruktorfunktionen innehåller enbart instansegenskapen `faceValue`, som är unik för varje instans av `Die`. - Metoden `roll` tillhör prototypen för `Die` och är gemensam för alla objekt instansierade från `Die`. --- # Designmönster för referenstyper - Alla objekt instansierade med samma konstruktor delar samma prototyp (egenskaper och metoder). ``` function Person (name, age) { this.name = name this.age = age } Person.prototype.talk = function () { console.log('Jag, ' + this.name + ', är ' + this.age + ' år.') } const person1 = new Person('Nisse', 42) console.log(person1.constructor) // [Function: Person] const person2 = new Person('Ellen', 7) console.log(person2.constructor) // [Function: Person] person1.talk() // Jag, Nisse, är 42 år. person2.talk() // Jag, Ellen, är 7 år. ``` --- # Klassyntax - bara syntaktiskt socker - _"JavaScript classes introduced in ECMAScript 2015 are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance."_ ``` class Die { constructor () { this.faceValue = undefined } roll () { this.faceValue = Math.floor(Math.random() * 6) + 1 } } const die = new Die() die.roll() console.log(die.faceValue) // OUTPUT: 4 console.log(typeof die) // OUTPUT: object console.log(die.constructor) // OUTPUT: [Function: Die] console.log(Object.getOwnPropertyNames(die)) // OUTPUT: [ 'faceValue' ] console.log(Object.getOwnPropertyNames(Die.prototype)) // OUTPUT: [ 'constructor', 'roll' ] ``` --- # Konstruktor med parametrar i klass ``` class Person { constructor (name, age) { this.name = name this.age = age } talk () { console.log('Jag, ' + this.name + ', är ' + this.age + ' år.') } } const person1 = new Person('Nisse', 42) console.log(person1.constructor) // OUTPUT: [Function: Person] const person2 = new Person('Ellen', 7) console.log(person2.constructor) // OUTPUT: [Function: Person] person1.talk() // OUTPUT: Jag, Nisse, är 42 år. person2.talk() // OUTPUT: Jag, Ellen, är 7 år. console.log(Object.getOwnPropertyNames(person1)) // OUTPUT: [ 'name', 'age' ] console.log(Object.getOwnPropertyNames(Person.prototype)) // OUTPUT: [ 'constructor', 'talk' ] ``` --- # Designmöntret OLOO (Object Linked to Other Objects) ``` const dieBase = { roll: function () { this.faceValue = Math.floor(Math.random() * 6) + 1 } } const createDie = function () { return Object.create(dieBase, { 'faceValue': { value: undefined, writable: true, enumerable: true, configurable: true } }) } const die = createDie() die.roll() console.log(die.faceValue) // OUTPUT: 4 console.log(typeof die) // OUTPUT: object console.log(die.constructor) // OUTPUT: [Function: Object] console.log(Object.getOwnPropertyNames(die)) // OUTPUT: [ 'faceValue' ] ``` - Beteendet att kasta tärningen delegeras från objektet med egenskapen `faceValue` till objektet med funktionen `roll`. --- # Person som beter sig ``` const Person = { talk: function () { console.log('Jag, ' + this.name + ', är ' + this.age + ' år.') } } const createPerson = function (name, age) { return Object.create(Person, { 'name': { value: name, writable: true, enumerable: true, configurable: true }, 'age': { value: age, writable: true, enumerable: true, configurable: true } }) } const person1 = createPerson('Nisse', 42) console.log(person1.constructor) // OUTPUT: [Function: Object] const person2 = createPerson('Ellen', 7) console.log(person2.constructor) // OUTPUT: [Function: Object] person1.talk() // OUTPUT: Jag, Nisse, är 42 år. person2.talk() // OUTPUT: Jag, Ellen, är 7 år. console.log(Object.getOwnPropertyNames(person1)) // OUTPUT: [ 'name', 'age' ] console.log(Object.getOwnPropertyNames(Person)) // OUTPUT: [ 'talk' ] ``` ---