如何使用 Prototype 库模拟 JavaScript 中的访问修饰符?

发布于 2024-08-15 12:35:01 字数 3773 浏览 8 评论 0原文

我使用原型库已经有一段时间了,偶尔发现自己希望拥有多个访问级别(公共、私有和受保护)。到目前为止,我最接近的是以下内容:

SampleBase = Class.create({
    /* virtual public constructor */
    initialize: function(arg1, arg2)
    {
        // private variables
        var privateVar1, privateVar2;

        // private methods
        var privateMethod1 = function() { }
        function privateMethod2() { }

        // public (non virtual) methods
        this.publicNonVirtual1 = function() { return privateVar1; }
        this.publicNonVirtual2 = function() { return privateVar2; }
    },
    // public methods (that cannot access privates)
    publicVirtual1: function() { /* Cannot access privates here. */ },
    publicVirtual2: function() { /* Cannot access privates here. */ }
});

由于以下几个原因,这不太理想:

  • 没有受保护级别
  • 我可以拥有可以访问私有成员的公共成员或可以覆盖的公共成员,但不能拥有可以访问私有成员的公共成员并被覆盖。
  • 我的可以被重写的公共方法没有原型化。

我做了一些搜索,但没有发现任何表明我可以在不改变原型工作方式的情况下做得更好的东西。以下是一些更有趣的链接:

我已经看到它建议您可以通过执行以下操作来为我的公共虚拟方法提供访问器:

Message = Class.create({
    initialize: function(message)
    {
        var _message = message;
        this.getMessage = function() { return _message; }
        this.setMessage = function(value) { _message = value; }
    },
    printMessage: function() { console.log(this.getMessage()); },
    appendToMessage: function(value) { this.setMessage(this.getMessage() + value); }
});

这显然不会按预期工作。目标是只允许从对象外部打印和附加到消息。为使虚拟公共功能正常工作而提供的设置器还允许完全控制消息。可以对其进行更改,以使虚拟方法更像一个 shell,如下所示:

Message = Class.create({
    initialize: function(message)
    {
        var _message = message;
        this._printMessage = function() { console.log(_message); }
        this._appendToMessage = function(value) { _message += value; }
    },
    printMessage: function() {this._printMessage(); },
    appendToMessage: function(value) { this._appendToMessage(value); }
});

这个新版本确实限制了此类的公共访问,但有些多余。更不用说如果在子类中重写appendToMessage,第三方仍然可以调用_appendToMessage来访问原始方法,这是不好的。

我确实有一个非常肮脏的想法,它会让我接近,但它是一罐蠕虫,我不想打开。我可能会稍后发布,但与此同时,是否有人对将两种类型的公共方法合并为一种有用的类型或如何实现受保护的成员提出建议。

编辑: 我怀疑缺乏反馈(除了 bobince 的不要打扰评论)意味着我是正确的,因为你我不能再继续下去了,但我想我会澄清一下以防万一。我认为不可能得到任何接近其他语言的保护。我更感兴趣的是知道限制在哪里以及我对所涉及原则的理解有多准确。不过,我确实认为,如果我们能让各种保护级别发挥作用,使非公共成员不会出现在 for...in 循环中(或在 Prototypes Object.keys 中),即使没有用,也会很有趣。使用 for..in),即使知道自己在做什么的人仍然可以通过修改我的原型之类的事情来打破规则。毕竟,就像 bobince 说“除了他们自己之外,没有人可以责怪”

现在评论 < 提出的问题a href="https://stackoverflow.com/users/18936/bobince">bobince:

即使你是真的 私有/受保护的变量仍然 不会让你得到完整的 封装有效的安全性 边界将需要。 JavaScript 的 摆弄原型的能力 您的方法将是内置类型 使用使攻击者能够 破坏方法。

这是我确实理解的一个限制,我可能应该在上面提到。然而,我并不是从保护我的代码不被试图“破解”它的人的角度来看这个问题的。然而,我确实有一些事情可能值得注意(或者如果我错了,则需要纠正):

  • 只有我的公共成员容易受到这种方式的攻击。
  • 如果我的公共虚拟方法以这种方式“受到损害”,那么“受到损害”的方法仍然无法访问私有成员。
  • 如果我的公共(非虚拟)成员以这种方式“受到损害”,那么与该方法的原始版本不同,“受到损害”的版本将无法访问私人成员。
  • 据我所知,通过在初始化方法之外定义的方法来访问私有成员的唯一方法也是利用某些浏览器处理 eval 调用的方式中的错误。

I've been working with the prototype library for some time now and occasionally find myself wishing I had multiple access levels (public, private, and protected). The closest I've come so far is the following:

SampleBase = Class.create({
    /* virtual public constructor */
    initialize: function(arg1, arg2)
    {
        // private variables
        var privateVar1, privateVar2;

        // private methods
        var privateMethod1 = function() { }
        function privateMethod2() { }

        // public (non virtual) methods
        this.publicNonVirtual1 = function() { return privateVar1; }
        this.publicNonVirtual2 = function() { return privateVar2; }
    },
    // public methods (that cannot access privates)
    publicVirtual1: function() { /* Cannot access privates here. */ },
    publicVirtual2: function() { /* Cannot access privates here. */ }
});

This less then ideal for several reasons:

  • No protected level
  • I can have public members that can access private members or public members that can be overridden but not not public member that can access private members and be overridden.
  • My public methods that can be overridden are not prototyped.

I've done some searching but haven't found anything that suggests I can do better without altering how prototype works. Here's some of the more interesting links:

I've seen it suggested that you can provide accessors for my public virtual methods to use by doing something like this:

Message = Class.create({
    initialize: function(message)
    {
        var _message = message;
        this.getMessage = function() { return _message; }
        this.setMessage = function(value) { _message = value; }
    },
    printMessage: function() { console.log(this.getMessage()); },
    appendToMessage: function(value) { this.setMessage(this.getMessage() + value); }
});

This clearly will not work as intended. The goal is to only allow printing and appending to the message from outside of the object. The setter provided to make the virtual public function work also allows full control of the message. It could be changed to make the virtual method little more then a shell as follows:

Message = Class.create({
    initialize: function(message)
    {
        var _message = message;
        this._printMessage = function() { console.log(_message); }
        this._appendToMessage = function(value) { _message += value; }
    },
    printMessage: function() {this._printMessage(); },
    appendToMessage: function(value) { this._appendToMessage(value); }
});

This new version does limit public access for this class but is somewhat redundant. Not to mention if appendToMessage is overriden in a subclass a third party can still call _appendToMessage to access the original method which is not good.

I do have a very dirty idea that will get me close but is a can of worms I'd rather not open. I may post it later but in the mean time does anyone have suggestions for merging the two types of public methods into one useful type or on how to implement protected members.

EDIT:
I suspect the lack of feedback (aside from bobince's don't bother comment) mean that I am correct in that you can't take it any farther but I think I'll clarify a little just in case. I don't think it is possible to get anything close to the protection of other languages. I'm more interested in knowing where the limits lie and how accurate my understanding of the principles involved are. I do however think it would be interesting, if not useful, if we could get the various protection levels functioning to the point that non public members don't show up in a for...in loop (or in Prototypes Object.keys which uses for..in) even if it people who know what they are doing can still break the rules by doing things like tinkering with my prototypes. Afterall, its like bobince says " they have no-one to blame but themselves"

Now to comment on an issue raised by bobince:

Even if you made real
private/protected variables it still
wouldn't get you the full
encapsulation an effective security
boundary would require. JavaScript's
ability to fiddle with the prototypes
of built-in types your methods will be
using gives an attacker the ability to
sabotage the methods.

This is one limitation I do understand and I probably should have mentioned above. However, I am not looking at this from the point of view of protecting my code from someone trying to "hack" it. However, I do have a few things that may be worth noting (or in need of correcting if I am wrong):

  • Only my public members are vulnerable in this way.
  • If my public virtual methods are "compromised" in this way, then the "compromised" methods will still not have access to the private members.
  • If my public (non virtual) members are "compromised" in this way, then, unlike the original version of the method, the "compromised" version will not have access to the private memebers.
  • As far as i know the only way to gain accesses to the private members by methods defined outside of initialize method is too take advantage of a bug in the way some browsers handle eval calls.

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

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

发布评论

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

评论(2

裂开嘴轻声笑有多痛 2024-08-22 12:35:01

如何在 JavaScript 中创建受保护成员:在名称开头添加下划线。

说真的,JS 脚本中不会有安全边界,因此需要真正适当的保护。访问级别是 trad-OO 编码人员的痴迷,在 Web 脚本环境中毫无意义。即使您创建了真正的私有/受保护变量,它仍然无法为您提供有效安全边界所需的完整封装。 JavaScript 能够篡改方法将使用的内置类型的原型,这使攻击者能够破坏这些方法。

所以不用费心去尝试。就像 Python 人员所做的那样:将该方法标记为外部人员不应该调用的方法,然后就可以完成它了。如果有人使用您的代码并依赖于名称开头带有 _ 的成员,那就是他们的问题,当脚本中断时,除了他们自己之外,没有人可以责怪。同时,由于没有严格的私有和受保护成员,您将使开发和调试阶段变得更容易。

然后,您可以选择每个实例成员(为了回调绑定方便)或原型成员(为了效率),无论您是否希望它们是私有的,并且您不会因为不一致而陷入困境。

How to make a protected member in JavaScript: put an underscore at the start of the name.

Seriously, you're not going to have security boundaries within a JS script that would make real proper protection necessary. Access levels are a trad-OO coder's obsession that make no sense in the context of web scripting. Even if you made real private/protected variables it still wouldn't get you the full encapsulation an effective security boundary would require. JavaScript's ability to fiddle with the prototypes of built-in types your methods will be using gives an attacker the ability to sabotage the methods.

So don't bother try. Just do like the Pythoners do: flag the method as something outsiders shouldn't be calling, and be done with it. If someone uses your code and relies on a member with _ at the start of the name, that's their problem and they have no-one to blame but themselves when their script breaks. Meanwhile you'll make the development and debugging stages easier for yourself by not having strict private and protected members.

Then you can choose per-instance-members (for callback binding convenience) or prototyped members (for efficiency), whether or not you intend them to be private, and you won't trip yourself up on the inconsistency.

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