564 Stimmen

Private Eigenschaften in JavaScript ES6 Klassen

Ist es möglich, private Eigenschaften in ES6-Klassen zu erstellen?

Hier ist ein Beispiel. Wie kann ich den Zugriff auf instance.property verhindern?

class Something {
  constructor(){
    this.property = "test";
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"

5voto

NanoWizard Punkte 1949

Ich denke, Bens Antwort ist wahrscheinlich die beste für die meisten Fälle, bis die Sprache nativ explizit private Variablen unterstützt.

Wenn Sie jedoch aus irgendeinem Grund den Zugriff mit Object.getOwnPropertySymbols() verhindern müssen, ist eine Methode, die ich in Betracht gezogen habe, das Anhängen einer eindeutigen, nicht konfigurierbaren, nicht aufzählbaren, nicht beschreibbaren Eigenschaft, die als Eigenschaftskennung für jedes Objekt beim Erstellen verwendet werden kann (wie z.B. ein eindeutiges Symbol, wenn Sie nicht bereits eine andere eindeutige Eigenschaft wie eine id haben). Dann führen Sie einfach eine Zuordnung der "privaten" Variablen jedes Objekts unter Verwendung dieser Kennung durch.

const privateVars = {};

class Something {
    constructor(){
        Object.defineProperty(this, '_sym', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: Symbol()
        });

        var myPrivateVars = {
            privateProperty: "Ich bin versteckt"
        };

        privateVars[this._sym] = myPrivateVars;

        this.property = "Ich bin öffentlich";
    }

    getPrivateProperty() {
        return privateVars[this._sym].privateProperty;
    }

    // Eine Aufräummethode ist erforderlich, da die Variablen nicht automatisch aus dem Speicher gelöscht werden,
    // wenn das Objekt vom Garbage Collector aufgeräumt wird
    destroy() {
        delete privateVars[this._sym];
    }
}

var instance = new Something();
console.log(instance.property); //=> "I'm public"
console.log(instance.privateProperty); //=> undefined
console.log(instance.getPrivateProperty()); //=> "I'm hidden"

Der potenzielle Vorteil dieses Ansatzes gegenüber der Verwendung eines WeakMap ist schnellere Zugriffszeit, wenn die Leistung ein Anliegen wird.

5voto

Robin F. Punkte 987

Persönlich finde ich den Vorschlag des Bind-Operators :: gut und würde ihn dann mit der Lösung kombinieren, die @d13 erwähnt hat, aber vorerst bei der Antwort von @d13 bleiben, wo du das export-Schlüsselwort für deine Klasse verwendest und die privaten Funktionen im Modul platzierst.

Es gibt jedoch noch eine weitere Lösung, die hier nicht erwähnt wurde, die einen funktionaleren Ansatz verfolgt und es ermöglichen würde, alle privaten Eigenschaften/Methoden innerhalb der Klasse zu haben.

Private.js

export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }

Test.js

import { get, set } from './utils/Private'
export default class Test {
  constructor(initialState = {}) {
    const _set = this.set = set(initialState);
    const _get = this.get = get(initialState);

    this.set('privateMethod', () => _get('propValue'));
  }

  showProp() {
    return this.get('privateMethod')();
  }
}

let one = new Test({ propValue: 5});
let two = new Test({ propValue: 8});
two.showProp(); // 8
one.showProp(); // 5

Kommentare dazu wären willkommen.

4voto

JSInitiate Punkte 41

Ich glaube, es ist möglich, mit Closures innerhalb von Konstruktoren das 'Beste aus beiden Welten' zu bekommen. Es gibt zwei Varianten:

Alle Datenelemente sind privat

function myFunc() {
   console.log('Wert von x: ' + this.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   console.log('Verbesserter Wert von x: ' + (this.x + 1));
}

class Test {
   constructor() {

      let internal = {
         x : 2,
      };

      internal.myPrivateFunc = myPrivateFunc.bind(internal);

      this.myFunc = myFunc.bind(internal);
   }
};

Einige Elemente sind privat

HINWEIS: Das ist zugegebenermaßen hässlich. Wenn Sie eine bessere Lösung kennen, bearbeiten Sie bitte diese Antwort.

function myFunc(priv, pub) {
   pub.y = 3; // Das Test-Objekt erhält nun ein Element 'y' mit Wert 3.
   console.log('Wert von x: ' + priv.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   pub.z = 5; // Das Test-Objekt erhält nun ein Element 'z' mit Wert 3.
   console.log('Verbesserter Wert von x: ' + (priv.x + 1));
}

class Test {
   constructor() {

      let self = this;

      let internal = {
         x : 2,
      };

      internal.myPrivateFunc = myPrivateFunc.bind(null, internal, self);

      this.myFunc = myFunc.bind(null, internal, self);
   }
};

4voto

Francisco Neto Punkte 349

Es ist tatsächlich möglich, Symbole und Proxies zu verwenden. Sie verwenden die Symbole im Klassenscope und setzen zwei Fallen in einem Proxy: eine für das Klassenprototype, so dass das Reflect.ownKeys(instance) oder Object.getOwnPropertySymbols Ihre Symbole nicht verrät, die andere ist für den Konstruktor selbst, so dass wenn new ClassName(attrs) aufgerufen wird, wird die zurückgegebene Instanz abgefangen und die eigenen Eigenschaften-Symbole blockiert. Hier ist der Code:

const Human = (function() {
  const pet = Symbol();
  const greet = Symbol();

  const Human = privatizeSymbolsInFn(function(name) {
    this.name = name; // öffentlich
    this[pet] = 'dog'; // privat
  });

  Human.prototype = privatizeSymbolsInObj({
    [greet]() { // privat
      return 'Hallo!';
    },
    revealSecrets() {
      console.log(this[greet]() + ` Das Haustier ist ein ${this[pet]}`);
    }
  });

  return Human;
})();

const bob = new Human('Bob');

console.assert(bob instanceof Human);
console.assert(Reflect.ownKeys(bob).length === 1) // nur ['name']
console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // nur ['revealSecrets']

// Einrichten der Fallen innerhalb der Proxies:
function privatizeSymbolsInObj(target) { 
  return new Proxy(target, { ownKeys: Object.getOwnPropertyNames });
}

function privatizeSymbolsInFn(Class) {
  function construct(TargetClass, argsList) {
    const instance = new TargetClass(...argsList);
    return privatizeSymbolsInObj(instance);
  }
  return new Proxy(Class, { construct });
}

Reflect.ownKeys() funktioniert wie folgt: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj)) deshalb benötigen wir eine Falle für diese Objekte.

4voto

Michael Franzl Punkte 1244

Auch Typescript kann es nicht machen. Aus ihrer Dokumentation:

Wenn ein Member als privat markiert ist, kann er nicht von außerhalb seiner enthaltenden Klasse zugegriffen werden. Zum Beispiel:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // Fehler: 'name' ist privat;

Aber transpiliert auf ihrem Spielplatz gibt dies:

var Animal = (function () {
    function Animal(theName) {
        this.name = theName;
    }
    return Animal;
}());
console.log(new Animal("Cat").name);

Also ist ihr "private" Schlüsselwort unwirksam.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X