17

好吧,我试图弄清楚这是否有可能。这是代码:

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 :(

如您所见,我尝试创建原型吸气剂。为了什么?好吧,如果你写这样的东西:

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());
4

8 回答 8

31

JavaScript 传统上不提供属性隐藏机制(“私有成员”)。

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

当然,有一些方法可以解决这个问题,但我不推荐它:

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;
})();

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


编辑 2015-12-01: ECMAScript6 使不需要手动对象破坏的改进变体成为可能,例如通过使用Wea​​kMap或最好是Symbol,完全避免了对外部存储的需要:

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

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

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

    return Foo;
})();
于 2009-01-27T12:55:56.513 回答
3

现代浏览器采用了一些 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 附加到对象。

于 2015-10-06T13:59:32.263 回答
2

原型上的方法不能访问“私有”成员,因为它们存在于 javascript 中;你需要某种特权访问者。由于您声明get了它可以在哪里看到b,它总是会返回b创建时的内容。

于 2009-01-27T12:36:08.597 回答
2

在受到 Christoph 的变通方法的极大启发后,我想出了一个稍微修改的概念,它提供了一些增强功能。同样,这个解决方案很有趣,但不一定推荐。这些增强功能包括:

  • 不再需要在构造函数中执行任何设置
  • 不再需要在实例上存储公共 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();

同样,您必须非常小心地使用此解决方案,因为如果您不实现并在必要时调用销毁函数,它可能会造成内存泄漏。

于 2013-12-11T07:43:55.617 回答
0

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

我非常喜欢彼得的回答,因为您可以使用上下文(this)访问私有成员。但让我非常困扰的一件事是,对私有成员的访问是在 ao(n) 复杂度中完成的。实际上,在数组中查找对象的索引是一种线性算法。假设您想将此模式用于实例化 10000 次的对象。然后,您可能会在每次想要访问私有成员时迭代 10000 个实例。

为了以 ao(1) 复杂性访问私有存储,除了使用 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属性的对象。确实,可以在第一次访问商店之前手动设置属性,但我认为没有神奇的解决方案。

于 2014-04-17T13:16:43.953 回答
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”后面。

于 2018-08-17T03:41:39.413 回答
0

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

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

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

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

于 2018-09-22T19:36:24.977 回答
-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
于 2014-01-16T16:15:08.287 回答