Подтвердить что ты не робот

Создание защищенных объектов в JavaScript

Есть ли шаблон JavaScript, который имитирует свойства объекта "Защищенный", как то, что вы видите на таких языках, как С++?

В принципе, я хотел бы создать объект A, который имеет ряд "защищенных" свойств объекта, к которым можно получить ТОЛЬКО ТОЛЬКО от методов, которые определены из прототипа объекта A. ie - НЕдоступно публично из не-прототипированных методы 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 - Я не хочу, чтобы шаблон привилегированных функций-членов обращался к закрытым свойствам, поскольку функция-член остается общедоступной.

4b9b3361

Ответ 1

Нет свойства объекта, доступ к которому можно получить только из прототипированных методов A, а не из не прототипированных методов A. Язык не имеет такого типа функции, и я не знаю о том, как это сделать.

Используя методы Doug Crockford, вы можете создавать свойства элемента, к которым можно получить доступ только из предопределенных непрототируемых методов (определенных в конструкторе). Итак, если вы пытаетесь ограничить доступ только предопределенным набором методов, это выполнит это. Кроме этого, я думаю, вам не повезло.

Если вам нужны другие идеи, вы, вероятно, получите больше помощи, если будете описывать больше о том, что вы на самом деле пытаетесь выполнить в своем коде, а не просто как эмулировать функцию на другом языке. Javascript настолько отличается от С++, что лучше начинать с потребностей проблемы, а не пытаться найти аналогию с некоторыми функциями С++.

Ответ 2

Вы не можете сделать это в Javascript.

Ответ 3

Я нашел способ создания защищенных членов. Поэтому я вызываю базовый конструктор и одновременно возвращаю объект с защищенными членами:

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

Ответ 5

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

Ответ 6

Мне было интересно найти способ ответить на ваш вопрос, и вот что я смог сделать.

Вам понадобится этот помощник:

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 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"

Ответ 7

Существует шаблон, который мне понравился, что работает не так, как защищенный доступ на большинстве языков, но обеспечивает аналогичную выгоду.

В принципе, используйте метод построителя для создания закрытия свойств, а затем создайте этот "полный" объект с либеральным доступом, а также "открытый" объект с более ограниченным доступом. Поместите выставленный объект в свойство полного объекта и верните этот полный объект вызывающему.

Затем вызывающий может использовать полный объект (и передавать его другим соответствующим сотрудникам), но предоставлять только открытый объект для соавторов, который должен иметь более ограниченный доступ.

Надуманный пример...

// 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 не является свойством открытого объекта записи.

Протест:

Так как это находится в семействе шаблонов, которые снова создают методы в новом закрытии для каждого нового объекта, это не подходит для ситуаций, в которых может потребоваться очень большой объем экземпляра.

Ответ 8

Взгляните на обходное решение, предложенное Максом на его веб-сайте: Эмуляция защищенных членов в JavaScript

Он эмулирует уровень доступа protected к методам и свойствам объекта.