Private JavaScript-Methoden


482

Um eine JavaScript-Klasse mit einer öffentlichen Methode zu erstellen, würde ich Folgendes tun:

function Restaurant() {}

Restaurant.prototype.buy_food = function(){
   // something here
}

Restaurant.prototype.use_restroom = function(){
   // something here
}

Auf diese Weise können Benutzer meiner Klasse:

var restaurant = new Restaurant();
restaurant.buy_food();
restaurant.use_restroom();

Wie erstelle ich eine private Methode, die von den Methoden buy_foodund aufgerufen werden use_restroomkann, jedoch nicht extern von Benutzern der Klasse?

Mit anderen Worten, ich möchte, dass meine Methodenimplementierung Folgendes kann:

Restaurant.prototype.use_restroom = function() {
   this.private_stuff();
}

Das sollte aber nicht funktionieren:

var r = new Restaurant();
r.private_stuff();

Wie definiere ich private_stuffeine private Methode, damit beide zutreffen?

Ich habe Doug Crockfords Artikel einige Male gelesen, aber es scheint nicht, dass "private" Methoden von öffentlichen Methoden aufgerufen werden können und "privilegierte" Methoden extern aufgerufen werden können.

Antworten:


403

Sie können es tun, aber der Nachteil ist, dass es nicht Teil des Prototyps sein kann:

function Restaurant() {
    var myPrivateVar;

    var private_stuff = function() {  // Only visible inside Restaurant()
        myPrivateVar = "I can set this here!";
    }

    this.use_restroom = function() {  // use_restroom is visible to all
        private_stuff();
    }

    this.buy_food = function() {   // buy_food is visible to all
        private_stuff();
    }
}

9
Das Verstecken von Ausdünnungen im Verschluss garantiert nicht die Privatsphäre aller Dolmetscher. Siehe code.google.com/p/google-caja/wiki/…
Mike Samuel

51
@mikesamuel - wahr, aber nur, wenn diese Dolmetscher Fehler in sich haben :)
Jvenema

133
Dies ist in Ordnung eine private Methode, verbraucht jedoch tendenziell viel mehr Speicher als eine übliche Prototypmethode, insbesondere wenn Sie viele dieser Objekte erstellen. Für jede Objektinstanz wird eine separate Funktion erstellt, die an das Objekt und nicht an die Klasse gebunden ist. Außerdem wird dadurch kein Müll gesammelt, bis das Objekt selbst zerstört ist.
Arindam

4
Wenn Sie ein Objekt erstellen, das McDonalds () von Restaurant () erbt, kann McDonalds private Methoden nicht überschreiben, wenn Sie sie auf diese Weise deklarieren. Nun, eigentlich können Sie, aber es wird nicht funktionieren, wenn eine andere Methode die private Methode aufruft, es wird die ursprüngliche Version der Methode aufrufen und Sie können auch die übergeordnete Methode nicht aufrufen. Bisher habe ich keinen guten Weg gefunden, private Methoden zu deklarieren, die gut mit Vererbung funktionieren. Dies und die Auswirkungen auf die Leistung machen dieses Muster überhaupt nicht sehr gut. Ich würde empfehlen, eine Art Präfix zu verwenden, um private Methoden anzugeben, und mit einer Unterstreichung zu beginnen.
Hoffmann

68
Private Methoden sollen nicht überschrieben werden - sie sind privat.
17 von 26

163

Verwenden der selbstaufrufenden Funktion und des Aufrufs

JavaScript verwendet Prototypen und verfügt nicht über Klassen (oder Methoden) wie objektorientierte Sprachen. Ein JavaScript-Entwickler muss in JavaScript denken.

Wikipedia-Zitat:

Im Gegensatz zu vielen objektorientierten Sprachen gibt es keinen Unterschied zwischen einer Funktionsdefinition und einer Methodendefinition. Die Unterscheidung erfolgt vielmehr während des Funktionsaufrufs; Wenn eine Funktion als Methode eines Objekts aufgerufen wird, ist das lokale Schlüsselwort this der Funktion für diesen Aufruf an dieses Objekt gebunden.

Lösung mit einer selbstaufrufenden Funktion und der Aufruffunktion zum Aufrufen der privaten "Methode":

var MyObject = (function () {

    // Constructor
    function MyObject (foo) {
        this._foo = foo;
    }

    function privateFun (prefix) {
        return prefix + this._foo;
    }

    MyObject.prototype.publicFun = function () {
        return privateFun.call(this, '>>');
    }

    return MyObject;
})();


var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined

Mit der Aufruffunktion können wir die private Funktion mit dem entsprechenden Kontext aufrufen ( this).


Einfacher mit Node.js.

Wenn Sie node.js verwenden , benötigen Sie das IIFE nicht, da Sie das Modulladesystem nutzen können :

function MyObject (foo) {
    this._foo = foo;
}

function privateFun (prefix) {
    return prefix + this._foo;
}

MyObject.prototype.publicFun = function () {
    return privateFun.call(this, '>>');
}

exports.MyObject = MyObject;

Laden Sie die Datei:

var MyObject = require('./MyObject').MyObject;

var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined


(experimentell) ES7 mit dem Bind Operator

Der Bindungsoperator ::ist ein ECMAScript- Vorschlag und wird in Babel implementiert ( Stufe 0 ).

export default class MyObject {
  constructor (foo) {
    this._foo = foo;
  }

  publicFun () {
    return this::privateFun('>>');
  }
}

function privateFun (prefix) {
  return prefix + this._foo;
}

Laden Sie die Datei:

import MyObject from './MyObject';

let myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // TypeError: myObject.privateFun is not a function

34
Dies ist die beste Antwort. Die Vorteile privater Methoden, kein Müll.
TaylorMac

1
@ TaylorMac Mit Ausnahme des .callTeils.
Pishpish

1
@janje Huh? Das ist der Punkt der Frage, es gibt kein privates f()in Javascript (im Moment nicht).
Yves M.

2
@ YvesM. Der Punkt der Frage ist die Auswahl des besten Musters, das es simuliert.
Pishpish

1
@changed Was ist der beste Kompromiss, um versteckte Funktionen (von außen nicht aufrufbar), nette Sintax (nein .call) und kleinen Speicher (keine Funktionen auf der Instanz) zu haben? Existiert das überhaupt?
Ciprian Tomoiagă

161

Sie können private Methoden wie folgt simulieren:

function Restaurant() {
}

Restaurant.prototype = (function() {
    var private_stuff = function() {
        // Private code here
    };

    return {

        constructor:Restaurant,

        use_restroom:function() {
            private_stuff();
        }

    };
})();

var r = new Restaurant();

// This will work:
r.use_restroom();

// This will cause an error:
r.private_stuff();

Weitere Informationen zu dieser Technik finden Sie hier: http://webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html


7
Ich würde auch Douglas Crockfords Website als Ressource für private / öffentliche Methoden und Mitglieder vorschlagen. Javascript.crockford.com/private.html
Jared

10
Er erwähnte diesen Link in der Frage.
Gulzar Nazim

8
Der Nachteil dieser Methode ist, dass private_stuff () nicht auf andere private Daten in Restaurant zugreifen kann und andere Restaurantmethoden private_stuff () nicht aufrufen können. Der Vorteil ist, dass Sie use_restroom () im Prototyp behalten können, wenn Sie keine der oben genannten Bedingungen benötigen.
17 vom 26.

6
Dies sollte die Lösung und akzeptierte Antwort sein, da der Autor eindeutig die Prototyp-Eigenschaft verwendet.
Gabriel Llamas

23
Mit dem von @georgebrock vorgeschlagenen Muster werden alle privaten Daten von allen Restaurantobjekten gemeinsam genutzt. Dies ähnelt statischen privaten Variablen und Funktionen in klassenbasiertem OOP. Ich gehe davon aus, dass das OP dies nicht will. Um zu veranschaulichen, was ich meine, habe ich eine jsFiddle erstellt .
Feklee

35

In diesen Situationen, in denen Sie eine öffentliche API haben und private und öffentliche Methoden / Eigenschaften wünschen, verwende ich immer das Modulmuster. Dieses Muster wurde in der YUI-Bibliothek populär gemacht. Die Details finden Sie hier:

http://yuiblog.com/blog/2007/06/12/module-pattern/

Es ist wirklich einfach und für andere Entwickler leicht zu verstehen. Ein einfaches Beispiel:

var MYLIB = function() {  
    var aPrivateProperty = true;
    var aPrivateMethod = function() {
        // some code here...
    };
    return {
        aPublicMethod : function() {
            aPrivateMethod(); // okay
            // some code here...
        },
        aPublicProperty : true
    };  
}();

MYLIB.aPrivateMethod() // not okay
MYLIB.aPublicMethod() // okay

Diese Art von Dingen würde durch die automatische Vervollständigung einer IDE nicht erkannt werden :(
Klicken Sie auf Upvote

19
Dies ist jedoch keine Klasse, daher können Sie keine 2 "Instanzen" davon mit unterschiedlichen Zuständen haben.
DevAntoine

Entfernen Sie den Teil () und Sie haben eine "Klasse". Zumindest dort, wo Sie verschiedene Isntanzen mit verschiedenen Zuständen instanziieren können. Das Modul Muster ist jedoch ziemlich speicherintensiv ...
Oligofren

@ DevAntoine Schauen Sie sich die Kommentare für 17 von 26 Antworten an. In JavaScript gehen erweiterbare Klassen und private Methoden nicht einfach Hand in Hand. Mein Vorschlag in diesem Fall wäre, mit Komposition über Vererbung zu gehen. Erstellen Sie einen erweiterbaren Prototyp mit denselben Methoden wie das eingeschlossene Betonobjekt. In Ihrem Prototyp können Sie dann entscheiden, wann Sie Methoden für Ihr konkretes Objekt aufrufen möchten.

Gibt es einen Nachteil beim Aufrufen der öffentlichen Variablen aus den privaten Funktionen wie folgt : aPrivateMethod = function() { MYLIB.aPublicProperty}?
Hanna

21

Hier ist die Klasse, die ich erstellt habe, um zu verstehen, was Douglas Crockford auf seiner Website Private Members in JavaScript vorgeschlagen hat

function Employee(id, name) { //Constructor
    //Public member variables
    this.id = id;
    this.name = name;
    //Private member variables
    var fName;
    var lName;
    var that = this;
    //By convention, we create a private variable 'that'. This is used to     
    //make the object available to the private methods. 

    //Private function
    function setFName(pfname) {
        fName = pfname;
        alert('setFName called');
    }
    //Privileged function
    this.setLName = function (plName, pfname) {
        lName = plName;  //Has access to private variables
        setFName(pfname); //Has access to private function
        alert('setLName called ' + this.id); //Has access to member variables
    }
    //Another privileged member has access to both member variables and private variables
    //Note access of this.dataOfBirth created by public member setDateOfBirth
    this.toString = function () {
        return 'toString called ' + this.id + ' ' + this.name + ' ' + fName + ' ' + lName + ' ' + this.dataOfBirth; 
    }
}
//Public function has access to member variable and can create on too but does not have access to private variable
Employee.prototype.setDateOfBirth = function (dob) {
    alert('setDateOfBirth called ' + this.id);
    this.dataOfBirth = dob;   //Creates new public member note this is accessed by toString
    //alert(fName); //Does not have access to private member
}
$(document).ready()
{
    var employee = new Employee(5, 'Shyam'); //Create a new object and initialize it with constructor
    employee.setLName('Bhaskar', 'Ram');  //Call privileged function
    employee.setDateOfBirth('1/1/2000');  //Call public function
    employee.id = 9;                     //Set up member value
    //employee.setFName('Ram');  //can not call Private Privileged method
    alert(employee.toString());  //See the changed object

}

5
Das das = dies ist ein Muster, das ziemlich häufig ist und von Crockford in seinem Buch "Javascript: The good parts"
Oligofren

8
thatwird verwendet this, um Scoping-Probleme zu vermeiden, wenn Funktionen an ein anderes Objekt gebunden sind. Hier werden Sie die Speicherung thisin thatund nie wieder mit denen ist das gleiche wie es überhaupt nicht tun. Sie sollten thismit thatallen Constructorinneren Funktionen wechseln (keine Methodendeklaration). Wenn employeesich applyed oder callin irgendeiner Weise ed werfen diese Methoden könnten da thiswird falsch gebunden.
Maroshii

Außerdem verfügt jede Instanz über eine vollständige Kopie der privaten Funktionen, die ineffizient ist. Das zusätzlich zu der Tatsache, dass öffentliche Methoden nicht auf private Klassenvariablen zugreifen können, veranlasst mich, zu Dart zu wechseln. Leider ist Angulardart Super Beta.
Ray Foss

Wo ist die "Konstruktor" -Methode dabei? Wo würde ich Logik ablegen, die normalerweise in der Konstruktormethode einer Klasse ausgeführt wird, wenn sie instanziiert wird?
BadHorsie

13

Ich habe das heraufbeschworen: EDIT: Eigentlich hat sich jemand mit einer identischen Lösung verbunden. Duh!

var Car = function() {
}

Car.prototype = (function() {
    var hotWire = function() {
        // Private code *with* access to public properties through 'this'
        alert( this.drive() ); // Alerts 'Vroom!'
    }

    return {
        steal: function() {
            hotWire.call( this ); // Call a private method
        },
        drive: function() {
            return 'Vroom!';
        }
    };
})();

var getAwayVechile = new Car();

hotWire(); // Not allowed
getAwayVechile.hotWire(); // Not allowed
getAwayVechile.steal(); // Alerts 'Vroom!'

1
Dies ist eine nette Technik, aber wie würden Sie Parameter in Ihrem Konstruktor zulassen? Zum Beispiel, var getAwayVehicle = new Car(100);wo 100ist Geschwindigkeit und Sie möchten Geschwindigkeit alarmieren. Vielen Dank!
Jason

1
Ich habe es herausgefunden, kann es haben var Car = function(speed) { this.speed = speed; }und zurückgeben {Konstruktor: Auto, ... `
Jason

11

Ich denke, solche Fragen tauchen immer wieder auf, weil die Schließungen nicht verstanden wurden. Verschlüsse sind das Wichtigste in JS. Jeder JS-Programmierer muss die Essenz davon spüren.

1. Zunächst müssen wir einen separaten Geltungsbereich festlegen (Abschluss).

function () {

}

2. In diesem Bereich können wir tun, was wir wollen. Und niemand wird davon erfahren.

function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
}

3. Damit die Welt etwas über unsere Restaurantklasse weiß, müssen wir sie von der Schließung zurückgeben.

var Restaurant = (function () {
    // Restaurant definition
    return Restaurant
})()

4. Am Ende haben wir:

var Restaurant = (function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
    return Restaurant
})()

5. Dieser Ansatz bietet auch Potenzial für Vererbung und Vorlagen

// Abstract class
function AbstractRestaurant(skills) {
    var name
    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return skills && name in skills ? skills[name]() : null
    }
    return Restaurant
}

// Concrete classes
SushiRestaurant = AbstractRestaurant({ 
    sushi: function() { return new Sushi() } 
})

PizzaRestaurant = AbstractRestaurant({ 
    pizza: function() { return new Pizza() } 
})

var r1 = new SushiRestaurant('Yo! Sushi'),
    r2 = new PizzaRestaurant('Dominos Pizza')

r1.getFood('sushi')
r2.getFood('pizza')

Ich hoffe, das hilft jemandem, dieses Thema besser zu verstehen


2
Was Sie in Punkt 4 haben, ist wunderbar! Ich denke, es ist die einzige Antwort von allen hier, bei der Sie sowohl die Leistung / den Speichergewinn durch die Verwendung von Methoden für den Prototyp erhalten, als auch diese öffentlichen Methoden vollen Zugriff auf die privaten Mitglieder haben. +1
Hudon

7
Es funktioniert nicht. Die Namensvariable wirkt hier wie eine statische Variable und wird von allen Instanzen von Restaurant gemeinsam genutzt. Hier ist jsbin: jsbin.com/oqewUWa/2/edit?js,output
Shital Shah

Es ist ein guter Versuch, aber wie Shital betonte, ist die Namensvariable fehlerhaft.
Oligofren

2
Das Hinzufügen meiner 2c hier, um zu bestätigen, dass dies nicht funktioniert, sieht gut aus, aber wie oben erwähnt, wird "Name" als statische Variable dienen, dh über alle Instanzen hinweg geteilt
Paul Carroll

10

Persönlich bevorzuge ich das folgende Muster zum Erstellen von Klassen in JavaScript:

var myClass = (function() {
    // Private class properties go here

    var blueprint = function() {
        // Private instance properties go here
        ...
    };

    blueprint.prototype = { 
        // Public class properties go here
        ...
    };

    return  {
         // Public class properties go here
        create : function() { return new blueprint(); }
        ...
    };
})();

Wie Sie sehen, können Sie sowohl Klasseneigenschaften als auch Instanzeigenschaften definieren, von denen jede öffentlich und privat sein kann.


Demo

var Restaurant = function() {
    var totalfoodcount = 0;        // Private class property
    var totalrestroomcount  = 0;   // Private class property
    
    var Restaurant = function(name){
        var foodcount = 0;         // Private instance property
        var restroomcount  = 0;    // Private instance property
        
        this.name = name
        
        this.incrementFoodCount = function() {
            foodcount++;
            totalfoodcount++;
            this.printStatus();
        };
        this.incrementRestroomCount = function() {
            restroomcount++;
            totalrestroomcount++;
            this.printStatus();
        };
        this.getRestroomCount = function() {
            return restroomcount;
        },
        this.getFoodCount = function() {
            return foodcount;
        }
    };
   
    Restaurant.prototype = {
        name : '',
        buy_food : function(){
           this.incrementFoodCount();
        },
        use_restroom : function(){
           this.incrementRestroomCount();
        },
        getTotalRestroomCount : function() {
            return totalrestroomcount;
        },
        getTotalFoodCount : function() {
            return totalfoodcount;
        },
        printStatus : function() {
           document.body.innerHTML
               += '<h3>Buying food at '+this.name+'</h3>'
               + '<ul>' 
               + '<li>Restroom count at ' + this.name + ' : '+ this.getRestroomCount() + '</li>'
               + '<li>Food count at ' + this.name + ' : ' + this.getFoodCount() + '</li>'
               + '<li>Total restroom count : '+ this.getTotalRestroomCount() + '</li>'
               + '<li>Total food count : '+ this.getTotalFoodCount() + '</li>'
               + '</ul>';
        }
    };

    return  { // Singleton public properties
        create : function(name) {
            return new Restaurant(name);
        },
        printStatus : function() {
          document.body.innerHTML
              += '<hr />'
              + '<h3>Overview</h3>'
              + '<ul>' 
              + '<li>Total restroom count : '+ Restaurant.prototype.getTotalRestroomCount() + '</li>'
              + '<li>Total food count : '+ Restaurant.prototype.getTotalFoodCount() + '</li>'
              + '</ul>'
              + '<hr />';
        }
    };
}();

var Wendys = Restaurant.create("Wendy's");
var McDonalds = Restaurant.create("McDonald's");
var KFC = Restaurant.create("KFC");
var BurgerKing = Restaurant.create("Burger King");

Restaurant.printStatus();

Wendys.buy_food();
Wendys.use_restroom();
KFC.use_restroom();
KFC.use_restroom();
Wendys.use_restroom();
McDonalds.buy_food();
BurgerKing.buy_food();

Restaurant.printStatus();

BurgerKing.buy_food();
Wendys.use_restroom();
McDonalds.buy_food();
KFC.buy_food();
Wendys.buy_food();
BurgerKing.buy_food();
McDonalds.buy_food();

Restaurant.printStatus();

Siehe auch diese Geige .


Dies bringt mich dazu, es6-Klassen zu verwenden und zu sehen, was es auch transpiliert
Sheriffderek

9

All diese Schließung kostet Sie. Stellen Sie sicher, dass Sie die Auswirkungen auf die Geschwindigkeit insbesondere im Internet Explorer testen. Sie werden feststellen, dass Sie mit einer Namenskonvention besser dran sind. Es gibt immer noch viele Webbenutzer in Unternehmen, die gezwungen sind, IE6 zu verwenden ...


34
Wen interessiert das ernsthaft?
nowayyy

17
Die 9%, die immer noch IE6 verwenden, interessieren sich nicht für Geschwindigkeit, Optimierungen und alle modernen HTML5-Funktionen. Schließungen kosten also nichts.
Gabriel Llamas


7
@ LorenzoPolidori w3schools Benutzer! == Corporate Web Benutzer;]
WynandB

Eine Namenskonvention (z. B. Voranstellen eines Unterstrichs) ist der richtige Weg. Der Code ist einfacher zu pflegen und die Methoden sind immer noch auf dem Prototyp definiert. Heutzutage aber ... markiere ich die Methode im Typoskript einfach als privat.
David Sherret

5

Sei nicht so wortreich. Es ist Javascript. Verwenden Sie eine Namenskonvention .

Nachdem ich jahrelang in es6-Klassen gearbeitet habe, habe ich kürzlich begonnen, an einem es5-Projekt zu arbeiten (mit requireJS, das bereits sehr ausführlich aussieht). Ich habe alle hier genannten Strategien immer wieder wiederholt und alles läuft im Grunde darauf hinaus, eine Namenskonvention zu verwenden :

  1. Javascript hat keine Scope-Schlüsselwörter wie private. Andere Entwickler, die Javascript eingeben, wissen dies im Voraus. Daher ist eine einfache Namenskonvention mehr als ausreichend. Eine einfache Namenskonvention zum Präfixieren eines Unterstrichs löst das Problem sowohl privater Eigenschaften als auch privater Methoden.
  2. Lassen Sie uns den Prototyp aus Geschwindigkeitsgründen nutzen, aber nicht ausführlicher. Lassen Sie uns versuchen, die es5 "Klasse" so genau wie möglich in anderen Backend-Sprachen zu betrachten (und jede Datei als Klasse zu behandeln, auch wenn wir keine Instanz zurückgeben müssen).
  3. Lassen Sie uns mit einer realistischeren Modulsituation demonstrieren (wir werden alte es5- und alte requireJs verwenden).

my-tooltip.js

    define([
        'tooltip'
    ],
    function(
        tooltip
    ){

        function MyTooltip() {
            // Later, if needed, we can remove the underscore on some
            // of these (make public) and allow clients of our class
            // to set them.
            this._selector = "#my-tooltip"
            this._template = 'Hello from inside my tooltip!';
            this._initTooltip();
        }

        MyTooltip.prototype = {
            constructor: MyTooltip,

            _initTooltip: function () {
                new tooltip.tooltip(this._selector, {
                    content: this._template,
                    closeOnClick: true,
                    closeButton: true
                });
            }
        }

        return {
            init: function init() {
               new MyTooltip();  // <-- Our constructor adds our tooltip to the DOM so not much we need to do after instantiation.
            }

            // You could instead return a new instantiation, 
            // if later you do more with this class.
            /* 
            create: function create() {
               return new MyTooltip();
            }
            */
        }
    });

2
Es sollte beachtet werden, dass weder die Javascript-Sprache noch ein typischer Browser-Host Objekte definiert, die auf Namenskonventionen beruhen, um den privaten Status zu "verbergen". Sie haben also vielleicht Recht, dass Entwickler das Konzept verstehen, aber es führt immer noch zu einem sehr unkonventionellen Objekt. OO-Ansatz zur OO-Programmierung.
Rich Remer

Darf ich um eine gute Referenz bitten? Es gibt Teile in diesem Beispiel, die für mich neu sind. Das defineund constructorund die Struktur selbst. Obwohl ich der Antwort größtenteils zustimme, habe ich angefangen, mit JS mit viel OOP-Einfluss zu arbeiten, und bin sogar zu früh zu TS gesprungen, da ich zuvor Erfahrung mit C # hatte. Ich denke, ich muss diese Dinge verlernen und das Prototyping- / Verfahrensparadigma verstehen. (upvoted, übrigens)
Cold Cerberus

1
@ColdCerberus Dieses Code-Snippet verwendet es5. Ein vollständiges Bild dieses Ansatzes finden Sie hier: gist.github.com/jonnyreeves/2474026 . Beachten Sie jedoch, dass Sie diesen Ansatz verwenden und ihn auf die Verwendung von es6-Klassen aktualisieren sollten: googlechrome.github.io/samples/classes-es6- und es6-Module (Import / Export-Syntax): hackernoon.com/…
Programmierer

5

Sie können dies jetzt mit privaten es10-Methoden tun . Sie müssen nur ein #vor dem Methodennamen einfügen.

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
    return #privateMethod();
  }
}

2
Nur dass dies Stufe 3 ist und noch nicht offiziell Teil der Sprache ist.
Misterhtmlcss

3

Nehmen Sie eine der Lösungen, die Crockfords privatem oder privilegiertem Muster folgen . Zum Beispiel:

function Foo(x) {
    var y = 5;
    var bar = function() {
        return y * x;
    };

    this.public = function(z) {
        return bar() + x * z;
    };
}

In jedem Fall, in dem der Angreifer kein "Ausführungsrecht" im JS-Kontext hat, hat er keine Möglichkeit, auf "öffentliche" oder "private" Felder oder Methoden zuzugreifen. Falls der Angreifer diesen Zugriff hat, kann er diesen Einzeiler ausführen:

eval("Foo = " + Foo.toString().replace(
    /{/, "{ this.eval = function(code) { return eval(code); }; "
));

Beachten Sie, dass der obige Code für alle Konstruktortyp-Datenschutzdaten generisch ist. Bei einigen der hier aufgeführten Lösungen wird dies fehlschlagen, aber es sollte klar sein, dass so gut wie alle auf Verschlüssen basierenden Lösungen auf unterschiedliche Weise auf diese Weise beschädigt werden könnenreplace() Parametern auf beschädigt werden können.

Nachdem dies ausgeführt wurde, verfügt jedes mit erstellte Objekt new Foo()über eine evalMethode, die aufgerufen werden kann, um Werte oder Methoden zurückzugeben oder zu ändern, die im Abschluss des Konstruktors definiert sind, z.

f = new Foo(99);
f.eval("x");
f.eval("y");
f.eval("x = 8");

Das einzige Problem, das ich dabei sehen kann, ist, dass es in Fällen, in denen es nur eine Instanz gibt und die beim Laden erstellt wird, nicht funktioniert. Es gibt jedoch keinen Grund, einen Prototyp tatsächlich zu definieren. In diesem Fall kann der Angreifer das Objekt anstelle des Konstruktors einfach neu erstellen, solange er die Möglichkeit hat, dieselben Parameter zu übergeben (z. B. sind sie konstant oder werden aus verfügbaren Werten berechnet).

Meiner Meinung nach macht dies Crockfords Lösung ziemlich nutzlos.Da die "Privatsphäre" leicht verletzt werden kann, sind die Nachteile seiner Lösung (verminderte Lesbarkeit und Wartbarkeit, verminderte Leistung, mehr Speicher) die prototypbasierte Methode "keine Privatsphäre" die bessere Wahl.

Normalerweise verwende ich führende Unterstriche zum Markieren __privateund für _protectedMethoden und Felder (Perl-Stil), aber die Idee, Datenschutz in JavaScript zu haben, zeigt nur, dass es sich um eine missverstandene Sprache handelt.

Daher bin ich mit Crockford bis auf seinen ersten Satz nicht einverstanden .

Wie erhalten Sie echte Privatsphäre in JS? Stellen Sie alles, was erforderlich ist, um privat zu sein, auf die Serverseite und verwenden Sie JS, um AJAX-Aufrufe auszuführen.


Dies ist ein ernstes Problem, das bekannter sein sollte. Gibt es eine "Verteidigung" gegen diesen Angriff?
James

@ James Keine, die ich kenne, ich denke, es ist die Natur des Tieres. Wie bereits erwähnt, können Sie Funktionen auf den Server verschieben, auf dem sie in einer geschützten Umgebung ausgeführt werden. Der Punkt, den ich in meiner Antwort hinter mich bringen wollte, ist, dass Crockfords Lösung nicht hilft, Code unnötig kompliziert und die Notwendigkeit verbirgt, etwas dagegen zu unternehmen.
Fozi

Wenn ein Benutzer ein geheimes Passwort eingibt, kann er dies nicht serverseitig tun. Irgendwann wird das Passwort in einer 'privaten' Variable sein. Könnte ein Angreifer es also lesen? Ich vertraue meinem Code und trotzdem erlauben meine Hausstandards eval () nicht. Der Angreifer kann ein böswilliges JavaScript-Plug-In oder eine böswillige Bibliothek eines Drittanbieters sein, die ich nicht richtig überprüft habe. Ja, ich muss diese überprüfen. Der Angreifer könnte auch so etwas wie eine Anzeige auf der Seite sein, die nicht mit meinem Code interagieren sollte. Ich schütze mich davor, indem ich meinen gesamten Code anonym verpacke (function () {allMyStuff}());, um nichts Globales preiszugeben.
James

@ James Dies wird OT, wenn Sie dies fortsetzen möchten, öffnen Sie bitte eine neue Frage. Ja, ein Angreifer kann das Passwort lesen. Aus Ihrer "privaten" Variablen. Oder aus dem DOM. Oder er kann die AJAX-API ersetzen. Oder er ersetzt Ihre Seite durch etwas anderes. Wenn er keine der oben genannten Aktionen ausführen kann, ist kein JS-Datenschutz erforderlich, da er auch keine Ihrer JS-Variablen lesen kann. Der Punkt ist, dass Crockfords "Lösung", die derzeit jeder verwendet, bei diesem Problem nicht hilft.
Fozi

Ich glaube, dass die Verschleierung von Pseudozufallscode eine schwache Verteidigung gegen diesen Angriff darstellt - es ist schwieriger, den Funktionskörper zu ändern, wenn Sie sich nicht darauf verlassen können, dass die Funktion einen festen Namen hat. schwieriger zu tun, f.eval('nameOfVariable')wenn Sie nicht wissen, was 'nameOfVariable'ist ...
Gershom


2

Wenn Sie alle öffentlichen und privaten Funktionen mit der Möglichkeit für öffentliche Funktionen nutzen möchten, auf private Funktionen zuzugreifen, geben Sie den Layoutcode für ein Objekt wie das folgende ein:

function MyObject(arg1, arg2, ...) {
  //constructor code using constructor arguments...
  //create/access public variables as 
  // this.var1 = foo;

  //private variables

  var v1;
  var v2;

  //private functions
  function privateOne() {
  }

  function privateTwon() {
  }

  //public functions

  MyObject.prototype.publicOne = function () {
  };

  MyObject.prototype.publicTwo = function () {
  };
}

Kann mir jemand sagen, warum dies abgelehnt wurde? Sieht gut für mich aus.
Thomasrutter

10
Jedes Mal, wenn Sie a ausführen new MyObject, wird der Prototyp von MyObjectdurch dieselben Werte ersetzt.
Bpierre

2
-1. Weisen Sie .prototypedem Konstruktor niemals das Innere zu.
Bergi

2
var TestClass = function( ) {

    var privateProperty = 42;

    function privateMethod( ) {
        alert( "privateMethod, " + privateProperty );
    }

    this.public = {
        constructor: TestClass,

        publicProperty: 88,
        publicMethod: function( ) {
            alert( "publicMethod" );
            privateMethod( );
        }
    };
};
TestClass.prototype = new TestClass( ).public;


var myTestClass = new TestClass( );

alert( myTestClass.publicProperty );
myTestClass.publicMethod( );

alert( myTestClass.privateMethod || "no privateMethod" );

Ähnlich wie Georgebrock, aber etwas weniger ausführlich (IMHO) Haben Sie Probleme damit? (Ich habe es nirgendwo gesehen)

edit: Mir wurde klar, dass dies irgendwie nutzlos ist, da jede unabhängige Instanziierung eine eigene Kopie der öffentlichen Methoden hat, was die Verwendung des Prototyps untergräbt.


2

Folgendes hat mir bisher am besten gefallen in Bezug auf private / öffentliche Methoden / Mitglieder und Instanziierung in Javascript:

Hier ist der Artikel: http://www.sefol.com/?p=1090

und hier ist das Beispiel:

var Person = (function () {

    //Immediately returns an anonymous function which builds our modules 
    return function (name, location) {

        alert("createPerson called with " + name);

        var localPrivateVar = name;

        var localPublicVar = "A public variable";

        var localPublicFunction = function () {
            alert("PUBLIC Func called, private var is :" + localPrivateVar)
        };

        var localPrivateFunction = function () {
            alert("PRIVATE Func called ")
        };

        var setName = function (name) {

            localPrivateVar = name;

        }

        return {

            publicVar: localPublicVar,

            location: location,

            publicFunction: localPublicFunction,

            setName: setName

        }

    }
})();


//Request a Person instance - should print "createPerson called with ben"
var x = Person("ben", "germany");

//Request a Person instance - should print "createPerson called with candide"
var y = Person("candide", "belgium");

//Prints "ben"
x.publicFunction();

//Prints "candide"
y.publicFunction();

//Now call a public function which sets the value of a private variable in the x instance
x.setName("Ben 2");

//Shouldn't have changed this : prints "candide"
y.publicFunction();

//Should have changed this : prints "Ben 2"
x.publicFunction();

JSFiddle: http://jsfiddle.net/northkildonan/kopj3dt3/1/


Dieser Ansatz hat einen wichtigen Aspekt: ​​Wenn Sie zwei Objekte erstellt haben, befinden sich im Speicher zwei gleiche Methoden (z. B. PublicFunction). 1000 Objekte verbrauchen Ihren gesamten Speicher.
Artem G

2

Das Modulmuster ist in den meisten Fällen richtig. Wenn Sie jedoch Tausende von Instanzen haben, sparen Klassen Speicher. Wenn das Speichern von Speicher ein Problem darstellt und Ihre Objekte eine kleine Menge privater Daten enthalten, aber viele öffentliche Funktionen haben, möchten Sie, dass alle öffentlichen Funktionen im Prototyp gespeichert werden, um Speicher zu sparen.

Das habe ich mir ausgedacht:

var MyClass = (function () {
    var secret = {}; // You can only getPriv() if you know this
    function MyClass() {
        var that = this, priv = {
            foo: 0 // ... and other private values
        };
        that.getPriv = function (proof) {
            return (proof === secret) && priv;
        };
    }
    MyClass.prototype.inc = function () {
        var priv = this.getPriv(secret);
        priv.foo += 1;
        return priv.foo;
    };
    return MyClass;
}());
var x = new MyClass();
x.inc(); // 1
x.inc(); // 2

Das Objekt priventhält private Eigenschaften. Der Zugriff ist über die öffentliche Funktion möglich getPriv(). Diese Funktion wird jedoch zurückgegeben, falsesofern Sie sie nicht übergeben. secretDies ist nur innerhalb des Hauptabschlusses bekannt.


Das simuliert geschützte Mitglieder, Typen, die davon erben, können auch auf die geschützten Mitglieder zugreifen. Ich bevorzuge dieses Muster auch gegenüber dem privaten
HMR

2

Was ist damit?

var Restaurant = (function() {

 var _id = 0;
 var privateVars = [];

 function Restaurant(name) {
     this.id = ++_id;
     this.name = name;
     privateVars[this.id] = {
         cooked: []
     };
 }

 Restaurant.prototype.cook = function (food) {
     privateVars[this.id].cooked.push(food);
 }

 return Restaurant;

})();

Die Suche nach privaten Variablen ist außerhalb des Bereichs der unmittelbaren Funktion nicht möglich. Es gibt keine doppelten Funktionen, wodurch Speicherplatz gespart wird.

Der Nachteil ist, dass die Suche nach privaten Variablen klobig privateVars[this.id].cookedist und lächerlich zu tippen ist. Es gibt auch eine zusätzliche "id" -Variable.


Dies wird so bleiben, Restaurantals würden undefinedSie nichts von der anonymen Funktion zurückgeben.
user4815162342

Wo und wie? Angenommen, der Verweis auf das erstellte Restaurant geht verloren, hat privateVars keinen Verweis auf seinen Besitzer. Der Referenzgraph ist azyklisch. Was vermisse ich?
Evan Leis

Tatsächlich ist dies die einzige Antwort, die neben Methoden auch private Eigenschaften unterstützt. Die einzigen zwei Probleme sind bereits in der Antwort vermerkt.
Pishpish

Ich sehe einen Speicherverlust: Wenn eine Instanz von RestaurantMüll gesammelt wurde, bleiben ihre Werte innerhalb privateVars. A WeakMapkann Arrayin diesem Fall ein guter Ersatz für das sein .
Gershom

2

Wickeln Sie den gesamten Code in die anonyme Funktion: Dann sind alle Funktionen privat und NUR mit Funktionen verbunden window Objekt :

(function(w,nameSpacePrivate){
     w.Person=function(name){
         this.name=name;   
         return this;
     };

     w.Person.prototype.profilePublic=function(){
          return nameSpacePrivate.profile.call(this);
     };  

     nameSpacePrivate.profile=function(){
       return 'My name is '+this.name;
     };

})(window,{});

Benutze das :

  var abdennour=new Person('Abdennour');
  abdennour.profilePublic();

GEIGE


1

Ich bevorzuge es, private Daten in einem zugehörigen zu speichern WeakMap. Auf diese Weise können Sie Ihre öffentlichen Methoden auf dem Prototyp belassen, zu dem sie gehören. Dies scheint der effizienteste Weg zu sein, um dieses Problem für eine große Anzahl von Objekten zu lösen.

const data = new WeakMap();

function Foo(value) {
    data.set(this, {value});
}

// public method accessing private value
Foo.prototype.accessValue = function() {
    return data.get(this).value;
}

// private 'method' accessing private value
function accessValue(foo) {
    return data.get(foo).value;
}

export {Foo};

0

Private Funktionen können nicht über das Modulmuster auf die öffentlichen Variablen zugreifen


0

Da jeder hier seinen eigenen Code gepostet hat, werde ich das auch tun ...

Ich mag Crockford, weil er echte objektorientierte Muster in Javascript eingeführt hat. Aber er hatte auch ein neues Missverständnis, das "das".

Warum benutzt er "das = das"? Es hat überhaupt nichts mit privaten Funktionen zu tun. Es hat mit inneren Funktionen zu tun!

Denn laut Crockford ist dies ein fehlerhafter Code:

Function Foo( ) {
    this.bar = 0; 
    var foobar=function( ) {
        alert(this.bar);
    }
} 

Also schlug er Folgendes vor:

Function Foo( ) {
    this.bar = 0;
    that = this; 
    var foobar=function( ) {
        alert(that.bar);
    }
}

Wie gesagt, ich bin mir ziemlich sicher, dass Crockford seine Erklärung zu diesem und jenem falsch war (aber sein Code ist sicherlich korrekt). Oder hat er nur die Javascript-Welt zum Narren gehalten, um zu wissen, wer seinen Code kopiert? Ich weiß nicht ... Ich bin kein Browser-Geek; D.

BEARBEITEN

Ah, darum geht es: Was bedeutet 'var that = this'? meine in JavaScript?

Also war Crockie wirklich falsch mit seiner Erklärung ... aber richtig mit seinem Code, also ist er immer noch ein großartiger Kerl. :))


0

Im Allgemeinen habe ich das private Objekt _ vorübergehend zum Objekt hinzugefügt. Sie müssen den Datenschutz exklusiv im "Power-Konstruktor" für die Methode öffnen. Wenn Sie die Methode vom Prototyp aus aufrufen, können Sie die Prototypmethode überschreiben

  • Machen Sie eine öffentliche Methode im "Power-Konstruktor" zugänglich: (ctx ist der Objektkontext)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
  • Jetzt habe ich diese openPrivacy:

    GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
        return function() {
            ctx._ = _;
            var res = clss[func].apply(ctx, arguments);
            ctx._ = null;
            return res;
        };
    };

0

Das habe ich herausgefunden:

Benötigt eine Klasse von Zuckercode, die Sie hier finden können . Unterstützt auch geschützte, vererbbare, virtuelle, statische ...

;( function class_Restaurant( namespace )
{
    'use strict';

    if( namespace[ "Restaurant" ] ) return    // protect against double inclusions

        namespace.Restaurant = Restaurant
    var Static               = TidBits.OoJs.setupClass( namespace, "Restaurant" )


    // constructor
    //
    function Restaurant()
    {
        this.toilets = 3

        this.Private( private_stuff )

        return this.Public( buy_food, use_restroom )
    }

    function private_stuff(){ console.log( "There are", this.toilets, "toilets available") }

    function buy_food     (){ return "food"        }
    function use_restroom (){ this.private_stuff() }

})( window )


var chinese = new Restaurant

console.log( chinese.buy_food()      );  // output: food
console.log( chinese.use_restroom()  );  // output: There are 3 toilets available
console.log( chinese.toilets         );  // output: undefined
console.log( chinese.private_stuff() );  // output: undefined

// and throws: TypeError: Object #<Restaurant> has no method 'private_stuff'

0
Class({  
    Namespace:ABC,  
    Name:"ClassL2",  
    Bases:[ABC.ClassTop],  
    Private:{  
        m_var:2  
    },  
    Protected:{  
        proval:2,  
        fight:Property(function(){  
            this.m_var--;  
            console.log("ClassL2::fight (m_var)" +this.m_var);  
        },[Property.Type.Virtual])  
    },  
    Public:{  
        Fight:function(){  
            console.log("ClassL2::Fight (m_var)"+this.m_var);  
            this.fight();  
        }  
    }  
});  

https://github.com/nooning/JSClass


0

Ich habe ein neues Tool erstellt, mit dem Sie echte private Methoden für den Prototyp https://github.com/TremayneChrist/ProtectJS verwenden können

Beispiel:

var MyObject = (function () {

  // Create the object
  function MyObject() {}

  // Add methods to the prototype
  MyObject.prototype = {

    // This is our public method
    public: function () {
      console.log('PUBLIC method has been called');
    },

    // This is our private method, using (_)
    _private: function () {
      console.log('PRIVATE method has been called');
    }
  }

  return protect(MyObject);

})();

// Create an instance of the object
var mo = new MyObject();

// Call its methods
mo.public(); // Pass
mo._private(); // Fail

1
Können Sie bitte erklären, wie es funktioniert? Wie / wo können Sie die _privateMethode aufrufen ?
Bergi

0

Sie müssen Ihre eigentliche Konstruktorfunktion schließen, um Ihre privaten Methoden zu definieren. Um Daten der Instanzen durch diese privaten Methoden zu ändern, müssen Sie ihnen "dies" geben, entweder als Funktionsargument oder indem Sie diese Funktion mit .apply (this) aufrufen:

var Restaurant = (function(){
    var private_buy_food = function(that){
        that.data.soldFood = true;
    }
    var private_take_a_shit = function(){
        this.data.isdirty = true;   
    }
    // New Closure
    function restaurant()
    {
        this.data = {
            isdirty : false,
            soldFood: false,
        };
    }

    restaurant.prototype.buy_food = function()
    {
       private_buy_food(this);
    }
    restaurant.prototype.use_restroom = function()
    {
       private_take_a_shit.call(this);
    }
    return restaurant;
})()

// TEST:

var McDonalds = new Restaurant();
McDonalds.buy_food();
McDonalds.use_restroom();
console.log(McDonalds);
console.log(McDonalds.__proto__);

Eigentlich funktioniert es nicht. Jeder new Restaurantwird seinen eigenen restaurantKonstruktor haben, und der "Prototyp" wird total missbraucht.
Bergi

@Bergi. Eigentlich hast du recht. Es würde funktionieren, wäre aber auch ein Ressourcenschwein (heißt es so?). Ich habe meine Antwort dazu bearbeitet.
Flex Elektro Deimling

Danke für das Update. Keine Ahnung, wie man die vorherige Version nennt (aber "Bug" :-)
Bergi

0

Ich weiß, dass es etwas zu spät ist, aber wie wäre es damit?

var obj = function(){
    var pr = "private";
    var prt = Object.getPrototypeOf(this);
    if(!prt.hasOwnProperty("showPrivate")){
        prt.showPrivate = function(){
            console.log(pr);
        }
    }    
}

var i = new obj();
i.showPrivate();
console.log(i.hasOwnProperty("pr"));

0

Es gibt bereits viele Antworten auf diese Frage, aber nichts entsprach meinen Bedürfnissen. Also habe ich meine eigene Lösung gefunden, ich hoffe, sie ist für jemanden nützlich:

function calledPrivate(){
    var stack = new Error().stack.toString().split("\n");
    function getClass(line){
        var i = line.indexOf(" ");
        var i2 = line.indexOf(".");
        return line.substring(i,i2);
    }
    return getClass(stack[2])==getClass(stack[3]);
}

class Obj{
    privateMethode(){
        if(calledPrivate()){
            console.log("your code goes here");
        }
    }
    publicMethode(){
        this.privateMethode();
    }
}

var obj = new Obj();
obj.publicMethode(); //logs "your code goes here"
obj.privateMethode(); //does nothing

Wie Sie sehen können, funktioniert dieses System, wenn diese Art von Klassen in Javascript verwendet wird. Soweit ich herausgefunden habe, hat keine der oben kommentierten Methoden dies getan.


1
Neugierig: Mussten Sie die Funktion wirklich verfügbar machen, sie aber zur Laufzeit zu einem No-Op machen - anstatt sie wie alle / die meisten anderen Antworten vor externen Anrufern zu verbergen? Wenn ja warum? Was ist Ihrer Meinung nach der Vorteil dieses Ansatzes? Für mich scheint dies nur ein unnötiger Leistungsaufwand zu sein, eine unklare API und wahrscheinlich eine Hölle des Debuggens, aber ich bin immer offen für neue Perspektiven ...
JHH

2
@JHH um ehrlich zu sein, ich bin ziemlich blass, wenn ich zurückblicke. Der Overhead ist es im Allgemeinen überhaupt nicht wert, obwohl es für mich nicht sehr wichtig war, da ich nicht viele Anrufe bei diesen Klassen tätigte. Der Grund, warum ich es so gemacht habe, ist nur, dass es relativ sauber ist, wie Sie die Funktionen schreiben und aufrufen. Ich habe Symbole und dergleichen zu der Zeit nicht verstanden, aber jetzt, wo ich es tue, denke ich, dass dies im Allgemeinen der richtige Weg ist, wenn ich Klassen benutze. Ich denke darüber nach, diese Antwort alle zusammen zu entfernen. Ich habe einige dumme Antworten gepostet, aber hey, du lebst und lernst.
Thegunmaster

Danke für die Rückmeldung! Ich war mir nicht sicher, ob ich etwas falsch verstanden hatte. Aber ja, wir alle leben und lernen!
JHH

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.