Accesso alle variabili dei membri privati ​​da funzioni definite dal prototipo


187

Esiste un modo per rendere le variabili "private" (quelle definite nel costruttore), disponibili per i metodi definiti dal prototipo?

TestClass = function(){
    var privateField = "hello";
    this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};

Questo funziona:

t.nonProtoHello()

Ma questo non:

t.prototypeHello()

Sono abituato a definire i miei metodi all'interno del costruttore, ma mi sto allontanando da quello per un paio di ragioni.



14
@Campver, eccetto che questo è stato chiesto 2 anni prima ....
Pacerier

Risposte:


191

No, non c'è modo di farlo. Questo sarebbe essenzialmente scoping al contrario.

I metodi definiti all'interno del costruttore hanno accesso a variabili private perché tutte le funzioni hanno accesso all'ambito in cui sono state definite.

I metodi definiti su un prototipo non sono definiti nell'ambito del costruttore e non avranno accesso alle variabili locali del costruttore.

Puoi ancora avere variabili private, ma se desideri che i metodi definiti sul prototipo possano accedervi, devi definire getter e setter thissull'oggetto, a cui i metodi prototipo (insieme a tutto il resto) avranno accesso. Per esempio:

function Person(name, secret) {
    // public
    this.name = name;

    // private
    var secret = secret;

    // public methods have access to private members
    this.setSecret = function(s) {
        secret = s;
    }

    this.getSecret = function() {
        return secret;
    }
}

// Must use getters/setters 
Person.prototype.spillSecret = function() { alert(this.getSecret()); };

14
"scoping in reverse" è una funzione C ++ con la parola chiave "amico". Essenzialmente qualsiasi funzione dovrebbe definire il suo prototipo come amico. Purtroppo questo concetto è C ++ e non JS :(
TWiStErRob,

1
Vorrei aggiungere questo post in cima alla mia lista dei preferiti e tenerlo lì.
Donato,

2
Non capisco il punto: stai solo aggiungendo uno strato di astrazione che non fa nulla. Puoi anche fare secretuna proprietà di this. JavaScript semplicemente non supporta le variabili private con i prototipi poiché i prototipi sono legati al contesto del sito di chiamata, non al contesto del "sito di creazione".
nicodemus13

1
Perché non farlo person.getSecret()allora?
Fahmi,

1
Perché questo ha così tanti voti positivi? Questo non rende la variabile privata. Come accennato in precedenza, l'uso di person.getSecret () ti consentirà di accedere a tale variabile privata da qualsiasi luogo.
alexr101,

64

Aggiornamento: con ES6, esiste un modo migliore:

Per farla breve, puoi usare il nuovo Symbolper creare campi privati.
Ecco una grande descrizione: https://curiosity-driven.org/private-properties-in-javascript

Esempio:

var Person = (function() {
    // Only Person can access nameSymbol
    var nameSymbol = Symbol('name');

    function Person(name) {
        this[nameSymbol] = name;
    }

    Person.prototype.getName = function() {
        return this[nameSymbol];
    };

    return Person;
}());

Per tutti i browser moderni con ES5:

Puoi usare solo le chiusure

Il modo più semplice per costruire oggetti è evitare del tutto l'eredità prototipale. Basta definire le variabili private e le funzioni pubbliche all'interno della chiusura e tutti i metodi pubblici avranno accesso privato alle variabili.

Oppure puoi usare solo i prototipi

In JavaScript, l'eredità prototipale è principalmente un'ottimizzazione . Consente a più istanze di condividere metodi prototipo, piuttosto che ogni istanza ha i propri metodi.
Lo svantaggio è che thisè l' unica cosa che cambia ogni volta che viene chiamata una funzione prototipo.
Pertanto, tutti i campi privati ​​devono essere accessibili attraverso this, il che significa che saranno pubblici. Quindi atteniamo solo alle convenzioni di denominazione per i _privatecampi.

Non preoccuparti di mescolare le chiusure con i prototipi

Penso che non dovresti mescolare le variabili di chiusura con i metodi prototipo. Dovresti usare l'uno o l'altro.

Quando si utilizza una chiusura per accedere a una variabile privata, i metodi prototipo non possono accedere alla variabile. Quindi, devi esporre la chiusura this, il che significa che la stai esponendo pubblicamente in un modo o nell'altro. C'è molto poco da guadagnare con questo approccio.

Quale scelgo?

Per oggetti davvero semplici, basta usare un oggetto semplice con chiusure.

Se hai bisogno dell'ereditarietà prototipale - per eredità, prestazioni, ecc. - segui la convenzione di denominazione "_private" e non preoccuparti delle chiusure.

Non capisco perché gli sviluppatori JS si sforzano così tanto di rendere i campi veramente privati.


4
Purtroppo, la _privateconvenzione di denominazione è ancora la soluzione migliore se si desidera trarre vantaggio dall'eredità prototipale.
schiaccia il

1
ES6 avrà un nuovo concetto, il Symbol, che è un modo eccellente per creare campi privati. Ecco una grande spiegazione: curiosity-driven.org/private-properties-in-javascript
Scott Rippey

1
No, puoi tenerlo Symbolin una chiusura che comprende tutta la tua classe. In questo modo, tutti i metodi prototipo possono utilizzare il simbolo, ma non viene mai esposto al di fuori della classe.
Scott Rippey,

2
L'articolo si è collegato dice: " I simboli sono simili ai nomi privati, ma - a differenza di nomi privati - che non forniscono la vera privacy . ". In effetti, se hai l'istanza, puoi ottenere i suoi simboli con Object.getOwnPropertySymbols. Quindi questa è solo privacy per oscurità.
Oriol,

2
@Oriol Sì, la privacy è attraverso una forte oscurità. È ancora possibile scorrere i simboli e dedurre lo scopo del simbolo tramite toString. Questo non è diverso da Java o C # ... i membri privati ​​sono ancora accessibili tramite la riflessione, ma di solito sono fortemente oscurati. Tutto ciò serve a rafforzare il mio ultimo punto, "Non capisco perché gli sviluppatori JS si sforzano così tanto di rendere i campi veramente privati".
Scott Rippey,

31

Quando l'ho letto, mi è sembrata una sfida difficile, quindi ho deciso di trovare un modo. Quello che mi è venuto in mente è CRAAAAZY ma funziona perfettamente .

Innanzitutto, ho provato a definire la classe in una funzione immediata in modo da avere accesso ad alcune delle proprietà private di quella funzione. Funziona e ti permette di ottenere alcuni dati privati, tuttavia, se provi a impostare i dati privati ​​scoprirai presto che tutti gli oggetti condivideranno lo stesso valore.

var SharedPrivateClass = (function() { // use immediate function
    // our private data
    var private = "Default";

    // create the constructor
    function SharedPrivateClass() {}

    // add to the prototype
    SharedPrivateClass.prototype.getPrivate = function() {
        // It has access to private vars from the immediate function!
        return private;
    };

    SharedPrivateClass.prototype.setPrivate = function(value) {
        private = value;
    };

    return SharedPrivateClass;
})();

var a = new SharedPrivateClass();
console.log("a:", a.getPrivate()); // "a: Default"

var b = new SharedPrivateClass();
console.log("b:", b.getPrivate()); // "b: Default"

a.setPrivate("foo"); // a Sets private to "foo"
console.log("a:", a.getPrivate()); // "a: foo"
console.log("b:", b.getPrivate()); // oh no, b.getPrivate() is "foo"!

console.log(a.hasOwnProperty("getPrivate")); // false. belongs to the prototype
console.log(a.private); // undefined

// getPrivate() is only created once and instanceof still works
console.log(a.getPrivate === b.getPrivate);
console.log(a instanceof SharedPrivateClass);
console.log(b instanceof SharedPrivateClass);

Ci sono molti casi in cui questo sarebbe adeguato come se volessi avere valori costanti come nomi di eventi che vengono condivisi tra istanze. Ma essenzialmente agiscono come variabili statiche private.

Se hai assolutamente bisogno dell'accesso alle variabili in uno spazio dei nomi privato all'interno dei tuoi metodi definiti sul prototipo, puoi provare questo schema.

var PrivateNamespaceClass = (function() { // immediate function
    var instance = 0, // counts the number of instances
        defaultName = "Default Name",  
        p = []; // an array of private objects

    // create the constructor
    function PrivateNamespaceClass() {
        // Increment the instance count and save it to the instance. 
        // This will become your key to your private space.
        this.i = instance++; 
        
        // Create a new object in the private space.
        p[this.i] = {};
        // Define properties or methods in the private space.
        p[this.i].name = defaultName;
        
        console.log("New instance " + this.i);        
    }

    PrivateNamespaceClass.prototype.getPrivateName = function() {
        // It has access to the private space and it's children!
        return p[this.i].name;
    };
    PrivateNamespaceClass.prototype.setPrivateName = function(value) {
        // Because you use the instance number assigned to the object (this.i)
        // as a key, the values set will not change in other instances.
        p[this.i].name = value;
        return "Set " + p[this.i].name;
    };

    return PrivateNamespaceClass;
})();

var a = new PrivateNamespaceClass();
console.log(a.getPrivateName()); // Default Name

var b = new PrivateNamespaceClass();
console.log(b.getPrivateName()); // Default Name

console.log(a.setPrivateName("A"));
console.log(b.setPrivateName("B"));
console.log(a.getPrivateName()); // A
console.log(b.getPrivateName()); // B

// private objects are not accessible outside the PrivateNamespaceClass function
console.log(a.p);

// the prototype functions are not re-created for each instance
// and instanceof still works
console.log(a.getPrivateName === b.getPrivateName);
console.log(a instanceof PrivateNamespaceClass);
console.log(b instanceof PrivateNamespaceClass);

Mi piacerebbe un feedback da parte di chiunque veda un errore in questo modo di farlo.


4
Immagino che una potenziale preoccupazione sia che qualsiasi istanza possa accedere a qualsiasi altra istanza privata utilizzando un ID istanza diverso. Non necessariamente una cosa negativa ...
Mims H. Wright,

15
Ridefinisci le funzioni del prototipo ad ogni chiamata del costruttore
Lu4

10
@ Lu4 Non sono sicuro che sia vero. Il costruttore viene restituito da una chiusura; l'unica volta che vengono definite le funzioni del prototipo è la prima volta, in quell'espressione di funzione immediatamente invocata. A parte i problemi di privacy menzionati sopra, questo mi sembra buono (a prima vista).
guypursey,

1
@ MimsH.Wright altre lingue consentono l'accesso ad altri oggetti privati della stessa classe , ma solo quando si fa riferimento ad essi. Per consentire ciò, è possibile nascondere i privati ​​dietro una funzione che accetta il puntatore agli oggetti come chiave (come associato a un ID). In questo modo hai accesso solo ai dati privati ​​degli oggetti che conosci, il che è più in linea con l'ambito in altre lingue. Tuttavia, questa implementazione fa luce su un problema più profondo con questo. Gli oggetti privati ​​non verranno mai raccolti in Garbage fino a quando non è la funzione Costruttore.
Thomas Nadin,

3
Voglio menzionare che iè stato aggiunto a tutte le istanze. Quindi non è completamente "trasparente" e ipotrebbe essere ancora manomesso.
Scott Rippey,

18

vedere la pagina di Doug Crockford su questo . Devi farlo indirettamente con qualcosa che può accedere all'ambito della variabile privata.

un altro esempio:

Incrementer = function(init) {
  var counter = init || 0;  // "counter" is a private variable
  this._increment = function() { return counter++; }
  this._set = function(x) { counter = x; }
}
Incrementer.prototype.increment = function() { return this._increment(); }
Incrementer.prototype.set = function(x) { return this._set(x); }

caso d'uso:

js>i = new Incrementer(100);
[object Object]
js>i.increment()
100
js>i.increment()
101
js>i.increment()
102
js>i.increment()
103
js>i.set(-44)
js>i.increment()
-44
js>i.increment()
-43
js>i.increment()
-42

47
Questo esempio sembra essere una pratica terribile. Il punto di usare i metodi prototipo è che non è necessario crearne uno nuovo per ogni istanza. Lo stai facendo comunque. Per ogni metodo ne stai creando un altro.
Kir

2
@ArmedMonkey Il concetto sembra valido, ma concorda che questo è un cattivo esempio perché le funzioni del prototipo mostrate sono banali. Se le funzioni del prototipo fossero funzioni molto più lunghe che richiedono un semplice accesso get / set alle variabili "private", avrebbe senso.
pancake

9
Perché preoccuparsi di esporre _setvia set? Perché non nominarlo setper cominciare?
Scott Rippey,

15

Suggerisco che sarebbe probabilmente una buona idea descrivere "avere un incarico prototipo in un costruttore" come un anti-pattern Javascript. Pensaci. È troppo rischioso.

Quello che stai effettivamente facendo lì nella creazione del secondo oggetto (cioè b) è ridefinire quella funzione prototipo per tutti gli oggetti che usano quel prototipo. Ciò ripristinerà efficacemente il valore per l'oggetto a nel tuo esempio. Funzionerà se vuoi una variabile condivisa e se ti capita di creare tutte le istanze dell'oggetto in anticipo, ma sembra troppo rischioso.

Ho trovato un bug in alcuni Javascript su cui stavo lavorando di recente a causa di questo preciso anti-pattern. Stava cercando di impostare un gestore di trascinamento della selezione sull'oggetto particolare che veniva creato, ma lo faceva invece per tutte le istanze. Non bene.

La soluzione di Doug Crockford è la migliore.


10

@Kai

Non funzionerà Se fate

var t2 = new TestClass();

quindi t2.prototypeHelloaccederà alla sezione privata di t.

@AnglesCrimes

Il codice di esempio funziona correttamente, ma in realtà crea un membro privato "statico" condiviso da tutte le istanze. Potrebbe non essere la soluzione cercata da morgancodes.

Finora non ho trovato un modo semplice e pulito per farlo senza introdurre un hash privato e funzioni di pulizia extra. Una funzione di membro privato può essere simulata in una certa misura:

(function() {
    function Foo() { ... }
    Foo.prototype.bar = function() {
       privateFoo.call(this, blah);
    };
    function privateFoo(blah) { 
        // scoped to the instance by passing this to call 
    }

    window.Foo = Foo;
}());

Comprendi chiaramente i tuoi punti, ma puoi spiegare cosa sta cercando di fare il tuo frammento di codice?
Vishwanath,

privateFooè completamente privato e quindi invisibile quando si ottiene un new Foo(). È solo bar()un metodo pubblico qui, che ha accesso a privateFoo. È possibile utilizzare lo stesso meccanismo per variabili e oggetti semplici, tuttavia è necessario tenere sempre presente che privatessono effettivamente statici e saranno condivisi da tutti gli oggetti creati.
Philzen,

6

Si è possibile. Il modello di progettazione PPF risolve questo problema.

PPF è l'acronimo di Private Prototype Functions. PPF di base risolve questi problemi:

  1. Le funzioni del prototipo ottengono l'accesso ai dati dell'istanza privata.
  2. Le funzioni del prototipo possono essere rese private.

Per il primo, solo:

  1. Inserire tutte le variabili di istanza private che si desidera siano accessibili dalle funzioni del prototipo all'interno di un contenitore di dati separato e
  2. Passare un riferimento al contenitore di dati a tutte le funzioni del prototipo come parametro.

È così semplice. Per esempio:

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  ...
}

// Prototype functions now have access to private instance data
Point.prototype.getX = function(data)
{
  return data.x;
}

Point.prototype.getY = function(data)
{
  return data.y;
}

...

Leggi la storia completa qui:

Modello di progettazione PPF


4
Le risposte solo link sono generalmente disapprovate su SO. Si prega di mostrare un esempio.
Corey Adler,

L'articolo contiene degli esempi, quindi per favore vedi lì
Edward,

5
Cosa succede, però, se ad un certo punto in un secondo momento quel sito dovesse andare in crash? In che modo qualcuno dovrebbe vedere un esempio allora? La politica è in atto in modo che qualsiasi cosa di valore in un collegamento possa essere conservata qui e non debba fare affidamento su un sito Web che ciò non è sotto il nostro controllo.
Corey Adler,

3
@Edward, il tuo link è una lettura interessante! Tuttavia, mi sembra che il motivo principale per accedere ai dati privati ​​utilizzando funzioni prototipiche sia quello di impedire che ogni oggetto sprechi memoria con identiche funzioni pubbliche. Il metodo che descrivi non risolve questo problema, poiché per un uso pubblico, una funzione prototipica deve essere racchiusa in una normale funzione pubblica. Immagino che il modello potrebbe essere utile per risparmiare memoria se hai molti ppf che sono combinati in una singola funzione pubblica. Li usi per qualcos'altro?
Dining Philosopher,

@DiningPhilosofer, grazie per aver apprezzato il mio articolo. Sì, hai ragione, usiamo ancora le funzioni di istanza. Ma l'idea è di averli il più leggeri possibile semplicemente richiamando le loro controparti in PPF che fanno tutto il lavoro pesante. Alla fine tutte le istanze chiamano gli stessi PPF (tramite involucri ovviamente), quindi ci si può aspettare un certo risparmio di memoria. La domanda è quanto. Mi aspetto un notevole risparmio.
Edward

5

Puoi effettivamente raggiungere questo obiettivo utilizzando la verifica degli accessi :

(function(key, global) {
  // Creates a private data accessor function.
  function _(pData) {
    return function(aKey) {
      return aKey === key && pData;
    };
  }

  // Private data accessor verifier.  Verifies by making sure that the string
  // version of the function looks normal and that the toString function hasn't
  // been modified.  NOTE:  Verification can be duped if the rogue code replaces
  // Function.prototype.toString before this closure executes.
  function $(me) {
    if(me._ + '' == _asString && me._.toString === _toString) {
      return me._(key);
    }
  }
  var _asString = _({}) + '', _toString = _.toString;

  // Creates a Person class.
  var PersonPrototype = (global.Person = function(firstName, lastName) {
    this._ = _({
      firstName : firstName,
      lastName : lastName
    });
  }).prototype;
  PersonPrototype.getName = function() {
    var pData = $(this);
    return pData.firstName + ' ' + pData.lastName;
  };
  PersonPrototype.setFirstName = function(firstName) {
    var pData = $(this);
    pData.firstName = firstName;
    return this;
  };
  PersonPrototype.setLastName = function(lastName) {
    var pData = $(this);
    pData.lastName = lastName;
    return this;
  };
})({}, this);

var chris = new Person('Chris', 'West');
alert(chris.setFirstName('Christopher').setLastName('Webber').getName());

Questo esempio proviene dal mio post su Funzioni prototipali e dati privati ed è spiegato in modo più dettagliato lì.


1
Questa risposta è troppo "intelligente" per essere utile, ma mi piace la risposta dell'uso di una variabile legata a IFFE come stretta di mano segreta. Questa implementazione utilizza troppe chiusure per essere utile; il punto di avere metodi definiti prototipo è impedire la costruzione di nuovi oggetti funzione per ciascun metodo su ciascun oggetto.
greg.kindel,

Questo approccio utilizza una chiave segreta per identificare quali metodi prototipo sono affidabili e quali no. Tuttavia, è l'istanza che convalida la chiave, quindi la chiave deve essere inviata all'istanza. Ma poi, il codice non attendibile potrebbe chiamare un metodo attendibile su un'istanza falsa, che ruberebbe la chiave. E con quella chiave, crea nuovi metodi che sarebbero considerati attendibili da istanze reali. Quindi questa è solo privacy per oscurità.
Oriol,

4

Nell'attuale JavaScript, sono abbastanza certo che esiste uno e un solo modo per avere uno stato privato , accessibile dalle funzioni del prototipo , senza aggiungere nulla di pubblico a this. La risposta è usare il modello "mappa debole".

Per riassumere: la Personclasse ha una singola mappa debole, in cui le chiavi sono le istanze di Person e i valori sono oggetti semplici usati per l'archiviazione privata.

Ecco un esempio completamente funzionale: (gioca su http://jsfiddle.net/ScottRippey/BLNVr/ )

var Person = (function() {
    var _ = weakMap();
    // Now, _(this) returns an object, used for private storage.
    var Person = function(first, last) {
        // Assign private storage:
        _(this).firstName = first;
        _(this).lastName = last;
    }
    Person.prototype = {
        fullName: function() {
            // Retrieve private storage:
            return _(this).firstName + _(this).lastName;
        },
        firstName: function() {
            return _(this).firstName;
        },
        destroy: function() {
            // Free up the private storage:
            _(this, true);
        }
    };
    return Person;
})();

function weakMap() {
    var instances=[], values=[];
    return function(instance, destroy) {
        var index = instances.indexOf(instance);
        if (destroy) {
            // Delete the private state:
            instances.splice(index, 1);
            return values.splice(index, 1)[0];
        } else if (index === -1) {
            // Create the private state:
            instances.push(instance);
            values.push({});
            return values[values.length - 1];
        } else {
            // Return the private state:
            return values[index];
        }
    };
}

Come ho detto, questo è davvero l'unico modo per ottenere tutte e 3 le parti.

Ci sono due avvertimenti, tuttavia. Innanzitutto, ciò costa prestazioni: ogni volta che si accede ai dati privati, si tratta di O(n)un'operazione, in cui si ntrova il numero di istanze. Quindi non vorrai farlo se hai un gran numero di istanze. Secondo, quando hai finito con un'istanza, devi chiamare destroy; in caso contrario, l'istanza e i dati non verranno raccolti in modo inutile e si verificherà una perdita di memoria.

Ed è per questo che la mia risposta originale, "Non dovresti" , è qualcosa a cui mi piacerebbe attenermi.


Se non distruggi esplicitamente un'istanza di Person prima che esca dall'ambito di applicazione, la mappa debole non mantiene un riferimento ad essa in modo da avere una perdita di memoria? Ho escogitato un modello per proteggere come altre istanze di Person possono accedere alla variabile e quelle ereditarie da Person possono. È appena stato risolto, quindi non sono sicuro che ci siano altri svantaggi oltre all'elaborazione extra (non sembra tanto quanto l'accesso ai privati) stackoverflow.com/a/21800194/1641941 Restituire un oggetto privato / protetto è una seccatura poiché chiamare il codice può quindi mutare il tuo privato / protetto.
HMR,

2
@HMR Sì, devi distruggere esplicitamente i dati privati. Aggiungerò questo avvertimento alla mia risposta.
Scott Rippey,

3

C'è un modo più semplice sfruttando l'uso di binde callmetodi.

Impostando le variabili private su un oggetto, è possibile sfruttare l'ambito di tale oggetto.

Esempio

function TestClass (value) {
    // The private value(s)
    var _private = {
        value: value
    };

    // `bind` creates a copy of `getValue` when the object is instantiated
    this.getValue = TestClass.prototype.getValue.bind(_private);

    // Use `call` in another function if the prototype method will possibly change
    this.getValueDynamic = function() {
        return TestClass.prototype.getValue.call(_private);
    };
};

TestClass.prototype.getValue = function() {
    return this.value;
};

Questo metodo non è privo di inconvenienti. Poiché il contesto dell'ambito viene effettivamente ignorato, non si ha accesso al di fuori _privatedell'oggetto. Tuttavia, non è impossibile dare comunque accesso all'ambito dell'oggetto dell'istanza. Puoi passare nel contesto dell'oggetto ( this) come secondo argomento bindo callavere ancora accesso ai suoi valori pubblici nella funzione prototipo.

Accesso a valori pubblici

function TestClass (value) {
    var _private = {
        value: value
    };

    this.message = "Hello, ";

    this.getMessage = TestClass.prototype.getMessage.bind(_private, this);

}

TestClass.prototype.getMessage = function(_public) {

    // Can still access passed in arguments
    // e.g. – test.getValues('foo'), 'foo' is the 2nd argument to the method
    console.log([].slice.call(arguments, 1));
    return _public.message + this.value;
};

var test = new TestClass("World");
test.getMessage(1, 2, 3); // [1, 2, 3]         (console.log)
                          // => "Hello, World" (return value)

test.message = "Greetings, ";
test.getMessage(); // []                    (console.log)
                   // => "Greetings, World" (return value)

2
Perché qualcuno dovrebbe creare una copia del metodo prototipo invece di creare un metodo istanziato in primo luogo?
schiaccia il

3

Provalo!

    function Potatoe(size) {
    var _image = new Image();
    _image.src = 'potatoe_'+size+'.png';
    function getImage() {
        if (getImage.caller == null || getImage.caller.owner != Potatoe.prototype)
            throw new Error('This is a private property.');
        return _image;
    }
    Object.defineProperty(this,'image',{
        configurable: false,
        enumerable: false,
        get : getImage          
    });
    Object.defineProperty(this,'size',{
        writable: false,
        configurable: false,
        enumerable: true,
        value : size            
    });
}
Potatoe.prototype.draw = function(ctx,x,y) {
    //ctx.drawImage(this.image,x,y);
    console.log(this.image);
}
Potatoe.prototype.draw.owner = Potatoe.prototype;

var pot = new Potatoe(32);
console.log('Potatoe size: '+pot.size);
try {
    console.log('Potatoe image: '+pot.image);
} catch(e) {
    console.log('Oops: '+e);
}
pot.draw();

1
Questo si basa su caller, che è un'estensione dipendente dall'implementazione non consentita in modalità rigorosa.
Oriol,

1

Ecco cosa mi è venuto in mente.

(function () {
    var staticVar = 0;
    var yrObj = function () {
        var private = {"a":1,"b":2};
        var MyObj = function () {
            private.a += staticVar;
            staticVar++;
        };
        MyObj.prototype = {
            "test" : function () {
                console.log(private.a);
            }
        };

        return new MyObj;
    };
    window.YrObj = yrObj;
}());

var obj1 = new YrObj;
var obj2 = new YrObj;
obj1.test(); // 1
obj2.test(); // 2

il problema principale con questa implementazione è che ridefinisce i prototipi su ogni istanciazione.


Interessante, mi piace molto il tentativo e pensavo alla stessa cosa, ma hai ragione nel ridefinire la funzione prototipo ad ogni istanza è un limite piuttosto grande. Questo non è solo perché sono sprecati cicli della CPU, ma perché se dovessi cambiare il prototipo in seguito, verrebbe "ripristinato" al suo stato originale come definito nel costruttore alla prossima istanza: /
Niko Bellic

1
Questo non solo ridefinisce i prototipi, ma definisce un nuovo costruttore per ogni istanza. Quindi le "istanze" non sono più istanze della stessa classe.
Oriol,

1

C'è un modo molto semplice per farlo

function SharedPrivate(){
  var private = "secret";
  this.constructor.prototype.getP = function(){return private}
  this.constructor.prototype.setP = function(v){ private = v;}
}

var o1 = new SharedPrivate();
var o2 = new SharedPrivate();

console.log(o1.getP()); // secret
console.log(o2.getP()); // secret
o1.setP("Pentax Full Frame K1 is on sale..!");
console.log(o1.getP()); // Pentax Full Frame K1 is on sale..!
console.log(o2.getP()); // Pentax Full Frame K1 is on sale..!
o2.setP("And it's only for $1,795._");
console.log(o1.getP()); // And it's only for $1,795._

I prototipi di JavaScript sono d'oro.


2
Credo che sia meglio non usare il prototipo nella funzione di costruzione in quanto creerà una nuova funzione ogni volta che viene creata una nuova istanza.
whamsicore,

@whamsicore Sì vero, ma in questo caso è essenziale poiché per ogni singolo oggetto istanziato dobbiamo organizzare una chiusura condivisa. Questo è il motivo per cui le definizioni delle funzioni risiedono all'interno del costruttore e dobbiamo fare riferimento a SharedPrivate.prototypecome this.constructor.prototypeNon è un grosso problema ridefinire getP e setP più volte ...
Redu,

1

Sono in ritardo alla festa, ma penso di poter contribuire. Qui, controlla questo:

// 1. Create closure
var SomeClass = function() {
  // 2. Create `key` inside a closure
  var key = {};
  // Function to create private storage
  var private = function() {
    var obj = {};
    // return Function to access private storage using `key`
    return function(testkey) {
      if(key === testkey) return obj;
      // If `key` is wrong, then storage cannot be accessed
      console.error('Cannot access private properties');
      return undefined;
    };
  };
  var SomeClass = function() {
    // 3. Create private storage
    this._ = private();
    // 4. Access private storage using the `key`
    this._(key).priv_prop = 200;
  };
  SomeClass.prototype.test = function() {
    console.log(this._(key).priv_prop); // Using property from prototype
  };
  return SomeClass;
}();

// Can access private property from within prototype
var instance = new SomeClass();
instance.test(); // `200` logged

// Cannot access private property from outside of the closure
var wrong_key = {};
instance._(wrong_key); // undefined; error logged

Chiamo questo metodo accessor pattern . L'idea essenziale è che abbiamo una chiusura , una chiave all'interno della chiusura e creiamo un oggetto privato (nel costruttore) a cui è possibile accedere solo se si dispone della chiave .

Se sei interessato, puoi leggere di più al riguardo nel mio articolo . Utilizzando questo metodo, è possibile creare proprietà per oggetto a cui non è possibile accedere al di fuori della chiusura. Pertanto, puoi usarli nel costruttore o nel prototipo, ma non altrove. Non ho visto questo metodo usato da nessuna parte, ma penso che sia davvero potente.


0

Non riesci a mettere le variabili in un ambito superiore?

(function () {
    var privateVariable = true;

    var MyClass = function () {
        if (privateVariable) console.log('readable from private scope!');
    };

    MyClass.prototype.publicMethod = function () {
        if (privateVariable) console.log('readable from public scope!');
    };
}))();

4
Quindi le variabili vengono condivise tra tutte le istanze di MyClass.
schiaccia il

0

Puoi anche provare ad aggiungere un metodo non direttamente sul prototipo, ma su una funzione di costruzione come questa:

var MyArray = function() {
    var array = [];

    this.add = MyArray.add.bind(null, array);
    this.getAll = MyArray.getAll.bind(null, array);
}

MyArray.add = function(array, item) {
    array.push(item);
}
MyArray.getAll = function(array) {
    return array;
}

var myArray1 = new MyArray();
myArray1.add("some item 1");
console.log(myArray1.getAll()); // ['some item 1']
var myArray2 = new MyArray();
myArray2.add("some item 2");
console.log(myArray2.getAll()); // ['some item 2']
console.log(myArray1.getAll()); // ['some item 2'] - FINE!

0

Ecco qualcosa che ho escogitato mentre cercavo di trovare la soluzione più semplice per questo problema, forse potrebbe essere utile a qualcuno. Sono nuovo di JavaScript, quindi potrebbero esserci dei problemi con il codice.

// pseudo-class definition scope
(function () {

    // this is used to identify 'friend' functions defined within this scope,
    // while not being able to forge valid parameter for GetContext() 
    // to gain 'private' access from outside
    var _scope = new (function () { })();
    // -----------------------------------------------------------------

    // pseudo-class definition
    this.Something = function (x) {

        // 'private' members are wrapped into context object,
        // it can be also created with a function
        var _ctx = Object.seal({

            // actual private members
            Name: null,
            Number: null,

            Somefunc: function () {
                console.log('Something(' + this.Name + ').Somefunc(): number = ' + this.Number);
            }
        });
        // -----------------------------------------------------------------

        // function below needs to be defined in every class
        // to allow limited access from prototype
        this.GetContext = function (scope) {

            if (scope !== _scope) throw 'access';
            return _ctx;
        }
        // -----------------------------------------------------------------

        {
            // initialization code, if any
            _ctx.Name = (x !== 'undefined') ? x : 'default';
            _ctx.Number = 0;

            Object.freeze(this);
        }
    }
    // -----------------------------------------------------------------

    // prototype is defined only once
    this.Something.prototype = Object.freeze({

        // public accessors for 'private' field
        get Number() { return this.GetContext(_scope).Number; },
        set Number(v) { this.GetContext(_scope).Number = v; },

        // public function making use of some private fields
        Test: function () {

            var _ctx = this.GetContext(_scope);
            // access 'private' field
            console.log('Something(' + _ctx.Name + ').Test(): ' + _ctx.Number);
            // call 'private' func
            _ctx.Somefunc();
        }
    });
    // -----------------------------------------------------------------

    // wrap is used to hide _scope value and group definitions
}).call(this);

function _A(cond) { if (cond !== true) throw new Error('assert failed'); }
// -----------------------------------------------------------------

function test_smth() {

    console.clear();

    var smth1 = new Something('first'),
      smth2 = new Something('second');

    //_A(false);
    _A(smth1.Test === smth2.Test);

    smth1.Number = 3;
    smth2.Number = 5;
    console.log('smth1.Number: ' + smth1.Number + ', smth2.Number: ' + smth2.Number);

    smth1.Number = 2;
    smth2.Number = 6;

    smth1.Test();
    smth2.Test();

    try {
        var ctx = smth1.GetContext();
    } catch (err) {
        console.log('error: ' + err);
    }
}

test_smth();

0

Ho affrontato esattamente la stessa domanda oggi e dopo aver elaborato la risposta di prima classe di Scott Rippey, mi è venuta in mente una soluzione molto semplice (IMHO) che è sia compatibile con ES5 che efficiente, è anche sicuro come lo scontro dei nomi (l'uso di _private non sembra sicuro) .

/*jslint white: true, plusplus: true */

 /*global console */

var a, TestClass = (function(){
    "use strict";
    function PrefixedCounter (prefix) {
        var counter = 0;
        this.count = function () {
            return prefix + (++counter);
        };
    }
    var TestClass = (function(){
        var cls, pc = new PrefixedCounter("_TestClass_priv_")
        , privateField = pc.count()
        ;
        cls = function(){
            this[privateField] = "hello";
            this.nonProtoHello = function(){
                console.log(this[privateField]);
            };
        };
        cls.prototype.prototypeHello = function(){
            console.log(this[privateField]);
        };
        return cls;
    }());
    return TestClass;
}());

a = new TestClass();
a.nonProtoHello();
a.prototypeHello();

Testato con ringojs e nodejs. Sono ansioso di leggere la tua opinione.


Ecco un riferimento: controlla la sezione "Un passo avanti". philipwalton.com/articles/…
jimasun,

0
var getParams = function(_func) {
  res = _func.toString().split('function (')[1].split(')')[0].split(',')
  return res
}

function TestClass(){

  var private = {hidden: 'secret'}
  //clever magic accessor thing goes here
  if ( !(this instanceof arguments.callee) ) {
    for (var key in arguments) {
      if (typeof arguments[key] == 'function') {
        var keys = getParams(arguments[key])
        var params = []
        for (var i = 0; i <= keys.length; i++) {
          if (private[keys[i]] != undefined) {
            params.push(private[keys[i]])
          }
        }
        arguments[key].apply(null,params)
      }
    }
  }
}


TestClass.prototype.test = function(){
  var _hidden; //variable I want to get
  TestClass(function(hidden) {_hidden = hidden}) //invoke magic to get
};

new TestClass().test()

Com'è questo? Utilizzando un accessor privato. Permette solo di ottenere le variabili anche se non di impostarle, dipende dal caso d'uso.


Questo non risponde alla domanda in modo utile. perché credi che questa sia la risposta? come funziona Semplicemente dire a qualcuno di cambiare il proprio codice senza alcun contesto o significato non li aiuta a capire cosa hanno fatto di sbagliato.
GrumpyCrouton,

Voleva un modo per accedere alle variabili private nascoste di una classe tramite prototipi senza dover creare quella variabile nascosta su ogni singola istanza della classe. Il codice sopra è un metodo di esempio per fare ciò. In che modo non è una risposta alla domanda?
dylan0150,

Non ho detto che non era una risposta alla domanda. Ho detto che non era una risposta utile , perché non aiuta nessuno a imparare. Dovresti spiegare il tuo codice, perché funziona, perché è il modo giusto di farlo. Se fossi l'autore della domanda, non accetterei la tua risposta perché non incoraggia l'apprendimento, non mi insegna cosa sto facendo di sbagliato o cosa sta facendo il codice dato o come funziona.
GrumpyCrouton,

0

Ho una soluzione, ma non sono sicuro che sia privo di difetti.

Perché funzioni, devi usare la seguente struttura:

  1. Utilizzare 1 oggetto privato che contiene tutte le variabili private.
  2. Utilizzare la funzione 1 istanza.
  3. Applicare una chiusura al costruttore e tutte le funzioni del prototipo.
  4. Qualsiasi istanza creata viene eseguita al di fuori della chiusura definita.

Ecco il codice:

var TestClass = 
(function () {
    // difficult to be guessed.
    var hash = Math.round(Math.random() * Math.pow(10, 13) + + new Date());
    var TestClass = function () {
        var privateFields = {
            field1: 1,
            field2: 2
        };
        this.getPrivateFields = function (hashed) {
            if(hashed !== hash) {
                throw "Cannot access private fields outside of object.";
                // or return null;
            }
            return privateFields;
        };
    };

    TestClass.prototype.prototypeHello = function () {
        var privateFields = this.getPrivateFields(hash);
        privateFields.field1 = Math.round(Math.random() * 100);
        privateFields.field2 = Math.round(Math.random() * 100);
    };

    TestClass.prototype.logField1 = function () {
        var privateFields = this.getPrivateFields(hash);
        console.log(privateFields.field1);
    };

    TestClass.prototype.logField2 = function () {
        var privateFields = this.getPrivateFields(hash);
        console.log(privateFields.field2);
    };

    return TestClass;
})();

Il modo in cui funziona è che fornisce una funzione di istanza "this.getPrivateFields" per accedere all'oggetto delle variabili private "privateFields", ma questa funzione restituirà solo l'oggetto "privateFields" all'interno della chiusura principale definita (anche funzioni prototipo che utilizzano "this.getPrivateFields "devono essere definiti all'interno di questa chiusura).

Un hash prodotto durante il runtime e difficile da indovinare viene utilizzato come parametro per assicurarsi che anche se "getPrivateFields" viene chiamato al di fuori dell'ambito di chiusura non restituirà l'oggetto "privateFields".

Lo svantaggio è che non possiamo estendere TestClass con più funzioni prototipo al di fuori della chiusura.

Ecco un po 'di codice di prova:

var t1 = new TestClass();
console.log('Initial t1 field1 is: ');
t1.logField1();
console.log('Initial t1 field2 is: ');
t1.logField2();
t1.prototypeHello();
console.log('t1 field1 is now: ');
t1.logField1();
console.log('t1 field2 is now: ');
t1.logField2();
var t2 = new TestClass();
console.log('Initial t2 field1 is: ');
t2.logField1();
console.log('Initial t2 field2 is: ');
t2.logField2();
t2.prototypeHello();
console.log('t2 field1 is now: ');
t2.logField1();
console.log('t2 field2 is now: ');
t2.logField2();

console.log('t1 field1 stays: ');
t1.logField1();
console.log('t1 field2 stays: ');
t1.logField2();

t1.getPrivateFields(11233);

EDIT: utilizzando questo metodo, è anche possibile "definire" funzioni private.

TestClass.prototype.privateFunction = function (hashed) {
    if(hashed !== hash) {
        throw "Cannot access private function.";
    }
};

TestClass.prototype.prototypeHello = function () {
    this.privateFunction(hash);
};

0

Stavo giocando con questo oggi e questa è stata l'unica soluzione che ho trovato senza usare Symbols. La cosa migliore di questo è che in realtà può essere completamente privato.

La soluzione si basa su un caricatore di moduli nostrano che in pratica diventa il mediatore per una cache di archiviazione privata (usando una mappa debole).

   const loader = (function() {
        function ModuleLoader() {}

    //Static, accessible only if truly needed through obj.constructor.modules
    //Can also be made completely private by removing the ModuleLoader prefix.
    ModuleLoader.modulesLoaded = 0;
    ModuleLoader.modules = {}

    ModuleLoader.prototype.define = function(moduleName, dModule) {
        if (moduleName in ModuleLoader.modules) throw new Error('Error, duplicate module');

        const module = ModuleLoader.modules[moduleName] = {}

        module.context = {
            __moduleName: moduleName,
            exports: {}
        }

        //Weak map with instance as the key, when the created instance is garbage collected or goes out of scope this will be cleaned up.
        module._private = {
            private_sections: new WeakMap(),
            instances: []
        };

        function private(action, instance) {
            switch (action) {
                case "create":
                    if (module._private.private_sections.has(instance)) throw new Error('Cannot create private store twice on the same instance! check calls to create.')
                    module._private.instances.push(instance);
                    module._private.private_sections.set(instance, {});
                    break;
                case "delete":
                    const index = module._private.instances.indexOf(instance);
                    if (index == -1) throw new Error('Invalid state');
                    module._private.instances.slice(index, 1);
                    return module._private.private_sections.delete(instance);
                    break;
                case "get":
                    return module._private.private_sections.get(instance);
                    break;
                default:
                    throw new Error('Invalid action');
                    break;
            }
        }

        dModule.call(module.context, private);
        ModuleLoader.modulesLoaded++;
    }

    ModuleLoader.prototype.remove = function(moduleName) {
        if (!moduleName in (ModuleLoader.modules)) return;

        /*
            Clean up as best we can.
        */
        const module = ModuleLoader.modules[moduleName];
        module.context.__moduleName = null;
        module.context.exports = null;
        module.cotext = null;
        module._private.instances.forEach(function(instance) { module._private.private_sections.delete(instance) });
        for (let i = 0; i < module._private.instances.length; i++) {
            module._private.instances[i] = undefined;
        }
        module._private.instances = undefined;
        module._private = null;
        delete ModuleLoader.modules[moduleName];
        ModuleLoader.modulesLoaded -= 1;
    }


    ModuleLoader.prototype.require = function(moduleName) {
        if (!(moduleName in ModuleLoader.modules)) throw new Error('Module does not exist');

        return ModuleLoader.modules[moduleName].context.exports;
    }



     return new ModuleLoader();
    })();

    loader.define('MyModule', function(private_store) {
        function MyClass() {
            //Creates the private storage facility. Called once in constructor.
            private_store("create", this);


            //Retrieve the private storage object from the storage facility.
            private_store("get", this).no = 1;
        }

        MyClass.prototype.incrementPrivateVar = function() {
            private_store("get", this).no += 1;
        }

        MyClass.prototype.getPrivateVar = function() {
            return private_store("get", this).no;
        }

        this.exports = MyClass;
    })

    //Get whatever is exported from MyModule
    const MyClass = loader.require('MyModule');

    //Create a new instance of `MyClass`
    const myClass = new MyClass();

    //Create another instance of `MyClass`
    const myClass2 = new MyClass();

    //print out current private vars
    console.log('pVar = ' + myClass.getPrivateVar())
    console.log('pVar2 = ' + myClass2.getPrivateVar())

    //Increment it
    myClass.incrementPrivateVar()

    //Print out to see if one affected the other or shared
    console.log('pVar after increment = ' + myClass.getPrivateVar())
    console.log('pVar after increment on other class = ' + myClass2.getPrivateVar())

    //Clean up.
    loader.remove('MyModule')

0

So che è passato più di 1 decennio da quando è stato chiesto, ma ho appena riflettuto su questo per l'ennesima volta nella mia vita da programmatore, e ho trovato una possibile soluzione che non so se mi piace ancora del tutto . Non ho mai visto questa metodologia documentata prima, quindi la chiamerò "modello dollaro privato / pubblico" o modello $ / $ .

var ownFunctionResult = this.$("functionName"[, arg1[, arg2 ...]]);
var ownFieldValue = this._$("fieldName"[, newValue]);

var objectFunctionResult = objectX.$("functionName"[, arg1[, arg2 ...]]);

//Throws an exception. objectX._$ is not defined
var objectFieldValue = objectX._$("fieldName"[, newValue]);

Il concetto utilizza una funzione ClassDefinition che restituisce una funzione di costruzione che restituisce un oggetto Interface . L'unico metodo dell'interfaccia è quello di $ricevere un nameargomento per invocare la funzione corrispondente nell'oggetto costruttore, eventuali argomenti aggiuntivi passati doponame vengono passati .

La funzione helper definita a livello globale ClassValuesmemorizza tutti i campi in un oggetto secondo necessità. Definisce la _$funzione per accedervi name. Questo segue un breve modello get / set, quindi se valueviene passato, verrà utilizzato come nuovo valore della variabile.

var ClassValues = function (values) {
  return {
    _$: function _$(name, value) {
      if (arguments.length > 1) {
        values[name] = value;
      }

      return values[name];
    }
  };
};

La funzione definita globalmente Interfaceaccetta un oggetto e un Valuesoggetto per restituire un oggetto _interfacecon una singola funzione $che esamina objper trovare una funzione denominata dopo il parametro namee lo richiama valuescome oggetto con ambito . Gli argomenti aggiuntivi passati $verranno passati all'invocazione della funzione.

var Interface = function (obj, values, className) {
  var _interface = {
    $: function $(name) {
      if (typeof(obj[name]) === "function") {
        return obj[name].apply(values, Array.prototype.splice.call(arguments, 1));
      }

      throw className + "." + name + " is not a function.";
    }
  };

  //Give values access to the interface.
  values.$ = _interface.$;

  return _interface;
};

Nell'esempio seguente, ClassXè assegnato al risultato di ClassDefinition, che è la Constructorfunzione. Constructorpuò ricevere un numero qualsiasi di argomenti. Interfaceè ciò che ottiene il codice esterno dopo aver chiamato il costruttore.

var ClassX = (function ClassDefinition () {
  var Constructor = function Constructor (valA) {
    return Interface(this, ClassValues({ valA: valA }), "ClassX");
  };

  Constructor.prototype.getValA = function getValA() {
    //private value access pattern to get current value.
    return this._$("valA");
  };

  Constructor.prototype.setValA = function setValA(valA) {
    //private value access pattern to set new value.
    this._$("valA", valA);
  };

  Constructor.prototype.isValAValid = function isValAValid(validMessage, invalidMessage) {
    //interface access pattern to call object function.
    var valA = this.$("getValA");

    //timesAccessed was not defined in constructor but can be added later...
    var timesAccessed = this._$("timesAccessed");

    if (timesAccessed) {
      timesAccessed = timesAccessed + 1;
    } else {
      timesAccessed = 1;
    }

    this._$("timesAccessed", timesAccessed);

    if (valA) {
      return "valA is " + validMessage + ".";
    }

    return "valA is " + invalidMessage + ".";
  };

  return Constructor;
}());

Non ha senso avere funzioni non prototipate Constructor, sebbene sia possibile definirle nel corpo della funzione di costruzione. Tutte le funzioni sono chiamate con il modello di dollaro pubblico this.$("functionName"[, param1[, param2 ...]]) . È possibile accedere ai valori privati ​​con il modello del dollaro privato this._$("valueName"[, replacingValue]); . Poiché Interfacenon ha una definizione per _$, i valori non sono accessibili da oggetti esterni. Poiché ogni corpo di funzione prototipato thisè impostato valuessull'oggetto in funzione $, si otterranno eccezioni se si chiamano direttamente funzioni di fratelli del costruttore; il modello _ $ / $ deve essere seguito anche nei corpi funzione prototipati. Sotto l'utilizzo del campione.

var classX1 = new ClassX();
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
console.log("classX1.valA: " + classX1.$("getValA"));
classX1.$("setValA", "v1");
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
var classX2 = new ClassX("v2");
console.log("classX1.valA: " + classX1.$("getValA"));
console.log("classX2.valA: " + classX2.$("getValA"));
//This will throw an exception
//classX1._$("valA");

E l'output della console.

classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2

Il modello _ $ / $ consente la totale privacy dei valori nelle classi completamente prototipate. Non so se lo userò mai, né se ha dei difetti, ma hey, è stato un bel puzzle!


0

ES6 WeakMaps

Utilizzando un modello semplice basato su ES6 WeakMaps è possibile ottenere variabili membro private, raggiungibili dalle funzioni del prototipo .

Nota: l'uso di WeakMaps garantisce sicurezza contro le perdite di memoria , consentendo a Garbage Collector di identificare e scartare le istanze inutilizzate.

// Create a private scope using an Immediately 
// Invoked Function Expression...
let Person = (function() {

    // Create the WeakMap that will hold each  
    // Instance collection's of private data
    let privateData = new WeakMap();
    
    // Declare the Constructor :
    function Person(name) {
        // Insert the private data in the WeakMap,
        // using 'this' as a unique acces Key
        privateData.set(this, { name: name });
    }
    
    // Declare a prototype method 
    Person.prototype.getName = function() {
        // Because 'privateData' is in the same 
        // scope, it's contents can be retrieved...
        // by using  again 'this' , as  the acces key 
        return privateData.get(this).name;
    };

    // return the Constructor
    return Person;
}());

Una spiegazione più dettagliata di questo modello può essere trovata qui


-1

Devi modificare 3 elementi nel tuo codice:

  1. Sostituisci var privateField = "hello"con this.privateField = "hello".
  2. Nel prototipo sostituire privateFieldcon this.privateField.
  3. Nel non prototipo sostituire anche privateFieldcon this.privateField.

Il codice finale sarebbe il seguente:

TestClass = function(){
    this.privateField = "hello";
    this.nonProtoHello = function(){alert(this.privateField)};
}

TestClass.prototype.prototypeHello = function(){alert(this.privateField)};

var t = new TestClass();

t.prototypeHello()

this.privateFieldnon sarebbe un campo privato. è accessibile dall'esterno:t.privateField
V. Rubinetti,

-2

È possibile utilizzare un'assegnazione prototipo nella definizione del costruttore.

La variabile sarà visibile al metodo aggiunto prototipo ma tutte le istanze delle funzioni accederanno alla stessa variabile CONDIVISA.

function A()
{
  var sharedVar = 0;
  this.local = "";

  A.prototype.increment = function(lval)
  {    
    if (lval) this.local = lval;    
    alert((++sharedVar) + " while this.p is still " + this.local);
  }
}

var a = new A();
var b = new A();    
a.increment("I belong to a");
b.increment("I belong to b");
a.increment();
b.increment();

Spero che questo possa essere utile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.