如何在JavaScript中创建受保护的对象属性

时间:2011-11-07 23:32:49

标签: javascript prototype-programming

是否存在模仿“受保护”对象属性的JavaScript模式,就像您在C ++等语言中看到的那样?

基本上,我想创建一个具有许多“受保护”对象属性的对象A,这些属性只能从对象A的原型中定义的方法访问。即 - 不能从非原型公开访问A的方法。

例如,理想情况是这样的:

function A(){
    var prop1 = 1;      
}

A.prototype.myFunc = function(){
    var newVar = this.prop1;   //newVar now is equivalent to 1
}

var instanceOfA = new A();
var newVar2 = instanceOfA.prop1;  //error given as prop1 is "protected"; hence undefined in this case

BTW - 我不希望特权成员函数的模式访问私有属性,因为成员函数仍然是公共的。

8 个答案:

答案 0 :(得分:12)

没有对象属性只能从A的原型方法访问,而不能从A的非原型方法访问。该语言没有这种类型的功能,我不知道任何解决方法/黑客实现它。

使用Doug Crockford's methods,您可以创建只能从预定义的非原型方法(构造函数中定义的方法)访问的成员属性。因此,如果您尝试仅限制访问一组预定义的方法,则可以实现此目的。除此之外,我认为你运气不好。

如果您想要其他想法,如果您更多地了解您在代码中实际尝试完成的内容,而不仅仅是如何使用其他语言模拟某个功能,则可能会获得更多帮助。 Javascript与C ++有很大的不同,最好从问题的需要开始,而不是试图找到一些C ++特性的类比。

答案 1 :(得分:9)

你不能用Javascript。

答案 2 :(得分:5)

我找到了创建受保护成员的方法。因此,我调用基础构造函数并同时返回带有受保护成员的对象:

var protected = BaseClass.call(this); 

这是一个例子:

function SignedIntegerArray(size)
{
    var public = this;
    var protected = {};

    // private property:
    var _maxSize = 10000;
    // protected property:
    protected.array = [];
    // public property:
    public.Length = size;

    if(!isInteger(size) || size < 0 || size > _maxSize) { throw "argument exception"; }
    for(var index = 0; index != size; index++) { protected.array[index] = 0; }

    // private method:
    function isInteger(i) { return i == i + 0 && i == ~~i; }
    // protected method:
    protected.checkIndex = function(index) { return index >= 0 && index < size; }
    // public methods:
    public.SetValue = function(index, value) { if(protected.checkIndex(index) && isInteger(value)) { protected.array[index] = value; } };
    public.GetValue = function(index) { if(protected.checkIndex(index)) { return protected.array[index]; } else { throw "index out of range exception"; }}

    return protected;
}

function FloatArray(size, range)
{
    var public = this;
    var protected = SignedIntegerArray.call(this, size); // call the base constructor and get the protected members 

    // new private method, "isInteger" is hidden...
    function isFloat(argument) { return argument != ~~argument; }
    // ...but "checkIndex" is accessible
    public.SetValue = function(index, value) { if(protected.checkIndex(index) && isFloat(value) && value >= public.MinValue && value <= public.MaxValue) { protected.array[index] = value; } };

    // new public properties:
    public.MinValue = -range;
    public.MaxValue = range;

    return protected; // for sub-classes
}

function newObject(className, args) { return new function() { className.apply(this, args)}} // you need to use function.call or function.apply to initialize an object. otherwise the protected-object is empty.
window.addEventListener("load", function()
{
    var o = newObject(FloatArray, [4, 50.0]);
    o.SetValue(3, 2.1);
    console.log(o.GetValue(3));
    console.log(o.Length); // property from the base-class
});

答案 3 :(得分:4)

这可能是您正在寻找的内容:http://javascript.crockford.com/private.html

答案 4 :(得分:2)

function ClassA(init)
{
    var protected = {};
    protected.prop = init * 10;
    if(this.constructor != ClassA) { return protected; }
}

function ClassB()
{
    var protected = ClassA.call(this, 5); //console.log(protected.prop);
}

//var a = new ClassA(123);
//var b = new ClassB();

答案 5 :(得分:1)

我有兴趣找到一种方法来回答你的问题,这就是我能做的事情。

你需要这个帮手:

var ProtectedHandler = (function () {
    /// <Sumarry>
    /// Tool to handle the protected members of each inheritance.
    /// </Summary>
    /// <param name="current">Current protected variable.</param>
    /// <param name="args">The arguments variable of the object.</param>
    /// <param name="callback">The function to initialise the variable in the 'object'.</param>
    /// <param name="isParent">Is this the ultimate base object.</param>
    function ProtectedHandler(current, args, callback, isParent) {
        this.child = getChild(args);
        if (callback)
            this.callback = callback;

        if (isParent)
            this.overrideChild(current);
    }

    // Get the ProtectedHandler from the arguments
    var getChild = function (args) {
        var child = null;
        if (args.length > 0 && (child = args[args.length - 1]) && child.constructor === ProtectedHandler)
            return child;
    };

    // Chain Initialise the protected variable of the object and its inheritances.
    ProtectedHandler.prototype.overrideChild = function (newValue) {
        if (this.callback != null) {
            this.callback(newValue);
        }
        if (this.child != null) {
            this.child.overrideChild(newValue);
        }
    };

    // Static function to create a new instance of the protectedHandler object.
    ProtectedHandler.handle = function (protected, arguments, callback, isParent) {
        return new ProtectedHandler(protected, arguments, callback, isParent);
    };

    return ProtectedHandler;
})();

此助手将允许您处理多个继承。诀窍是将受保护的变量从基础对象复制到新对象(子)。

为了证明你的工作,以下是一个例子:

// That's the default extends function from typescript (ref: http://www.typescriptlang.org/)
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};

var BaseClass = (function () {        
    function BaseClass() {
        // Members
        var private = {},
            protected = {},
            public = this;

        // Constructor
        ProtectedHandler.handle(protected, arguments, function () {
            protected.type = "BaseClass";
        }, true);

        // Methods
        protected.saySomething = function () {
            return "Hello World";
        };

        public.getType = function () {
            return protected.type;
        };
    }

    return BaseClass;
})();



var Person = (function (_super) {
    __extends(Person, _super);

    function Person(name) {
        // Members
        var private = {},
            protected = {},
            public;

        // Constructor
        _super.call(public = this, ProtectedHandler.handle(protected, arguments, function (p) {
            protected = p; //This is required to copy the object from its base object.
            protected.name = name;
            protected.type = "Person";
        }));

        //Method
        public.getName = function () {
            return protected.name;
        };

        public.saySomething = function () {
            return protected.saySomething();
        };
    }
    return Person;
})(BaseClass);


var Child = (function (_super) {
    __extends(Child, _super);

    function Child(name) {
        // Members
        var private = {},
            protected = {},
            public;

        // Constructor
        _super.call(public = this, name, ProtectedHandler.handle(protected, arguments, function (p) {
            protected = p; //This is required to copy the object from its base object.
            protected.type = "Child";
        }));

        //Method
        public.setName = function (value) {
            return protected.name = value;
        };
    }
    return Child;
})(Person);

以下是测试:

var testBase = new BaseClass();
testBase.getType(); //"BaseClass"
testBase.saySomething; //undefined

var testPerson = new Person("Nic");
testPerson.getType(); //"Person"
testPerson.saySomething(); //"Hello World"
testPerson.name; //undefined
testPerson.getName() //"Nic"
testPerson.setName; //undefined

var testChild = new Child("Bob");
testChild.getType(); //"Child"
testChild.saySomething(); //"Hello World"
testChild.name; //undefined
testChild.getName(); //"Bob"
testChild.setName("George");
testChild.getName(); //"George"

答案 6 :(得分:1)

我所喜欢的模式与大多数语言中的受保护访问方式不同,但提供了类似的好处。

基本上,使用构建器方法为属性创建闭包,然后让该方法创建一个&#34; full&#34;自由访问的对象以及&#34;暴露的&#34;访问权限受限的对象。将公开的对象放入完整对象的属性中,并将该完整对象返回给调用者。

然后,调用者可以使用完整对象(并将其传递给其他适当的协作者),但仅向应该具有更多限制访问权限的协作者提供公开的对象。

一个人为的例子......

// Ring employs a typical private/public pattern while
// RingEntry employs a private/exposed/full access pattern.

function buildRing( size ) {
  var i
    , head = buildRingEntry( 0 )
    , newEntry;
  ;
  head.setNext( head );
  for( i = size - 1; i ; i-- ) {
    newEntry = buildRingEntry( i );
    newEntry.setNext( head.getNext() );
    head.setNext( newEntry );
  }
  function getHead() { return head.exposed; }
  return {
      getHead : getHead
  }
}

function buildRingEntry( index ) {
  var next
    , exposed
  ;
  function getIndex() { return index; }
  function setNext( newNext ) { next = newNext; }
  function getNextFullEntry() { return next; }
  function getNextExposedEntry() { return next.exposed; }
  exposed = {
      getIndex : getIndex
    , getNext  : getNextExposedEntry
  };
  return {
      getIndex : getIndex
    , setNext  : setNext
    , getNext  : getNextFullEntry
    , exposed  : exposed
  };
}

如果我们使用它来构建一个包含4个条目ring = buildRing(4);的环,那么ring.getHead().getIndex()会给我们0,ring.getHead().getNext().getIndex()给我们1,ring.getHead().getNext().getNext().getIndex()给我们2,等等。

但是,如果我们尝试执行ring.getHead().setNext({})ring.getHead().getNext().setNext({}),则会收到错误,因为setNext不是公开的条目对象的属性。

警告:

由于这是在每个新对象的新闭包中再次构建方法的模式族,因此它不适用于可能需要非常大量实例化的情况。

答案 7 :(得分:0)

看看Maks在他的网站上提出的解决方法:Emulating protected members in JavaScript

它模拟对象的方法和属性的protected访问级别。