使用闭包来模拟封装是一个坏主意吗?

时间:2016-10-20 22:08:38

标签: javascript closures

例如,看看我的简单堆栈实现:

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;
})();

通过使用这种方法,我可以确保没有人能够(意外或有意)操纵“私人”字段,如min和head。我还可以使用不需要公开的Node()等私有函数。

我已经读过这将使用更多内存,因为它必须为为MyStack创建的每个新对象维护一个额外的范围。它需要这么多额外的内存,这种方式是个坏主意吗?

我确实尝试通过使用原型来优化它,而不是每次创建新对象时都创建函数。换句话说,我没有将函数作为MyStack构造函数的一部分包含在内。

我的问题是,这是一个糟糕的设计吗?这种方法有任何重大缺陷吗?

2 个答案:

答案 0 :(得分:3)

  

使用闭包来模拟封装是一个坏主意吗?

没有。虽然我不认为这是&#34;仿真&#34;,但是闭包正在实施封装。

  

我确实尝试通过使用原型来优化它,而不是每次创建新对象时都创建函数。换句话说,我没有将这些函数作为MyStack构造函数的一部分包含在内。

     

我的问题是,这是一个糟糕的设计吗?这种方法有任何重大缺陷吗?

是的,这确实是错的。您的minhead(以及MyStackNode)变量基本上是静态。它们只定义一次,并将由所有实例共享。您不能创建两个不同的堆栈,它们都具有相同的head引用。

要封装每个实例的状态,您需要在构造函数中声明变量,以便使用每个新对象创建它们。为此,您还必须在构造函数范围内声明需要访问它们的所有方法(&#34;特权&#34;)。

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;
})();

如果您想将这些方法放在原型上,则需要将headsize值作为实例的属性。

答案 1 :(得分:1)

考虑到封装是面向对象编程的原则之一,我不认为这是一个糟糕的编码实践。 JavaScript中的闭包是封装变量和限制对应用程序其他部分的访问的手段。

在很多方面,JavaScript中没有什么是真正安全的。即使使用闭包,您想要保密的变量也可以通过不同浏览器中的不同方式访问。例如,在Google Chrome中,您可以在调试器中设置断点并访问活动磁盘阵列中的任何变量。虽然我们可以在浏览器中做很多事情以确保安全性,但我们仍在处理在运行代码的机器上编译的解释语言。

考虑你的例子: var MyStack = (function(){...})(); 如果您使用的是typescript或任何快速开发框架,您将看到这是在框架编译/转换时用于命名空间对象的输出编码方法。