JavaScript模块模式 - 私有变量与静态变量

时间:2014-02-18 17:55:31

标签: javascript modularity

所以这是着名的JavaScript模块模式的一个例子:

var Person = (function() {

    var _name; // so called 'private variable'

    function Person(name) {
        _name = name;
    }

    Person.prototype.kill = function() {
        console.log(_name + ' has been shot');
    };

    return Person;
})();

var paul = new Person('Paul');
paul.kill();

到目前为止这么好吗?这会将'Paul has been shot'记录到控制台,这就是我们想要的。

但是

_name真的是私有变量吗?我将私有变量定义为属于对象实例的变量,外部世界无法访问该变量。最后一部分有效,我无法从闭包之外访问_name

但如果我这样做:

var paul = new Person('Paul');
var bran = new Person('Bran');
paul.kill();
bran.kill();

然后,这将记录'Bran has been shot'两次。那里没有保罗。因此_name实际上与我的Person对象的所有实例共享。这就是我将其定义为“静态变量”,尽管它也无法从外部访问。

那么有没有办法用模块模式创建真正的私有成员变量?一个不是静止的。

同样发生的事情是在构造函数中定义this._name,但这会杀死私有部分,现在可以从外部访问:

function Person(name) {
    this._name = name;
}

var bran = new Person();
console.log(bran._name); // yep, accessible

问题:

因此。私人不是私人的,只是静态的。我们如何使用模块模式创建一个真正的私有成员变量?属于实例的变量,它不是静态的,而是一个无法从外部访问的变量。

5 个答案:

答案 0 :(得分:6)

你是对的; _name更像是一个静态变量。它保存在包含构造函数的闭包中,因此构造函数的每次使用都将使用相同的变量。请记住,这与类无关,而与闭包和函数有关。它可以非常方便,但不是你如何做私人会员。

不出所料,道格拉斯·克罗克福德有page devoted to private members in Javascript

为了成为私人会员,你必须“更深层次”。不要在构造函数之外使用闭包;使用构造函数作为闭包。构造函数内定义的任何变量或方法显然都不能被外界使用。实际上,对象也无法访问它们,因此它们非常“私密”。

我们想要使用我们的私人会员。 :)那该怎么办?

好吧,在构造函数中,执行以下操作:

var Klass = function () {
    var private = 3;
    this.privileged = function () { return private; };
};

然后:

var k = Klass();
console.log(k.privileged()); // 3

看看如何使用构造函数作为闭包? this.privileged依赖于对象,因此private生活在this.privileged的封闭内。

不幸的是,Javascript中的私有和特权方法存在一个问题。它们必须每次都从头开始实例化。没有代码共享。这显然是私人会员想要的,但对于方法来说并不理想。使用它们会减慢对象实例化并使用更多内存。如果您遇到效率问题,请记住这一点。

答案 1 :(得分:2)

“真正的私有成员变量”和基于原型的方法并不能很好地协同工作。实现所需的唯一方法是在构造函数中创建所有方法。

var Person = (function() {

    function Person(name) {
        this.kill = function() {
            console.log(name + ' has been shot');
        };
    }

    return Person;
})();

var paul = new Person('Paul');
var bran = new Person('Bran');
paul.kill(); // Paul has been shot
bran.kill(); // Bran has been shot

但由于每个实例都有kill函数的唯一版本,因此会占用更多内存并且速度较慢。

传统上,下划线前缀用于半私有实例属性,只要暴露的数据不是安全风险。您的javascript代码的大多数消费者都知道不要使用下划线前缀属性。

Some more reading you may find useful is here.

答案 2 :(得分:2)

问题是你的_name变量在Person范围之外并在所有Person实例之间共享。请执行以下操作:

(function() {

    var Person = function(name) {
         var _name = name; // so called 'private variable'

        this.getName = function()
        {
            return _name;
        }

        this.kill = function()
        {
            console.log(this.getName() + ' has been shot');
        }
    }

    /*Alternative
    Person.prototype.kill = function() {
        console.log(this.getName() + ' has been shot');
    };*/

    window.Person = Person;
})();

var paul = new Person('Paul');
var bran = new Person('Bran');
paul.kill();
bran.kill();

答案 3 :(得分:0)

在实例范围中需要为私有的变量必须在此范围内,例如:

function Person(name) {
    var _name = name;

    this.kill = function() {
      console.log( _name + ' has been shot' ); 
    }
}

缺点是必须在也可以访问私有变量的范围中定义kill

答案 4 :(得分:0)

为了创建私有var,你应该把它放在构造函数中,所以你的代码应该是这样的:

var Person = (function() {

    function Person(name) {
        // since name is a param, you don't even have to create a _name var
        // and assign name to it
        this.getName = function () {
            return name;
        };
    }

    Person.prototype.kill = function() {
        console.log(this.getName() + ' has been shot');
    };

    return Person;
})();

var paul = new Person('Paul');
paul.kill();

您还可以在构造函数中声明.kill

var Person = (function() {

    function Person(name) {
        this.kill = function() {
            console.log(name + ' has been shot');
        };
    }

    return Person;
})();

var paul = new Person('Paul');
paul.kill();