javascript - 对私有(private)成员使用闭包或下划线约定

标签 javascript design-patterns

Douglas Crockford 和许多其他人 suggest using closures for private members如下:

function Container(param) {

    function dec() {
        if (secret > 0) {
            secret -= 1;
            return true;
        } else {
            return false;
        }
    }

    this.member = param;
    var secret = 3;
    var that = this;
}

这样做的好处是这些成员在构造函数之外是不可访问的,但缺点是不能在原型(prototype)中使用私有(private)成员。所以你最终将所有使用私有(private)成员的东西都放在了构造函数中,这不利于内存。

其他一些人建议在命名私有(private)成员时使用下划线:

function Container(param) {
    this.member = param;
    this._secret = 3;
}

Container.prototype.dec = function {
    if (this.secret > 0) {
        this.secret -= 1;
        return true;
    } else {
        return false;
    }
}

这样做的缺点是这些成员很容易公开访问,唯一阻止人们的是约定。

我的问题是:

  1. 您什么时候决定使用一个而不是另一个?
  2. 一种方式比另一种方式更受欢迎吗?
  3. 使用其中一种方法的著名图书馆有哪些?
  4. 有没有比这两个更好的方法?

最佳答案

正如您已经概述的那样,这两种方法之间存在权衡。因此,使用哪个完全取决于您的用例的具体情况,并且没有不考虑实际用例的答案。您可以通过几种方式限制决策:

如果您正在制作成千上万个这样的对象,那么不使用原型(prototype)而增加的内存使用量可能是有意义的,您可能想要牺牲真正的隐私来管理您的内存使用量和使用没有真正隐私的原型(prototype)版本。如果您没有制作大量对象或在非常紧凑的内存占用空间中运行,则内存使用差异不太重要。

Crockford 指出,如今内存问题很少成为真正的问题。但是,我认为这只是一个笼统的说法,您必须评估您的具体情况才能知道这是否属实。

如果隐私是最重要的,并且您绝对不希望外部人员破坏内部私有(private)变量(甚至可能是 API 的安全问题或可靠性问题),那么一定要选择闭包为您提供真正隐私的选项。

When do you decide to use one over the other?

这取决于您最想针对什么进行优化,最佳选择是针对特定用例的。

Is one way preferred more commonly than the other?

这取决于您最想针对什么进行优化,最佳选择是针对特定用例的。

What are some famous libraries that use one of these methods?

jQuery 混合使用了这些。它有一些以下划线开头的“非公开”属性。并且,它使用了一些真正私有(private)的闭包变量,尽管通常仅用于只有一个或少量或有时仅用于模块级全局变量的对象。随着时间的推移,在我看来,jQuery 已将它的一些 _priv 属性切换为更 protected 闭包变量,但仍然有一些。

这可能是因为 jQuery 正在尝试优化 jQuery 对象本身的内存使用,因此不想创建额外的闭包或为方法使用额外的内存。但是,对于它在不那么多的情况下使用的其他类型的对象,它可以更轻松地承担额外的内存使用量以实现真正的隐私。

Is there a better method than these two?

如果您有 weakMaps 可用,还有另一种选择。闭包是我见过的最好的方案,可以在甚至更旧的浏览器上工作,实现真正的隐私。表示非公开的唯一其他方式是约定俗成,前导下划线是更常见的约定。


根据 Felix 的建议,weakMap 选项的工作原理如下。

var MyClass = (function () {
    var data = new WeakMap();
    return class MyClass {
        constructor() {
            var secretData = {
                foo: 42
            };
            data.set(this, secretData);
        }
        // normal prototyped method can get access to the private data
        doSomethingWithSecret() {
            return data.get(this).foo * 2;
        }
    };
}());

每个类定义都有一个 WeakMap 对象。在对象的构造函数中,您使用 this 作为将对象存储在 weakMap 中的键。由于您需要 this 和访问 weakMap 才能访问私有(private)数据,因此只有在私有(private)闭包中定义的方法才能访问 weakMap,但不像Crockford 方法,您可以在此闭包中使用 .prototype 方法。这会为每个类创建一个闭包,并为每个类创建一个 WeakMap 对象,因此这两者都不是针对每个实例的。

由于这里的一切似乎都是某种权衡,与其他两种方法中的任何一种相比,每次您想要将数据获取或设置到 WeakMap 时,性能都会受到一点影响。性能损失的多少完全取决于 weakMap 的实现,如果性能是最重要的,则必须对其进行衡量,但它可能更多的是一个查找过程,而不是简单的属性查找,而且可能没有那么优化,因为它是一个相对较新的也是特色。

关于javascript - 对私有(private)成员使用闭包或下划线约定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38032092/

相关文章:

Java Restful Web 服务 (jax rs) 身份验证模式

mysql - Data Mapper 是不是比 Active Record 更现代的趋势

c# - 使用显式接口(interface)来确保针对接口(interface)进行编程

python - Python 中的通用命令模式和命令调度模式

sql - 实现字段更改跟踪的模式

javascript - Google Map v3 map 加载事件

javascript - jQuery - 展开/折叠所有按钮,Internet Explorer 问题

javascript - var w = q||q2;这个表达是什么意思?

javascript - 2 Div 关闭并删除中间的 1 div

javascript - 为什么在 javascript 中枚举继承的(如果它是继承的则有混淆)属性?