Méthodes privées JavaScript


Answers

Vous pouvez simuler des méthodes privées comme ceci:

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

Plus d'informations sur cette technique ici: http://webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html

Question

Pour créer une classe JavaScript avec une méthode publique, je ferais quelque chose comme:

function Restaurant() {}

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

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

De cette façon, les utilisateurs de ma classe peuvent:

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

Comment créer une méthode privée qui peut être appelée par les méthodes buy_food et use_restroom mais pas par les utilisateurs de la classe?

En d'autres termes, je souhaite que l'implémentation de ma méthode soit capable de faire:

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

Mais cela ne devrait pas fonctionner:

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

Comment puis-je définir private_stuff comme une méthode privée, donc les deux sont vraies?

J'ai lu l'écriture de Doug Crockford à quelques reprises, mais il ne semble pas que les méthodes «privées» puissent être appelées par des méthodes publiques et que les méthodes «privilégiées» puissent être appelées de l'extérieur.




There are many answers on this question already, but nothing fitted my needs. So i came up with my own solution, I hope it is usefull for someone:

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

As you can see this system works when using this type of classes in javascript. As far as I figured out none of the methods commented above did.




Here's what i enjoyed the most so far regarding private/public methods/members and instantiation in javascript:

here is the article: http://www.sefol.com/?p=1090

and here is the example:

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/




Personnellement, je préfère le modèle suivant pour créer des classes en 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(); }
        ...
    };
})();

Comme vous pouvez le voir, il vous permet de définir à la fois les propriétés de classe et les propriétés d'instance, chacune pouvant être publique et privée.

Démo

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

Voir aussi ce violon .




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

Semblable à georgebrock, mais un peu moins verbeux (IMHO) Des problèmes à le faire de cette façon? (Je ne l'ai jamais vu nulle part)

edit: J'ai réalisé que c'est un peu inutile car chaque instanciation indépendante possède sa propre copie des méthodes publiques, ce qui sape l'utilisation du prototype.




C'est ce que j'ai travaillé:

A besoin d'une classe de code de sucre que vous pouvez trouver ici . Prend également en charge les éléments protégés, hérités, virtuels, statiques ...

;( 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'



Toute cette fermeture vous coûtera. Assurez-vous de tester les implications de vitesse en particulier dans IE. Vous trouverez que vous êtes mieux avec une convention de nommage. Il y a encore beaucoup d'internautes qui sont obligés d'utiliser IE6 ...




Je préfère stocker des données privées dans un associé WeakMap. Cela vous permet de garder vos méthodes publiques sur le prototype où ils appartiennent. Cela semble être le moyen le plus efficace de gérer ce problème pour un grand nombre d'objets.

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



Puisque tout le monde postait ici son propre code, je vais le faire aussi ...

J'aime Crockford parce qu'il a introduit de vrais modèles orientés objet en Javascript. Mais il est également venu avec un nouveau malentendu, le "ça".

Alors pourquoi utilise-t-il "that = this"? Cela n'a rien à voir avec des fonctions privées. Cela a à voir avec les fonctions internes!

Parce que, selon Crockford, c'est un code buggé:

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

Alors il a suggéré de faire ceci:

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

Donc, comme je l'ai dit, je suis tout à fait sûr que Crockford a eu tort d'expliquer cela et cela (mais son code est certainement correct). Ou était-il juste en train de tromper le monde Javascript, pour savoir qui copie son code? Je ne sais pas ... je ne suis pas un navigateur geek; D

MODIFIER

Ah, c'est tout ce qui compte: Qu'est-ce que 'var that = this'? signifie en JavaScript?

Donc, Crockie avait vraiment tort avec son explication ... mais juste avec son code, donc il est toujours un bon gars. :))




Dans ces situations, lorsque vous avez une API publique et que vous souhaitez utiliser des méthodes / propriétés privées et publiques, j'utilise toujours le modèle de module. Ce modèle a été rendu populaire dans la bibliothèque YUI, et les détails peuvent être trouvés ici:

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

C'est vraiment simple, et facile à comprendre pour les autres développeurs. Pour un exemple simple:

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



I have created a new tool to allow you to have true private methods on the prototype https://github.com/TremayneChrist/ProtectJS

Exemple:

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



J'ai évoqué ceci: EDIT: En fait, quelqu'un a lié à une solution identique. 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!'



Envelopper tout le code dans la fonction anonyme: Ensuite, toutes les fonctions seront privées, seules les fonctions attachées à l'objet window :

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

Utilisez ceci :

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

FIDDLE




Si vous voulez la gamme complète de fonctions publiques et privées avec la possibilité pour les fonctions publiques d'accéder à des fonctions privées, code de disposition pour un objet comme ceci:

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