在JavaScript(ES5 +)中,我正在尝试实现以下方案:

  • 一个对象(将有许多单独的实例)每个对象都有一个只读属性.size,可以通过直接属性读取从外部读取它,但不能从外部设置。
  • 必须通过原型(prototype)上的某些方法(并应保留在原型(prototype)上)维护/更新.size属性。
  • 我的API已由规范定义,因此我无法对其进行修改(我正在为已定义的ES6对象进行polyfill处理)。
  • 我主要是在试图防止人们意外地用脚射击自己,并且不必真正具有防弹只读性(尽管防 flex 越强越好),所以我愿意妥协只要不允许直接设置obj.size = 3;,就可以在侧门上访问该属性。

  • 我知道我可以使用构造函数中声明的私有(private)变量并设置一个getter来读取它,但是我必须将需要维护该变量的方法移出原型(prototype),并在构造函数中声明它们(因此他们可以访问包含变量的闭包)。对于这种特殊情况,我宁愿不要将方法从原型(prototype)中删除,而是要寻找其他可能的选择。

    还有什么其他想法(即使有一些妥协)?

    最佳答案

    好的,因此对于解决方案,您需要两个部分:

  • 不可分配的size属性,即writable:true或没有setter属性
  • 一种更改size反射(reflect)的值的方法,该值不是.size = …且是公共(public)的,以便原型(prototype)方法可以调用它。

  • @plalx已经通过第二个“半私有(private)的” _size属性提供了一种明显的方式,该属性由size的getter反射(reflect)出来。这可能是最简单,最直接的解决方案:
    // declare
    Object.defineProperty(MyObj.prototype, "size", {
        get: function() { return this._size; }
    });
    // assign
    instance._size = …;
    

    另一种方法是使size属性不可写,但可配置,以便您必须对Object.defineProperty使用“很长的路要走”(尽管imho对于辅助函数来说太短了)才能在其中设置值:
    function MyObj() { // Constructor
        // declare
        Object.defineProperty(this, "size", {
            writable: false, enumerable: true, configurable: true
        });
    }
    // assign
    Object.defineProperty(instance, "size", {value:…});
    

    这两种方法绝对足以防止“脚踏实地”的size = …分配。对于更复杂的方法,我们可以构建一个公共(public)的,特定于实例的(关闭)设置方法,该方法只能从原型(prototype)模块作用域方法中调用。
    (function() { // module IEFE
        // with privileged access to this helper function:
        var settable = false;
        function setSize(o, v) {
            settable = true;
            o.size = v;
            settable = false;
        }
    
        function MyObj() { // Constructor
            // declare
            var size;
            Object.defineProperty(this, "size", {
                enumerable: true,
                get: function() { return size; },
                set: function(v) {
                    if (!settable) throw new Error("You're not allowed.");
                    size = v;
                }
            });
            …
        }
    
        // assign
        setSize(instance, …);
    
        …
    }());
    

    只要没有泄漏对settable的封闭访问,这确实是故障安全的。还有一种类似的,流行的,简短的方法是使用对象的身份作为访问 token ,大致如下:
    // module IEFE with privileged access to this token:
    var token = {};
    
    // in the declaration (similar to the setter above)
    this._setSize = function(key, v) {
        if (key !== token) throw new Error("You're not allowed.");
            size = v;
    };
    
    // assign
    instance._setSize(token, …);
    

    但是,此模式并不安全,因为有可能通过使用带有恶意token方法的自定义对象将带有赋值的代码应用于自定义对象来窃取_setSize

    07-24 09:50
    查看更多