2011-11-07 28 views
11

C'è un modello JavaScript che imita le proprietà dell'oggetto "Protetto" come quello che si vede in linguaggi come C++ ??Come creare proprietà oggetto protetto in JavaScript

Fondamentalmente, mi piacerebbe creare un oggetto A che ha un numero di proprietà dell'oggetto "protetto" a cui è possibile accedere SOLO dai metodi definiti dal prototipo di Oggetto A. cioè - NON accessibile pubblicamente da non- metodi realizzato il prototipo di A.

Per esempio, idealmente sarebbero in questo modo:

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 - non voglio che il modello delle funzioni di membro privilegiato di accesso a proprietà private in quanto la funzione di membro è ancora pubblico.

+2

No ............ –

+6

Il mio consiglio, come spesso, è di non forzare la visibilità in JavaScript. * Alcune cose * sono possibili con chiusura, ecc, ma la lingua non è progettata per questo. Renderà il tuo codice più complesso. Invece, documenta i tuoi metodi correttamente come privati ​​o pubblici e se altri sviluppatori non seguono le tue specifiche, è il loro problema. –

+0

Btw, gli oggetti prototipo possono essere aumentati (non sono sigillati) - nulla impedisce a un intruso di aggiungere nuovi metodi all'oggetto prototipo. Quindi, avere una proprietà accessibile solo tramite metodi prototipo non sarebbe comunque sicuro (anche se fosse possibile). –

risposta

12

Non esiste alcuna proprietà dell'oggetto a cui è possibile accedere solo dai metodi prototipati di A e non da metodi non prototipati di A. Il linguaggio non ha quel tipo di funzionalità e non sono a conoscenza di alcun work-around/hack per implementarlo.

Utilizzando Doug Crockford's methods, è possibile creare proprietà membro a cui è possibile accedere solo da metodi predefiniti non prototipici (quelli definiti nel costruttore). Quindi, se stai provando a limitare l'accesso solo a un insieme predefinito di metodi, questo lo realizzerà. A parte questo, penso che tu sia sfortunato.

Se vuoi altre idee, probabilmente avrai più aiuto se descrivi di più su cosa stai effettivamente cercando di realizzare nel tuo codice piuttosto che solo come emulare una funzionalità in un'altra lingua. Javascript è così diverso da C++ che è meglio partire dalle esigenze del problema piuttosto che cercare di trovare un'analogia con alcune funzionalità del C++.

4

Questo è probabilmente quello che stai cercando: http://javascript.crockford.com/private.html

+0

Sono d'accordo. Quell'articolo di Doug Crockford è la "descrizione" definitiva delle opzioni per la privacy delle variabili membro. Questo è ciò che è disponibile. Sono tutti in qualche modo hack, dal momento che ogni variabile membro ufficialmente supportata dalla lingua è pubblica, ma è possibile ottenere la privacy in vari modi utilizzando le chiusure. – jfriend00

+2

@Ericson L'OP ha dichiarato di non essere interessato alle funzioni privilegiate ... –

+3

Vorrei anche sottolineare che mentre puoi fare quello che Crockford ha in quell'articolo, non significa che dovresti farlo. Programma in linguaggio X come se stessi programmando per linguaggio X, non linguaggio Y. Detto questo, ci sono alcuni usi legittimi per (! AHEM!) Variabili private (penso che la mia bocca abbia appena dissanguato dicendole) in JS, ma non ci andrò, perché questi usi non hanno davvero nulla a che fare con il design di classe, per così dire. –

9

Non si può farlo in Javascript.

+0

Puoi spiegare perché ho ottenuto un -1? Quello che ho scritto è completamente vero in JS. Non è possibile creare proprietà protette in JS. Non c'è nient'altro. –

+4

Questo è vero, provare a fingere che il contrario sia molto più dannoso di accettare questo semplice fatto. – Esailija

+5

{{citation needed}} – Offirmo

5

Ho trovato un modo per creare membri protetti. Perciò io chiamo il costruttore di base e restituire un oggetto con i membri protetti allo stesso tempo:

var protected = BaseClass.call(this); 

Ecco un esempio:

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

Lo svantaggio di questo approccio è che si spreca memoria per ogni impedenza, perché ogni istanza ha funzioni duplicate piuttosto che riutilizzare i metodi sui prototipi. Quindi per 100 istanze hai 100 versioni delle funzioni definite nel costruttore, mentre con i metodi prototipo avresti solo una istanza di funzione. Vedere questo per un metodo che non spreca memoria: https://github.com/philipwalton/mozart – trusktr

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

ero interessato a trovare un modo per rispondere alla tua domanda, ed ecco cosa sono stato in grado di fare.

Avrete bisogno di questo helper:

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

questo helper vi permetterà di gestire più eredità. Il trucco è copiare la variabile protetta dall'oggetto base al nuovo oggetto (figlio).

per dimostrare che si sta lavorando, ecco un esempio:

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

E 'qui il test:

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

c'è un modello che io sia venuto ad apprezzare che non funziona allo stesso modo come l'accesso protetto fa nella maggior parte delle lingue, ma offre un vantaggio simile.

Fondamentalmente, utilizzare un metodo builder per creare una chiusura per le proprietà, quindi disporre di tale metodo per creare un oggetto "completo" con accesso liberale e un oggetto "esposto" con accesso più limitato. Posizionare l'oggetto esposto in una proprietà dell'oggetto completo e restituire l'oggetto completo al chiamante.

Il chiamante può quindi utilizzare l'oggetto completo (e passarlo ad altri collaboratori appropriati), ma fornire solo l'oggetto esposto ai collaboratori che dovrebbero avere l'accesso più limitato.

Un esempio inventato ...

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

Se usiamo che per costruire un anello di 4 voci ring = buildRing(4);, quindi ring.getHead().getIndex() ci dà 0, ring.getHead().getNext().getIndex() ci dà 1, ring.getHead().getNext().getNext().getIndex() ci dà 2, ecc

Se proviamo a eseguire ring.getHead().setNext({}) o ring.getHead().getNext().setNext({}), tuttavia, otteniamo un errore perché setNext non è una proprietà di un oggetto di ingresso esposto.

Avvertimento:

Poiché questo è nella famiglia di modelli che costruiscono i metodi di nuovo in una nuova chiusura per ogni nuovo oggetto, non è adatta a situazioni in cui può essere necessario un volume molto elevato di istanziazione.

+0

Si noti che questo modello può essere utilizzato anche con una funzione di costruzione per l'oggetto completo. Semplicemente crea e assegna la proprietà esposta di 'this' dal corpo del costruttore. –

Problemi correlati