Javascript 中的私有成员。它们可以是非静态的吗?

Posted

技术标签:

【中文标题】Javascript 中的私有成员。它们可以是非静态的吗?【英文标题】:Private members in Javascript. Can they be non-static? 【发布时间】:2012-04-21 04:46:21 【问题描述】:

我这里有这段代码:

var Person = (function() 
    var name;

    var PersonConstructor = function(n) 
        name = n;
    ;

    PersonConstructor.prototype.getName = function() 
        return name;
    ;

    return PersonConstructor;

)();

var people = [];
var person1 = new Person("Foo");
var person2 = new Person("Bar");
alert(person1.getName()); // I want it to be Foo
people.push(person1);
people.push(person2);

我从here. 得到了模拟类的想法。但是当然,我忽略了私有变量var name;也是一个静态变量的事实。由于这阻碍了我目前的努力,我想知道是否有办法在这个例子中保持私人行为但避免静态行为?

【问题讨论】:

你不能让原型函数在构造函数中引用局部变量。我会远离在 javascript 中模拟可见性的方法。这不是语言的一部分,并且产生的问题多于它的帮助。使用适当的文档,将属性标记为 @private,并根据代码的用例,使用 Google 的 Closure 编译器之类的工具仅通过其实际名称公开公共属性。 【参考方案1】:

使用this

var Person = (function() 
  var PersonConstructor = function(n) 
    this.name = n;
  ;

  PersonConstructor.prototype.getName = function() 
    return this.name;
  ;

  return PersonConstructor;    
)();

很遗憾,这不会保留私有状态。

【讨论】:

这不保留名称的“私有”状态? 这确实有效,但如上所述它不保留所谓的私有状态,也许我应该坚持命名约定。【参考方案2】:

这只是范围问题。

var Person = (function()

    var PersonConstructor = function(n) 
        // *************************************************************** 
        // PRIVATE VARIABLES AND FUNCTIONS 
        // ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE 
        // *************************************************************** 

        var myName=n?n:"John Doe";

        // *************************************************************** 
        // PRIVILEGED METHODS 
        // MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS 
        // MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS 
        // *************************************************************** 
        this.toString=this.getName=function() return myName  
     
    return PersonConstructor;
)();

var person1 = new Person("foo");
var person2 = new Person("bar");

alert(person1.getName());
alert(person1.toString());
alert(person1.myName);

// alerts "foo", "foo", undefined

编辑 - 这是我原来的解决方案。

var Person = function(n) 
    // *************************************************************** 
    // PRIVATE VARIABLES AND FUNCTIONS 
    // ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE 
    // *************************************************************** 

    var myName=n?n:"John Doe";

    // *************************************************************** 
    // PRIVILEGED METHODS 
    // MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS 
    // MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS 
    // *************************************************************** 
    this.toString=this.getName=function() return myName  

 

var person1 = new Person("foo");
var person2 = new Person("bar");

alert(person1.getName());
alert(person1.toString());
alert(person1.myName);

// alerts "foo", "foo", undefined

【讨论】:

特权方法和公共方法根本不能很好地协同工作。 @Raynos - 对我来说似乎工作正常。就个人而言,我会取消prototype.getName,但它是OP设计的一部分,所以我想我会把它留在那里。 您的特定示例将更适合公共只读属性。在现实世界的场景中,这是行不通的 @Raynos - 就像我说的那样,它被留下作为替代方案,以表明prototype.getName 确实有效并且私有变量myName 只是一个范围问题。 @Raynos - 实际上,与使用状态对象的公认答案相比,它非常简单(哦,那是你的)。我想发布一个非常非常简单的版本,但它没有使用“PersonConstructor”和OP想要的原型东西。我已经做 OO JS 大约 15 年了,并且知道我在做什么。简单更好。【参考方案3】:

使用原型时没有“私有”。

需要注意的是,私有状态没有任何价值,像瘟疫一样避免它。闭包既丑陋又昂贵。

var o = 
    name: value

但是,如果你坚持妄想并且非常想要私人状态,那么

您可以将状态存储在闭包中

function nameHolder(name) 
    return 
        get name() 
            return name
        ,
        set name(n) 
            name = n
        
    

请注意,这是非常低效的,几乎没有什么好处。

或者,您可以将状态存储在弱映射中

function privates() 
    var map = new WeakMap()

    return function (key) 
        var v = map.get(key)
        if (!v) 
            v = 
            map.set(key, v)
        
        return v
    


var NameHolder = (function () 
    var state = privates()

    return 
        constructor: function (name) 
            state(this).name = name
        ,
        getName: function () 
            return state(this).name
        
    
())

WeakMap 浏览器支持不存在,因此使用pd.Name 模拟它

【讨论】:

你能详细说明一下吗?所以对于每个变量我可能想保持“私有”(抱歉不知道如何引用它)我必须创建一个持有者函数?编辑:好的,我看到了你的阐述,我会试试这个,虽然我必须先试着理解它。 我很惊讶@Raynos 对私有状态的使用没有更积极的态度。这是一个足够公平的问题,但问问你自己,你真的需要私有状态吗?您可以遵循使用前导下划线标记所有内部不可混淆变量的约定。可能更容易调试,效率更高。 您声称关闭既丑陋又昂贵。确实,跨越范围边界进行引用查找是有成本的,但在编写良好的代码中,可以最大限度地降低成本,而在现代 JIT 解释器中,它通常在循环之外是无关紧要的。闭包可能是 JavaScript 中最优雅和最具表现力的特性。闭包使得一个 2000 行的 Java 程序在 JavaScript 中只有 500 行。 闭包与此无关。一等函数很强大,过度使用闭包会导致糟糕的代码(在 OO 风格中。在函数式代码中有效使用闭包效果很好)。您还应该自己对其进行基准测试,它们仍然具有显着的性能和复杂性损失 当问题只是错误的范围时太复杂了。当原始代码的范围正确时,一切都按预期工作。

以上是关于Javascript 中的私有成员。它们可以是非静态的吗?的主要内容,如果未能解决你的问题,请参考以下文章

深入理解JavaScript模拟私有成员

Swift 中的友元类(访问内部类的私有成员)

ServiceStack 的 JsonSerializer 可以序列化私有成员吗?

子类真的继承私有成员变量吗?

私有静态变量

单例模式