原型上的 Javascript 私有成员

发布于 2024-07-12 06:06:02 字数 1311 浏览 4 评论 0原文

好吧,我试图弄清楚这是否可能以任何方式进行。 这是代码:

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 函数。 我希望内存中有一个原型函数可以做同样的事情......有什么想法吗?

编辑:

我尝试了克里斯托夫给出的解决方案,它似乎是目前唯一已知的解决方案。 它需要记住 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());

Well I tried to figure out is this possible in any way. Here is code:

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

As you see I tried to create prototype getter. For what? Well if you write something like this:

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

... everything should be fine.. but in fact every time I create object - i create getText function that uses memory. I would like to have one prototypical function lying in memory that would do the same... Any ideas?

EDIT:

I tried solution given by Christoph, and it seems that its only known solution for now. It need to remember id information to retrieve value from context, but whole idea is nice for me :) Id is only one thing to remember, everything else can be stored once in memory. In fact you could store a lot of private members this way, and use anytime only one id. Actually this is satisfying me :) (unless someone got better idea).

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

向地狱狂奔 2024-07-19 06:06:02

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 改进了不需要手动对象销毁的变体,例如通过使用 WeakMap 或最好是 Symbol,完全不需要外部存储:

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

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

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

    return Foo;
})();

JavaScript traditionally did not provide a mechanism for property hiding ('private members').

As JavaScript is lexically scoped, you could always simulate this on a per-object level by using the constructor function as a closure over your 'private members' and defining your methods in the constructor, but this won't work for methods defined in the constructor's prototype property.

Of course, there are ways to work around this, but I wouldn't recommend it:

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

This will store the 'private' properties in another object seperate from your Foo instance. Make sure to call destroy() after you're done wih the object: otherwise, you've just created a memory leak.


edit 2015-12-01: ECMAScript6 makes improved variants that do not require manual object destruction possible, eg by using a WeakMap or preferably a Symbol, avoiding the need for an external store altogether:

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

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

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

    return Foo;
})();
我不吻晚风 2024-07-19 06:06:02

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

With modern browsers adopting some ES6 technologies, you can use WeakMap to get around the GUID problem. This works in IE11 and above:

// 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 won't store references to any Foo after it gets deleted or falls out of scope, and since it uses objects as keys, you don't need to attach GUIDs to your object.

往日情怀 2024-07-19 06:06:02

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

Methods on the prototype cannot access "private" members as they exist in javascript; you need some kind of privileged accessor. Since you are declaring get where it can lexically see b, it will always return what b was upon creation.

难如初 2024-07-19 06:06:02

在受到 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();

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

After being hugely inspired by Christoph's work-around, I came up with a slightly modified concept that provides a few enhancements. Again, this solution is interesting, but not necessarily recommended. These enhancements include:

  • No longer need to perform any setup in the constructor
  • Removed the need to store a public GUID on instances
  • Added some syntactic sugar

Essentially the trick here is to use the instance object itself as the key to accessing the associated private object. Normally this is not possible with plain objects since their keys must be strings. However, I was able to accomplish this using the fact that the expression ({} === {}) returns false. In other words the comparison operator can discern between unique object instances.

Long story short, we can use two arrays to maintain instances and their associated private objects:

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

You'll notice the _ function above. This is the accessor function to grab ahold of the private object. It works lazily, so if you call it with a new instance, it will create a new private object on the fly.

If you don't want to duplicate the _ code for every class, you can solve this by wrapping it inside a factory function:

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

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

Now you can reduce it to just one line for each class:

var _ = createPrivateStore();

Again, you have to be very careful using this solution as it can create memory leaks if you do not implement and call a destroy function when necessary.

夏九 2024-07-19 06:06:02

就我个人而言,我不太喜欢使用 guid 的解决方案,因为它迫使开发人员除了存储之外还要声明它并在构造函数中递增它。 在大型 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 属性尚未处理。 事实上,人们可以在第一次访问商店之前手动设置属性,但我认为没有神奇的解决方案。

Personally, I don't really like the solution with the guid, because it forces the developer to declare it in addition to the store and to increment it in the constructor. In large javascript application developers might forget to do so which is quite error prone.

I like Peter's answer pretty much because of the fact that you can access the private members using the context (this). But one thing that bothers me quite much is the fact that the access to private members is done in a o(n) complexity. Indeed finding the index of an object in array is a linear algorithm. Consider you want to use this pattern for an object that is instanciated 10000 times. Then you might iterate through 10000 instances each time you want to access a private member.

In order to access to private stores in a o(1) complexity, there is no other way than to use guids. But in order not to bother with the guid declaration and incrementation and in order to use the context to access the private store I modified Peters factory pattern as follow:

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__];
}

}

The trick here is to consider that the objects that do not have the ajxguid property are not yet handled. Indeed, one could manually set the property before accessing the store for the first time, but I think there is no magical solution.

时光无声 2024-07-19 06:06:02

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

  // 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”后面。

I think real privacy is overrated. Virtual privacy is all that is needed. I think the use of _privateIdentifier is a step in the right direction but not far enough because you're still presented with a listing of all the _privateIdentifiers in intellisense popups. A further and better step is to create an object in the prototype and/or constructor function for segregating your virtually private fields and methods out of sight like so:

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

now when the coder types "mo." intellisense will only show the public function and "x". So all the private members are not shown but hidden behind "x" making it more unlikely for a coder to accidentally call a private member because they'll have to go out of their way and type "mo.x." to see private members. This method also avoids the intellisense listing from being cluttered with multiple private member names, hiding them all behind the single item "x".

贱人配狗天长地久 2024-07-19 06:06:02

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

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

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

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

I know this thread is really really old now, but thought this solution might be of interest to anyone passing by:

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

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

Essentially hiding the internal _val from being manipulated and making an instance of this object immutable.

悍妇囚夫 2024-07-19 06:06:02

我创建了一个新库,用于在原型链上启用私有方法。
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

I have created a new library for enabling private methods on the prototype chain.
https://github.com/TremayneChrist/ProtectJS

Example:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文