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"

9voto

efidiles Punkte 191

Vervollständigung von @d13 und den Kommentaren von @johnny-oshika und @DanyalAytekin:

Ich glaube, in dem von @johnny-oshika bereitgestellten Beispiel könnten wir normale Funktionen anstelle von Pfeilfunktionen verwenden und sie dann mit dem aktuellen Objekt plus einem _privates-Objekt als teilweise Parameter binden:

something.js

function _greet(_privates) {
  return 'Hallo ' + _privates.message;
}

function _updateMessage(_privates, newMessage) {
  _privates.message = newMessage;
}

export default class Something {
  constructor(message) {
    const _privates = {
      message
    };

    this.say = _greet.bind(this, _privates);
    this.updateMessage = _updateMessage.bind(this, _privates);
  }
}

main.js

import Something from './something.js';

const something = new Something('Sonniger Tag!');

const message1 = something.say();
something.updateMessage('Bewölkter Tag!');
const message2 = something.say();

console.log(message1 === 'Hallo Sonniger Tag!');  // true
console.log(message2 === 'Hallo Bewölkter Tag!');  // true

// Die folgenden sind nicht öffentlich
console.log(something._greet === undefined);  // true
console.log(something._privates === undefined);  // true
console.log(something._updateMessage === undefined);  // true

// Eine weitere Instanz, die die _privates nicht teilt
const something2 = new Something('ein weiterer Sonniger Tag!');

const message3 = something2.say();

console.log(message3 === 'Hallo ein weiterer Sonniger Tag!'); // true

Vorteile, die ich sehe:

  • wir können private Methoden haben (_greet und _updateMessage verhalten sich wie private Methoden, solange wir die Referenzen nicht exportieren)
  • auch wenn sie nicht im Prototyp sind, werden die oben genannten Methoden Speicher sparen, da die Instanzen einmal außerhalb der Klasse erstellt werden (im Gegensatz dazu, sie im Konstruktor zu definieren)
  • wir lassen keine globalen Variablen auslaufen, da wir uns in einem Modul befinden
  • wir können auch private Eigenschaften mit dem gebundenen _privates-Objekt haben

Einige Nachteile, die mir einfallen:

Ein laufendes Snippet finden Sie hier: http://www.webpackbin.com/NJgI5J8lZ

9voto

Nikita Kurtin Punkte 5299

Ja - du kannst eine gekapselte Eigenschaft erstellen, aber das ist zumindest mit ES6 nicht mit Zugriffsmodifizierern (public|private) erfolgt.

Hier ist ein einfaches Beispiel, wie es mit ES6 gemacht werden kann:

1 Erstelle eine Klasse mit dem class Wort

2 Deklariere innerhalb des Konstruktors einen blockgesteuerten Variablen mit den reservierten Wörtern let ODER const -> da sie blockgesteuert sind, können sie von außerhalb nicht zugegriffen werden (gekapselt)

3 Um einige Zugriffskontrollen (Setter|Getter) für diese Variablen zuzulassen, kannst du eine Instanzmethode innerhalb des Konstruktors deklarieren using: this.methodName=function(){} Syntax

"use strict";
    class Something{
        constructor(){
            //private Eigenschaft
            let property="test";
            //private finale (unveränderliche) Eigenschaft
            const property2="test2";
            //öffentlicher Getter
            this.getProperty2=function(){
                return property2;
            }
            //öffentlicher Getter
            this.getProperty=function(){
                return property;
            }
            //öffentlicher Setter
            this.setProperty=function(prop){
                property=prop;
            }
        }
    }

Testen wir es jetzt:

var s=new Something();
    console.log(typeof s.property);//undefined 
    s.setProperty("another");//setze die gekapselte `property`
    console.log(s.getProperty());//erhalte den Wert der gekapselten `property`
    console.log(s.getProperty2());//erhalte den Wert der gekapselten unveränderlichen `property2`

7voto

Sergei Punkte 5385

Oh, so viele exotische Lösungen! Normalerweise ist mir Datenschutz egal, also verwende ich "Pseudo-Privatsphäre" wie es hier gesagt wird. Aber wenn es dir wichtig ist (wenn es spezielle Anforderungen dafür gibt), verwende ich etwas Ähnliches wie in diesem Beispiel:

class jobImpl{
  // public
  constructor(name){
    this.name = name;
  }
  // public
  do(time){
    console.log(`${this.name} started at ${time}`);
    this.prepare();
    this.execute();
  }
  //public
  stop(time){
    this.finish();
    console.log(`${this.name} finished at ${time}`);
  }
  // private
  prepare(){ console.log('prepare..'); }
  // private
  execute(){ console.log('execute..'); }
  // private
  finish(){ console.log('finish..'); }
}

function Job(name){
  var impl = new jobImpl(name);
  return {
    do: time => impl.do(time),
    stop: time => impl.stop(time)
  };
}

// Test:
// create class "Job"
var j = new Job("Graben eines Grabens");
// call public members..
j.do("08:00 Uhr");
j.stop("18:00 Uhr");

// try to call private members or fields..
console.log(j.name); // undefined
j.execute(); // error

Ein andere mögliche Implementierung der Funktion (Konstruktor) Job:

function Job(name){
  var impl = new jobImpl(name);
  this.do = time => impl.do(time),
  this.stop = time => impl.stop(time)
}

6voto

MarkM Punkte 101

Ich bin auf diesen Beitrag gestoßen, als ich nach bewährten Methoden für "private Daten für Klassen" gesucht habe. Es wurde erwähnt, dass einige der Muster Leistungsprobleme haben könnten.

Ich habe einige jsperf-Tests basierend auf den 4 Hauptmustern aus dem Online-Buch "Exploring ES6" erstellt:

http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes

Die Tests finden Sie hier:

https://jsperf.com/private-data-for-classes

In Chrome 63.0.3239 / Mac OS X 10.11.6 waren die leistungsstärksten Muster "Private Daten über Konstruktorumgebungen" und "Private Daten über eine Namenskonvention". Safari hat für WeakMap gut abgeschnitten, aber nicht so gut Chrome.

Ich kenne den Speicherplatz nicht, aber das Muster für "Konstruktorumgebungen", vor dem einige gewarnt hatten, dass es ein Leistungsproblem geben könnte, war sehr leistungsstark.

Die 4 grundlegenden Muster sind:

Private Daten über Konstruktorumgebungen

class Countdown {
    constructor(counter, action) {
        Object.assign(this, {
            dec() {
                if (counter < 1) return;
                counter--;
                if (counter === 0) {
                    action();
                }
            }
        });
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Private Daten über Konstruktorumgebungen 2

class Countdown {
    constructor(counter, action) {
        this.dec = function dec() {
            if (counter < 1) return;
            counter--;
            if (counter === 0) {
                action();
            }
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Private Daten über eine Namenskonvention

class Countdown {
    constructor(counter, action) {
        this._counter = counter;
        this._action = action;
    }
    dec() {
        if (this._counter < 1) return;
        this._counter--;
        if (this._counter === 0) {
            this._action();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Private Daten über WeakMaps

const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
    constructor(counter, action) {
        _counter.set(this, counter);
        _action.set(this, action);
    }
    dec() {
        let counter = _counter.get(this);
        if (counter < 1) return;
        counter--;
        _counter.set(this, counter);
        if (counter === 0) {
            _action.get(this)();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Private Daten über Symbole

const _counter = Symbol('counter');
const _action = Symbol('action');

class Countdown {
    constructor(counter, action) {
        this[_counter] = counter;
        this[_action] = action;
    }
    dec() {
        if (this[_counter] < 1) return;
        this[_counter]--;
        if (this[_counter] === 0) {
            this[_action]();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

6voto

kevlened Punkte 10228

WeakMap

  • Unterstützt in IE11 (Symbole nicht)
  • Hard-privat (Eigenschaften, die Symbole verwenden, sind aufgrund von Object.getOwnPropertySymbols weich-privat)
  • kann wirklich sauber aussehen (im Gegensatz zu Closures, die alle Eigenschaften und Methoden im Konstruktor erfordern)

Zuerst muss eine Funktion definiert werden, um WeakMap zu umhüllen:

function Private() {
  const map = new WeakMap();
  return obj => {
    let props = map.get(obj);
    if (!props) {
      props = {};
      map.set(obj, props);
    }
    return props;
  };
}

Dann muss außerhalb Ihrer Klasse eine Referenz erstellt werden:

const p = new Private();

class Person {
  constructor(name, age) {
    this.name = name;
    p(this).age = age; // es ist einfach, eine private Variable zu setzen
  }

  getAge() {
    return p(this).age; // und eine private Variable zu erhalten
  }
}

Hinweis: Klasse wird von IE11 nicht unterstützt, sieht aber im Beispiel sauberer aus.

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