Moduly v JavaScripte

Časť 1: Module Pattern

Milan Herda, 10/2016, úpravy 08/2022

Úloha

Úloha

Vytvorte v JS objekt, ktorý bude mať privátnu premennú name, ku ktorej sa bude dať pristúpiť iba pomocou getter a setter metód

// váš kód

const person = /* váš kód */;

person.setName('fero');

console.log(
    person.getName()
);

Scope premenných

Scope je rozsah platnosti, tj. kde všade je možné k existujúcej premennej pristúpiť.

Je určený miestom a spôsobom deklarovania premennej.

Scope premenných

Scope je rozsah platnosti, tj. kde všade je možné k existujúcej premennej pristúpiť.

Je určený miestom a spôsobom deklarovania premennej.

Scope premenných

Scope je rozsah platnosti, tj. kde všade je možné k existujúcej premennej pristúpiť.

Je určený miestom a spôsobom deklarovania premennej.

Aký je scope v prípade, že tento kód je v top-level skripte?

a = 1;
var b = 2;

function foo () {
    c = 3;
    var d = 4;

    for (i = 5; i < 10; i++) {
        e = 6;
        var f = 7;
    }

    for (var j = 8; j < 10; j++) {
    }
}

Aký je scope v prípade, že tento kód je v top-level skripte?

a = 1;      // globálna
var b = 2;

function foo () {
    c = 3;
    var d = 4;

    for (i = 5; i < 10; i++) {
        e = 6;
        var f = 7;
    }

    for (var j = 8; j < 10; j++) {
    }
}

Aký je scope v prípade, že tento kód je v top-level skripte?

a = 1;      // globálna
var b = 2;  // globálna

function foo () {
    c = 3;
    var d = 4;

    for (i = 5; i < 10; i++) {
        e = 6;
        var f = 7;
    }

    for (var j = 8; j < 10; j++) {
    }
}

Aký je scope v prípade, že tento kód je v top-level skripte?

a = 1;      // globálna
var b = 2;  // globálna

function foo () {
    c = 3;      // globálna
    var d = 4;

    for (i = 5; i < 10; i++) {
        e = 6;
        var f = 7;
    }

    for (var j = 8; j < 10; j++) {
    }
}

Aký je scope v prípade, že tento kód je v top-level skripte?

a = 1;      // globálna
var b = 2;  // globálna

function foo () {
    c = 3;      // globálna
    var d = 4;  // lokálna pre foo

    for (i = 5; i < 10; i++) {
        e = 6;
        var f = 7;
    }

    for (var j = 8; j < 10; j++) {
    }
}

Aký je scope v prípade, že tento kód je v top-level skripte?

a = 1;      // globálna
var b = 2;  // globálna

function foo () {
    c = 3;      // globálna
    var d = 4;  // lokálna pre foo

    for (i = 5; i < 10; i++) {  // globálna
        e = 6;
        var f = 7;
    }

    for (var j = 8; j < 10; j++) {
    }
}

Aký je scope v prípade, že tento kód je v top-level skripte?

a = 1;      // globálna
var b = 2;  // globálna

function foo () {
    c = 3;      // globálna
    var d = 4;  // lokálna pre foo

    for (i = 5; i < 10; i++) {  // globálna
        e = 6;                  // globálna
        var f = 7;
    }

    for (var j = 8; j < 10; j++) {
    }
}

Aký je scope v prípade, že tento kód je v top-level skripte?

a = 1;      // globálna
var b = 2;  // globálna

function foo () {
    c = 3;      // globálna
    var d = 4;  // lokálna pre foo

    for (i = 5; i < 10; i++) {  // globálna
        e = 6;                  // globálna
        var f = 7;              // lokálna pre foo
    }

    for (var j = 8; j < 10; j++) {
    }
}

Aký je scope v prípade, že tento kód je v top-level skripte?

a = 1;      // globálna
var b = 2;  // globálna

function foo () {
    c = 3;      // globálna
    var d = 4;  // lokálna pre foo

    for (i = 5; i < 10; i++) {  // globálna
        e = 6;                  // globálna
        var f = 7;              // lokálna pre foo
    }

    for (var j = 8; j < 10; j++) {  // lokálna pre foo
    }
}

Ako je to, keď nahradíme var za let?

Ako je to, keď nahradíme var za let?

a = 1;
let b = 2;

function foo () {
    c = 3;
    let d = 4;

    for (i = 5; i < 10; i++) {
        e = 6;
        let f = 7;
    }

    for (let j = 8; j < 10; j++) {
    }
}

Ako je to, keď nahradíme var za let?

a = 1;      // globálna
let b = 2;  // globálna

function foo () {
    c = 3;      // globálna
    let d = 4;  // lokálna pre foo

    for (i = 5; i < 10; i++) {  // globálna
        e = 6;                  // globálna
        let f = 7;              // lokálna pre cyklus
    }

    for (let j = 8; j < 10; j++) {  // lokálna pre cyklus
    }
}

A keď použijeme const?

a = 1;
const b = 2;

function foo () {
    c = 3;
    const d = 4;

    for (i = 5; i < 10; i++) {
        e = 6;
        const f = 7;
    }

    for (const j = 8; j < 10; j++) {
    }
}

A keď použijeme const?

a = 1;      // globálna
let b = 2;  // globálna

function foo () {
    c = 3;      // globálna
    const d = 4;  // lokálna pre foo

    for (i = 5; i < 10; i++) {  // globálna
        e = 6;                  // globálna
        const f = 7;              // lokálna pre cyklus
    }

    for (const j = 8; j < 10; j++) {  // TypeError:
                              // Assignment to constant variable.
    }
}

A keď použijeme const?

Spôsoby deklarácie premennej v ES2015

  • var - bezo zmeny, deklaruje lokálne pre funkciu
  • let - platnosť iba vo svojom bloku
  • const - konštanta(?), platnosť iba vo svojom bloku

Closure

Closure

Closure je "obálka", ktorú vytvára funkcia a ktorá v sebe obsahuje všetky premenné, ku ktorým má funkcia prístup.

*Môže obsahovať premenné deklarované vo funkciách, ktorých beh už dávno skončil

Closure

const a = 1;

function foo () {
    const b = 2;

    return function () {
        const c = 3;

        return a + b + c;
    }
}

const bar = foo();

bar();

IIFE

IIFE

Immediately Invoked Function Expression

IIFE

(function () {
    // kód
})();

K čomu to je?

Nájdite rozdiel

var a = 1;
var b = 2;

var c = a + b;








(function () {
  var a = 1;
  var b = 2;
  
  var c = a + b;
})();






K čomu to je?

Nájdite rozdiel

var a = 1;
var b = 2;

var c = a + b;

// Premenné sú dostupné
// v globálnom kontexte





(function () {
  var a = 1;
  var b = 2;
  
  var c = a + b;
})();

// Premenné sú lokálne
// pre anonymnú funkciu

// Nehrozí konflikt
// medzi knižnicami

Module Pattern

Module Pattern

Spôsob, ako pomocou closure vieme vytvárať moduly/objekty s privátnymi premennými/funkciami

Module Pattern

function () {
    let name;

    return {
        setName: function (newName) {
            name = newName;
        },
        getName: function () {
            return name;
        },
    };
}

Revealing Module Pattern

function () {
    let name;
        
    const setName = function (newName) {
        name = newName;
    };

    const getName = function () {
        return name;
    };
  
    // von poskytneme iba metódy, ktoré chceme
    return {
        setName,
        getName,
    };
}

Vytvorenie inštancie objektu

const person = (function () {
    let name;
        
    const setName = function (newName) {
        name = newName;
    };

    const getName = function () {
        return name;
    };
  
    // von poskytneme iba metódy, ktoré chceme
    return {
        setName,
        getName,
    };
})();

Vytvorenie inštancie objektu cez factory

const personFactory = function () {
    let name;
        
    const setName = function (newName) {
        name = newName;
    };

    const getName = function () {
        return name;
    };
  
    // von poskytneme iba metódy, ktoré chceme
    return {
        setName,
        getName,
    };
};

const personA = personFactory();
const personB = personFactory();

Kontrolná otázka

const personFactory = function () {
    let name;
        
    const setName = function (newName) {
        name = newName;
    };

    const getName = function () {
        return name;
    };
  
    // von poskytneme iba metódy, ktoré chceme
    return {
        setName,
        getName,
    };
};

const personA = personFactory();
const personB = personFactory();

Ako nastavíme úvodnú hodnotu pri vytvorení inštancie?

Kontrolná otázka

const personFactory = function (initialName) {
    let name = initialName;
        
    const setName = function (newName) {
        name = newName;
    };

    const getName = function () {
        return name;
    };
  
    // von poskytneme iba metódy, ktoré chceme
    return {
        setName,
        getName,
    };
};

const personA = personFactory('fero');
const personB = personFactory('jozo');

Pohrajte sa

Úloha

Vytvorte modul reprezentujúci bojovú jednotku v hre s vlastnosťami:

  • názov
  • rýchlosť
  • sila
  • zdravie
const unitFactory = function () {
    let name;
    let speed;
    // ...

    const setName = function (newName) {
        name = newName;
    };

    const getName = function () {
        return name;
    };

    const setSpeed = function (newSpeed) {
        speed = newSpeed;
    };

    const getSpeed = function () {
        return speed;
    };

    // ...
  
    return {
        setName,
        getName,
        setSpeed,
        getSpeed,
        //...,
    };
};

Úloha

Vytvorte tri inštancie reprezentujúce:

  • kopijníka,
  • jazdca
  • a lukostrelca
const pikeman = unitFactory();
const horseman = unitFactory();
const archer = unitFactory();

pikeman.setName('pikeman');
pikeman.setSpeed(1);
pikeman.setStrength(5);
pikeman.setHealth(10);

// ...

Pochvala pre každého, kto príde na to, ako urobiť set metódy chainovateľné a nerozbije pri tom kód

Úloha

Pridajte lukostrelcovi (a iba lukostrelcovi) vlastnosť dostrel (range)

archer.range = 5;

archer.setRange = function (newRange) {
    this.range = newRange;
};

archer.getRange = function () {
    return this.range;
};
var extendByRange = function (unit) {
    let range;

    const setRange = function (newRange) {
        range = newRange;
    };

    const getRange = function () {
        return range;
    };

    unit.setRange = setRange;
    unit.getRange = getRange;

    return unit;
};
const archer = unitFactory();

extendByRange(archer);

archer.setRange(4);

Využitie Module Patternu

  • skrývanie implementáčných detailov a privátnych vlastností
  • ochrana objektov pred narušením zvonka
  • umožňuje "komponentové" programovanie
  • ochrana pred zaprasením globálneho menného priestoru

Moduly nie sú iba Module Pattern

CommonJS a ES2015 dovoľujú mať pre skripty samostatné menné priestory a exportovať von iba želané premenné.

CommonJS

// index.js
var square = require('./square.js');

console.log('area is ' + square.area(5));
// square.js
var area = function (length) {
    return length * length;
};

module.exports = {
    area: area
};

ES2015

// index.js
import square from './square';

console.log('area is ' + square.area(5));
// square.js
const area = (length) => {
    return length * length;
};

export default {
    area
};

Opakovanie

  • Privátnosť pre premenné sa v JS dosahuje inak, ako v OOP jazykoch
  • Scope je definovaný miestom a spôsobom deklarácie (var, let, const)
  • Closure je obálka nad premennými, ku ktorým má funkcia prístup
  • IIFE je okamžité zavolanie anonymnej funkcie, robíme to preto, aby sme neprasili globálny menný priestor
  • Module Pattern nám umožňuje vytvárať privátne premenné a funkcie
  • V JS vieme rozširovať objekty aj po ich vytvorení

Ďakujem za pozornosť