2

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

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说“除了他们自己,他们没有人可以责备”

现在评论bobince提出的问题:

即使您制作了真正的私有/受保护变量,它仍然无法为您提供有效安全边界所需的完整封装。JavaScript 能够修改您的方法将使用的内置类型的原型,从而使攻击者能够破坏这些方法。

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

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

2 回答 2

7

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

说真的,您不会在 JS 脚本中设置安全边界,从而需要真正的适当保护。访问级别是传统 OO 编码人员的痴迷,在 Web 脚本的上下文中没有任何意义。即使您制作了真正的私有/受保护变量,它仍然无法为您提供有效安全边界所需的完整封装。JavaScript 能够修改您的方法将使用的内置类型的原型,从而使攻击者能够破坏这些方法。

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

然后,您可以选择 per-instance-members(为了回调绑定方便)或原型成员(为了提高效率),无论您是否希望它们是私有的,并且您不会因不一致而绊倒自己。

于 2009-12-24T14:43:46.203 回答
2

http://www.crockford.com/javascript/private.html

说明一切...

于 2011-02-03T10:04:48.240 回答