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

时间:2009-12-24 12:41:36

标签: javascript inheritance prototypejs theory access-modifiers

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

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

这显然不会按预期工作。目标是仅允许从对象外部打印和附加消息。为使虚拟公共功能工作而提供的setter也允许完全控制消息。它可以更改为使虚拟方法稍微多于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中,那么它将是有趣的,如果没有用的话)。即使知道他们正在做什么的人仍然可以通过修改我的原型等方法来破坏规则。毕竟,它像bobince所说“他们没有人责怪他们自己”

现在评论bobince提出的问题:

  

即使你成功了   它仍然是私有/受保护的变量   不会让你满满的   封装有效的安全性   边界需要。 JavaScript的   能够摆弄原型   你的方法将是内置类型   使用给予攻击者能力   破坏方法。

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

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

2 个答案:

答案 0 :(得分:7)

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

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

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

然后你可以选择per-instance-members(用于回调绑定方便)或prototyped成员(为了提高效率),无论你是否打算将它们设为私有,你都不会因为不一致而惹恼自己。

答案 1 :(得分:2)