Vyhledávání na webu

javascript - Je používání closure k napodobení zapouzdření špatný nápad?


Například se podívejte na mou jednoduchou implementaci zásobníku:

var MyStack = (function() {
    var min;
    var head;

    // Constructor
    function MyStack() {
        this.size = 0;
    }

    MyStack.prototype.push = function(val) {
        var node = new Node(val);

        if (typeof min === 'undefined' || val < min) {
            min = val;
        }

        ++this.size;
        if (typeof head === 'undefined') {
            head = node;
        } else {
            node.next = head;
            head = node;
        }
    };

    MyStack.prototype.pop = function() {
        if (typeof head === 'undefined') {
            throw new Error('Empty stack');
        }

        --this.size;

        var data = head.data;
        head = head.next;

        return data;
    };

    MyStack.prototype.min = function() {
        if (typeof min === 'undefined') {
            throw new Error('Min not defined');
        }

        return min;
    };

    MyStack.prototype.peek = function() {
        if (typeof head === 'undefined') {
            throw new Error('Empty stack');
        }

        return head.data;
    };

    function Node(data) {
        this.data = data;
        this.next;
    }

    return MyStack;
})();

Použitím této metody mohu zajistit, že nikdo nebude schopen (nedopatřením nebo úmyslně) manipulovat s „privátními“ poli, jako jsou min a head. Mohu také využít privátní funkce, jako je Node(), která nemusí být zpřístupněna.

Četl jsem, že to bude používat více paměti jen kvůli tomu, že musí udržovat další oblast pro každý nový objekt, který je vytvořen pro MyStack. Potřebuje to opravdu tolik další paměti, že je tento způsob špatný nápad?

Snažil jsem se to optimalizovat tím, že jsem využil prototypy místo vytváření funkcí pokaždé, když je vytvořen nový objekt. Jinými slovy, nezahrnul jsem tyto funkce jako součást konstruktoru MyStack.

Moje otázka je, je to špatný design? Existují nějaké hlavní nevýhody této metodologie?

Odpověď č. 1

Je používání uzávěrů k napodobenině zapouzdření špatný nápad?

Ne. Ačkoli bych toto nepovažoval za „emulaci“, uzávěry realizují zapouzdření.

Snažil jsem se to optimalizovat tím, že jsem využil prototypy místo vytváření funkcí pokaždé, když je vytvořen nový objekt. Jinými slovy, nezahrnul jsem tyto funkce jako součást konstruktoru MyStack.

Moje otázka je, je to špatný design? Existují nějaké hlavní nevýhody této metodologie?

Ano, to je vlastně špatně. Vaše proměnné min a head (a MyStack a Node) jsou v podstatě statické. Jsou definovány pouze jednou a budou sdíleny všemi instancemi. Nemůžete vytvořit dva odlišné zásobníky, oba budou mít stejný odkaz head.

Aby bylo možné zapouzdřit stav jednotlivých instancí, budete muset deklarovat proměnné v konstruktoru, aby byly vytvořeny s každým novým objektem. Pro to budete také muset deklarovat všechny metody, které potřebují k nim přistupovat („privilegované“), v rozsahu konstruktoru.

var MyStack = (function() {
    function MyStack() {
        var size = 0;
        var head = undefined;

        function checkNonEmpty() {
            if (typeof head === 'undefined') {
                throw new Error('Empty stack');
            }
        }
        this.push = function(val) {
            size++;
            head = new Node(val, head);
        };
        this.pop = function() {
            checkNonEmpty();
            this.size--;
            var data = head.data;
            head = head.next;
            return data;
        };
        this.peek = function() {
            checkNonEmpty();
            return head.data;
        };
        this.getSize = function() {
            return size;
        };
    }

    function Node(data, next) {
        this.data = data;
        this.next = next;
    }

    return MyStack;
})();

Pokud chcete tyto metody přidat na prototyp, budete muset zpřístupnit hodnoty head a size jako vlastnosti instance.

Odpověď č. 2

Vzhledem k tomu, že zapouzdření je jedním z principů objektově orientovaného programování, nemyslím si, že se jedná o špatnou programátorskou praxi. Uzávěry (closures) v JavaScriptu jsou vaším prostředkem zapouzdření proměnných a omezení přístupu k jiným částem aplikace.

V mnoha ohledech není v JavaScriptu nic opravdu bezpečné. I s uzávěrem jsou proměnné, které chcete udržet soukromé, přístupné různými způsoby v různých prohlížečích. Například v Google Chrome můžete v ladicím nástroji nastavit zarážku a získat přístup k jakékoli proměnné v aktivním uzávěru. Ačkoli můžeme v prohlížeči udělat hodně pro zajištění bezpečnosti, stále pracujeme s interpretovaným jazykem, který je překládán na stroji, který spouští kód.

A pokud vezmeme v úvahu váš příklad: var MyStack=(function(){...})(); Pokud někdy budete pracovat s TypeScriptem nebo jakýmikoli rapidními vývojovými frameworky, uvidíte, že toto je metoda kódování výstupu používaná k vytváření jmenných prostorů objektů, když frameworky kompilují/transpilují.