JavaScript private methods

前端 未结 30 1505
-上瘾入骨i
-上瘾入骨i 2020-11-22 08:16

To make a JavaScript class with a public method I\'d do something like:

function Restaurant() {}

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


        
相关标签:
30条回答
  • 2020-11-22 08:42

    I think such questions come up again and again because of the lack of understanding of the closures. Сlosures is most important thing in JS. Every JS programmer have to feel the essence of it.

    1. First of all we need to make separate scope (closure).

    function () {
    
    }
    

    2. In this area, we can do whatever we want. And no one will know about it.

    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. For the world to know about our restaurant class, we have to return it from the closure.

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

    4. At the end, we have:

    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. Also, this approach has potential for inheritance and templating

    // 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')
    

    I hope this helps someone better understand this subject

    0 讨论(0)
  • 2020-11-22 08:42

    Take any of the solutions that follow Crockford's private or priviledged pattern. For example:

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

    In any case where the attacker has no "execute" right on the JS context he has no way of accessing any "public" or "private" fields or methods. In case the attacker does have that access he can execute this one-liner:

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

    Note that the above code is generic to all constructor-type-privacy. It will fail with some of the solutions here but it should be clear that pretty much all of the closure based solutions can be broken like this with different replace() parameters.

    After this is executed any object created with new Foo() is going to have an eval method which can be called to return or change values or methods defined in the constructor's closure, e.g.:

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

    The only problem I can see with this that it won't work for cases where there is only one instance and it's created on load. But then there is no reason to actually define a prototype and in that case the attacker can simply recreate the object instead of the constructor as long as he has a way of passing the same parameters (e.g. they are constant or calculated from available values).

    In my opinion, this pretty much makes Crockford's solution useless. Since the "privacy" is easily broken the downsides of his solution (reduced readability & maintainability, decreased performance, increased memory) makes the "no privacy" prototype based method the better choice.

    I do usually use leading underscores to mark __private and _protected methods and fields (Perl style), but the idea of having privacy in JavaScript just shows how it's a misunderstood language.

    Therefore I disagree with Crockford except for his first sentence.

    So how do you get real privacy in JS? Put everything that is required to be private on the server side and use JS to do AJAX calls.

    0 讨论(0)
  • 2020-11-22 08:42

    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/

    0 讨论(0)
  • 2020-11-22 08:45

    If you want the full range of public and private functions with the ability for public functions to access private functions, layout code for an object like this:

    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 () {
      };
    }
    
    0 讨论(0)
  • 2020-11-22 08:45
    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" );
    

    Similar to georgebrock but a little less verbose (IMHO) Any problems with doing it this way? (I haven't seen it anywhere)

    edit: I realised this is kinda useless since every independent instantiation has its own copy of the public methods, thus undermining the use of the prototype.

    0 讨论(0)
  • 2020-11-22 08:46

    Don't be so verbose. It's Javascript. Use a Naming Convention.

    After years of working in es6 classes, I recently started work on an es5 project (using requireJS which is already very verbose-looking). I've been over and over all the strategies mentioned here and it all basically boils down to use a naming convention:

    1. Javascript doesn't have scope keywords like private. Other developers entering Javascript will know this upfront. Therefore, a simple naming convention is more than sufficient. A simple naming convention of prefixing with an underscore solves the problem of both private properties and private methods.
    2. Let's take advantage of the Prototype for speed reasons, but lets not get anymore verbose than that. Let's try to keep the es5 "class" looking as closely to what we might expect in other backend languages (and treat every file as a class, even if we don't need to return an instance).
    3. Let's demonstrate with a more realistic module situation (we'll use old es5 and old requireJs).

    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();
                }
                */
            }
        });
    
    0 讨论(0)
提交回复
热议问题