简体   繁体   English

使用闭包还是常规构造函数来创建私有属性?

[英]Closure versus regular constructor function for creating private properties?

I've heard that one of the advantages of closures is the ability to create private properties for objects as follows. 我听说闭包的优点之一是可以为对象创建私有属性,如下所示。

function Func2(){
    //A is a closure, kept alive after the Func2 has returned, 
    //After Func2 returns, A is only accessible by getA and setA (it's "private")
    var A = 100;
    return {
        getA: function(){
            return A;
        },
        setA: function(newA){
            A = newA;
        }
    }
}

You can get and set the private property A of an object created with Func2 using the getter and setter functions... 您可以使用getter和setter函数获取并设置使用Func2创建的对象的私有属性A ...

var obj2 = Func2();
obj2.getA();
obj2.setA(200);

But what's the point of that if I can do the same thing using a regular constructor function? 但是,如果我可以使用常规构造函数执行相同的操作,那有什么意义呢?

function Func1(){ 
    var A = 100; //A is a private property of any object created with the Func1 constructor
    this.getA = function(){
            return A;
            };
    this.setA = function(newA){
        A = newA;
    };
}

Accessing the private properties is done the same way... 访问私有属性的方法相同。

var obj1 = new Func1()
obj1.getA();
obj1.setA(200);

As others have pointed out in the comments, a closure is created in both cases, and both will work. 正如其他人在评论中指出的那样,在两种情况下都将创建一个闭包,并且两者都将起作用。

I think the reason that the return approach is recommended for the module pattern is that the module pattern does not make use of Javascript's prototype chain, so it's unnecessary to use a regular constructor function that would create a new prototype (which isn't needed and would waste memory). 我认为建议对模块模式使用return方法的原因是,该模块模式不使用Javascript的原型链,因此没有必要使用会创建新原型的常规构造函数(这是不必要的,会浪费内存)。 In your example, objects created by Func2 would just have the default prototype of Object.prototype , whereas objects created by Func1 would have a prototype of Func1.prototype which in turn would have the prototype Object.prototype . 在您的示例中,由Func2创建的对象将仅具有默认的Object.prototype原型,而由Func1创建的对象将具有Func1.prototype的原型,而该原型又具有Object.prototype的原型。 Also, Func2 has the advantage that it will work with or without the new keyword (although it's best to avoid the new keyword if you're using the module pattern for the reasons I mentioned above). 另外, Func2的优点是可以使用new关键字,也可以不使用new关键字(尽管由于上述原因,如果您使用模块模式,最好避免使用new关键字)。 (I have seen some programmers complain about the fact that if a programmer forgets the new keyword in traditional OO Javascript, it can cause hard-to-detect bugs; I personally have never found this to be an issue though). (我已经看到一些程序员抱怨这样一个事实,即如果程序员忘记了传统的OO Javascript中的new关键字,它会导致难以检测的错误;不过我个人从来没有发现这是一个问题)。

Another approach to private variables, as you're probably aware, is to simply prefix them with _ , eg: 您可能已经知道,私有变量的另一种方法是简单地在它们前面加上_ ,例如:

function Func1() { 
    this._A = 100;
}

Func1.prototype = {
    constructor: Func1,

    getA: function(){
        return this._A;
    },

    setA: function(newA){
        this._A = newA;
    }
};

Personally I prefer this over the module pattern - the main goal of private variables is communication to other programmers, ie "this is an internal property; don't access it directly" - and the underscore prefix is a well-known convention. 我个人更喜欢这种方式而不是模块模式-私有变量的主要目标是与其他程序员进行通讯,即“这是一个内部属性;不要直接访问它”-下划线前缀是众所周知的约定。 Private properties have never really been about 100% preventing access to those properties, just discouraging it (consider the fact that most programming languages allow you to access private properties using reflection if you really want to). 私有属性从来没有真正地100%阻止访问那些属性,只是阻止了它(考虑一下,大多数编程语言都允许您使用反射来访问私有属性这一事实)。 And also, simply prefixing with an underscore allows you to easily have "protected" properties in addition to private ones (useful for sub-types / sub-classes). 而且,只需在下划线加上前缀即可让您轻松拥有除私有属性(对子类型/子类有用)之外的“受保护”属性。 Making use of the prototype chain is also more memory-efficient, because you don't have multiple copies of the same functions for each instance. 利用原型链还可以提高内存效率,因为每个实例没有相同功能的多个副本。

But I do realize that in spite of the simplicity of the underscore naming convention, some programmers still prefer using the module pattern to really make private properties inaccessible from outside the closure. 但是我确实意识到,尽管下划线命名约定很简单,但是某些程序员仍然喜欢使用模块模式来真正使私有属性无法从闭包外部访问。 If you want to do that, then I'd recommend using the traditional module pattern, using the return statement as in your first example. 如果您想这样做,那么我建议您使用传统的模块模式,如第一个示例所示,使用return语句。

Another pattern you could consider, if you prefer to stick with the module pattern but still want to declare public properties as you go along (rather than in a return statement at the end), would be to create a new object and use that instead of this , eg: 如果您更喜欢使用模块模式,但仍然想声明公共属性(而不是在最后的return语句中),则可以考虑的另一种模式是创建一个新对象,并使用它代替this ,例如:

function Func3(){ 
    var obj = {};
    var A = 100;
    obj.getA = function(){
        return A;
    };
    obj.setA = function(newA){
        A = newA;
    };
    return obj;
}

(By the way, for private methods , as opposed to private data properties, the underscore prefix isn't a necessity even if you're using the prototypal approach. The code sample in this answer is an example of this.) (顺便说一句,对于私有方法 ,相对于私有数据属性,即使您使用原型方法,下划线前缀也不是必需的。 此答案中的代码示例就是一个示例。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM