原型上的Javascript私有成员

时间:2009-01-27 12:26:18

标签: javascript prototype private

我试图弄清楚这是否可行。这是代码:

a=function(text)
{
   var b=text;
   if (!arguments.callee.prototype.get)
      arguments.callee.prototype.get=function()
    {
         return b;
    }
    else
      alert('already created!');
}

var c=new a("test");  // creates prototype instance of getter
var d=new a("ojoj");  // alerts already created
alert(c.get())        // alerts test 
alert(d.get())        // alerts test from context of creating prototype function :(

如你所见,我试图创建原型getter。为了什么?好吧,如果你写这样的东西:

a=function(text)
{
    var b=text;
    this.getText=function(){ return b}
}

......一切都应该没问题......但实际上每次创建对象时 - 我都会创建使用内存的getText函数。我想在记忆中有一个原型函数可以做同样的事情...任何想法?

编辑:

我尝试过Christoph提供的解决方案,而且它似乎是目前唯一已知的解决方案。它需要记住id信息以从上下文中检索值,但是整个想法对我来说很好:) Id只是要记住的一件事,其他一切都可以在内存中存储一​​次。实际上,您可以通过这种方式存储许多私有成员,并且只能使用一个id。实际上这让我很满意:)(除非有人有更好的主意)。

someFunc = function()
{
  var store = new Array();
  var guid=0;
  var someFunc = function(text)
  {
    this.__guid=guid;
    store[guid++]=text;
  }

  someFunc.prototype.getValue=function()
  {
    return store[this.__guid];
  }

  return someFunc;
}()

a=new someFunc("test");
b=new someFunc("test2");

alert(a.getValue());
alert(b.getValue());

8 个答案:

答案 0 :(得分:28)

JavaScript传统上没有提供属性隐藏机制('私有成员')。

由于JavaScript是词法范围的,您可以始终在每个对象级别上使用构造函数作为对“私有成员”的闭包并在构造函数中定义方法来模拟这个,但这不适用于方法在构造函数的prototype属性中定义。

当然,有办法解决这个问题,但我不推荐它:

Foo = (function() {
    var store = {}, guid = 0;

    function Foo() {
        this.__guid = ++guid;
        store[guid] = { bar : 'baz' };
    }

    Foo.prototype.getBar = function() {
        var privates = store[this.__guid];
        return privates.bar;
    };

    Foo.prototype.destroy = function() {
        delete store[this.__guid];
    };

    return Foo;
})();

这会将'private'属性存储在与Foo实例分开的另一个对象中。确保在完成对象后调用destroy():否则,您刚刚创建了内存泄漏。


编辑2015-12-01: ECMAScript6制作了不需要手动对象销毁的改进变体,例如使用WeakMap或最好是Symbol,避免需要一个外部商店:

var Foo = (function() {
    var bar = Symbol('bar');

    function Foo() {
        this[bar] = 'baz';
    }

    Foo.prototype.getBar = function() {
        return this[bar];
    };

    return Foo;
})();

答案 1 :(得分:2)

原型上的方法无法访问javascript中存在的“私有”成员;你需要某种特权访问者。由于您声明get可以在词汇上看到b,因此它始终会返回b创建时的内容。

答案 2 :(得分:2)

在克里斯托夫的解决方案受到巨大启发后,我想出了一个略微修改过的概念,提供了一些改进。同样,这个解决方案很有趣,但不一定推荐。这些增强功能包括:

  • 不再需要在构造函数中执行任何设置
  • 删除了在实例上存储公共GUID的需要
  • 添加了一些语法糖

这里的技巧基本上是使用实例对象本身作为访问相关私有对象的密钥。通常这对于普通对象是不可能的,因为它们的键必须是字符串。但是,我能够使用表达式({} === {})返回false这一事实来实现此目的。换句话说,比较运算符可以在唯一对象实例之间进行辨别。

长话短说,我们可以使用两个数组来维护实例及其关联的私有对象:

Foo = (function() {
    var instances = [], privates = [];

    // private object accessor function
    function _(instance) {
        var index = instances.indexOf(instance), privateObj;

        if(index == -1) {
            // Lazily associate instance with a new private object
            instances.push(instance);
            privates.push(privateObj = {});
        }
        else {
            // A privateObject has already been created, so grab that
            privateObj = privates[index];
        }
        return privateObj;
    }

    function Foo() {
        _(this).bar = "This is a private bar!";
    }

    Foo.prototype.getBar = function() {
        return _(this).bar;
    };

    return Foo;
})();

您会注意到上面的_功能。这是获取私有对象的访问器功能。它懒惰地工作,所以如果你用一个新实例调用它,它将动态创建一个新的私有对象。

如果您不想为每个类复制_代码,可以通过将其包装在工厂函数中来解决此问题:

function createPrivateStore() {
    var instances = [], privates = [];

    return function (instance) {
        // Same implementation as example above ...
    };
}

现在,您可以将每个类只减少一行:

var _ = createPrivateStore();

同样,你必须非常小心使用此解决方案,因为如果你没有实现并在必要时调用destroy函数,可以创建内存泄漏。

答案 3 :(得分:2)

现代浏览器采用某些ES6技术,您可以使用WeakMap来解决GUID问题。这适用于IE11及以上版本:

// Scope private vars inside an IIFE
var Foo = (function() { 
    // Store all the Foos, and garbage-collect them automatically
    var fooMap = new WeakMap();

    var Foo = function(txt) { 
        var privateMethod = function() { 
            console.log(txt); 
        };
        // Store this Foo in the WeakMap
        fooMap.set(this, {privateMethod: privateMethod}); 
    } 

    Foo.prototype = Object.create(Object.prototype); 
    Foo.prototype.public = function() { 
        fooMap.get(this).p(); 
     } 
     return Foo; 
 }());

 var foo1 = new Foo("This is foo1's private method");
 var foo2 = new Foo("This is foo2's private method");
 foo1.public(); // "This is foo1's private method"
 foo2.public(); // "This is foo2's private method"

WeakMap在被删除或超出范围后不会存储对任何Foo的引用,并且由于它使用对象作为键,因此您无需将GUID附加到对象。

答案 4 :(得分:0)

就个人而言,我并不喜欢使用guid的解决方案,因为它会强制开发人员在存储之外声明它并在构造函数中增加它。在大型javascript应用程序中,开发人员可能会忘记这样做,这很容易出错。

我非常喜欢Peter的答案,因为您可以使用上下文(this)访问私有成员。但令我困扰的一件事是,私人成员的访问是以复杂的方式完成的。实际上,在阵列中找到对象的索引是线性算法。考虑您要将此模式用于10000次实例化的对象。然后,每次要访问私有成员时,您可以迭代10000个实例。

为了以复杂的方式访问私人商店,除了使用guid之外别无他法。但是为了不打扰guid声明和增量,并且为了使用上下文访问私有商店,我修改了Peters工厂模式如下:

createPrivateStore = function () {
var privates = {}, guid = 0;

return function (instance) {
    if (instance.__ajxguid__ === undefined) {
        // Lazily associate instance with a new private object
        var private_obj = {};
        instance.__ajxguid__ = ++guid;
        privates[instance.__ajxguid__] = private_obj;
        return private_obj;
    }

    return privates[instance.__ajxguid__];
}

}

这里的诀窍是考虑尚未处理没有 ajxguid 属性的对象。实际上,人们可以在第一次访问商店之前手动设置属性,但我认为没有神奇的解决方案。

答案 5 :(得分:0)

我认为真实的隐私被高估了。虚拟隐私是所需要的。我认为_privateIdentifier的使用是朝正确方向迈出的一步,但还远远不够,因为您仍会看到智能感知弹出窗口中所有_privateIdentifiers的列表。进一步更好的步骤是在原型和/或构造函数中创建一个对象,以将您的虚拟私有字段和方法隔离开,如下所示:

  // Create the object
  function MyObject() {}

  // Add methods to the prototype
  MyObject.prototype = {

    // This is our public method
    public: function () {
      console.log('PUBLIC method has been called');
    },

    // This is our private method tucked away inside a nested privacy object called x
    x: {
      private: function () {
        console.log('PRIVATE method has been called');
      }
    },

  }

// Create an instance of the object
var mo = new MyObject(); 

现在当编码器键入“ mo”时。智能感知将仅显示公共功能和“ x”。因此,所有私有成员都没有显示,而是隐藏在“ x”后面,这使编码人员不太可能意外调用私有成员,因为他们必须走开路并键入“ mo.x”。去看私人会员此方法还可以避免智能感知列表被多个私有成员名称所困扰,将它们全部隐藏在单个项目“ x”后面。

答案 6 :(得分:0)

我知道这个线程现在真的很旧了,但是认为此解决方案可能对任何路过的人都感兴趣:

const Immutable = function ( val ) {
    let _val = val;

    this.$ = {
        _resolve: function () {
            return _val;
        }
    };
};
Immutable.prototype = {
    resolve: function () {
        return this.$._resolve();
    }
};

本质上隐藏了内部 _val 使其免受操纵,并使该对象的实例不可变。

答案 7 :(得分:-1)

我创建了一个新的库,用于在原型链上启用私有方法。 https://github.com/TremayneChrist/ProtectJS

示例:

var MyObject = (function () {

  // Create the object
  function MyObject() {}

  // Add methods to the prototype
  MyObject.prototype = {

    // This is our public method
    public: function () {
      console.log('PUBLIC method has been called');
    },

    // This is our private method, using (_)
    _private: function () {
      console.log('PRIVATE method has been called');
    }
  }

  return protect(MyObject);

})();

// Create an instance of the object
var mo = new MyObject();

// Call its methods
mo.public(); // Pass
mo._private(); // Fail