#152 ✓wont_fix
Will Palmer

Implement private variables for classes

Reported by Will Palmer | June 8th, 2008 @ 03:59 AM

Class.create() is a much prettier way to define things, but unfortunately the way it's set up makes declaring "private" variables impossible. Maybe I'm just idealistic? I gave it some thought, and here's what I came up with:

If the first argument to a method is named $private, instance-local private variables will be passed in automatically. The method itself is curried, so this is only at define-time that you need to worry about it.

SomeClass.addMethods({
  foo: function($private) {
    return 'I am a public method, but I reveal: ' + $private.private_method();
  }
});

Private methods/properties can be added directly using Object.extend($private, {}), or through the class methods:

SomeClass.addPrivateProperties({});

and

SomeClass.addPrivateMethods({});

The distinction between the two is that functions passed into addPrivateProperties will be treated as initializers for the property values, and will be automatically called just before the initialize() method, passing in the arguments which the initializer will receive (and of course $private, if requested)

private methods / properties are inherited as would be expected, as are methods referencing $private.

This has been tested non-thoroughly by me, but does seem to work without error in firefox at least. I think it could do with some feedback regarding its utility and interface before polish is added.

More than anything, I'm looking for feedback as to whether this is even a good idea / how it can be improved. (new variable names are definitely needed.. one place has three hashes in a row named variations of private_properties). It may also want optimizations regarding speed, as .argumentNames() is called a lot.

patch against b1dd9357a8b08b3e730cc4911cd79a745d22597d

Comments and changes to this ticket

  • Juriy Zaytsev

    Juriy Zaytsev June 8th, 2008 @ 05:15 AM

    • State changed from “new” to “enhancement”

    Privatizing instance and "class methods" is actually quite possible with a simple closure : )

    var Foo = Class.create((function(){
      ...
      // private members go here
      ...
      return {
        initialize: function(name) {
          // and they are accessible in instance methods through the closure
        },
        // ...
      }
    })());
    

    Couple of notes:

    1) I'm concerned about performance - storing private methods in a closure ("around" instance methods as shown above) is not too verbose and brings no speed/size overhead.

    2) They are not really "private". One could still read them (though not modify as it looks like they are early bound with #curry)

    3) How does $private being first argument play with $super being first argument?

    4) Would be helpful to have some comments in the code : )

    Cheers,

    kangax

  • Juriy Zaytsev

    Juriy Zaytsev June 8th, 2008 @ 05:21 AM

    It looks like it's possible to modify private methods:

    var Foo = Class.create({
      initialize: function(name) {
        //
      },
      getSecret: function($private) {
        return $private.secret();
      }
    })
    
    Foo.addPrivateMethods({
      secret: function(){ return 'it\'s a secret, don\'t you get it?' }
    })
    
    Foo.private_instance_properties.secret.value = function(){ 
      return 'spoofed'; 
    };
    
    new Foo().getSecret(); // "spoofed"
    
  • Will Palmer

    Will Palmer June 8th, 2008 @ 01:49 PM

    In response to your comments, in order:

    (closure in initial creation)

    Unfortunately, this would get rid of the prettiness benefits of using Class.create / SomeClass.addMethods(), as methods added later on would still not be able to access the private members. Still, point taken: my changes aren't necessary for the effect.

    (interaction with $super)

    I can only assume it doesn't play nicely. I couldn't figure out what that one was actually doing, so I couldn't make sure it was compatible. (my assumption is it is not)

    (modification of private initializers)

    I don't see this as a problem, as you can only modify them for the class. itself, not for instances. This allows the class to remain flexible, while keeping the values in the instance private.

    In short: I don't really see a distinction between being able to call Foo.addPrivateMethods() at any time, and being able to do so at any time through a lower-level.

    In the mean-time, I'll just use the closure method unless there's more interest in this way.

  • Will Palmer

    Will Palmer June 8th, 2008 @ 01:50 PM

    Oh yes, and the lack of comments is due to my not seeing any in prototype to begin with - I expected when I got the git repository, there would be comments everywhere which got automatically stripped out when doing a rake dist. But there are no comments to be found! What's up with that? :/

  • Will Palmer

    Will Palmer June 8th, 2008 @ 02:30 PM

    And now I've come to realize that the closure-method is not instance-local:

    var Foo = Class.create((function(){
      var private_member;
      return {
        initialize: function(name) {
          private_member = 'foo';
        },
        get_private: function() { return private_member; },
        set_private: function(new_val) { private_member = new_val; }
      }
    })());
    
    foo = new Foo();
    document.write('foo: ' + foo.get_private() + '<br />');
    foo.set_private('bar');
    document.write('[set foo to "bar"]<br />');
    document.write('foo: ' + foo.get_private() + '<br />'); //bar
    bar = new Foo();
    document.write('[bar = new Foo()]<br />');
    document.write('foo: ' + foo.get_private() + '<br />'); //foo
    document.write('bar: ' + bar.get_private() + '<br />'); //foo
    bar.set_private('baz');
    document.write('[set bar to "baz"]<br />');
    document.write('bar: ' + bar.get_private() + '<br />'); //baz
    document.write('foo: ' + foo.get_private() + '<br />'); //baz
    

    is there something I'm overlooking?

  • Juriy Zaytsev

    Juriy Zaytsev June 8th, 2008 @ 08:33 PM

    Will,

    unfortunately, when a closure is associated with constructor's prototype, it's obviously shared between all instance members. That means that private members act as "static", not instance (local).

    To make private instance members, one would need to define them in constructor (not in constructor's prototype) as direct properties. That obviously leads to performance decrease, as every instance creates and carries its private instance methods (rather than sharing them via a prototype chain).

    var Person = Class.create({
      initialize: function(name) {
        // private instance property
        var name = name;
        // private instance methods
        this.getName = function(){ return name }; 
        this.setName = function(value){ name = value };
      }
    });
    
    var foo = new Person();
    foo.name; // undefined (truly private)
    foo.setName('Joe');
    foo.getName(); // 'Joe'
    
    var bar = new Person();
    bar.setName('John');
    bar.getName(); // 'John'
    
    foo.getName(); // 'Joe'
    
  • Will Palmer

    Will Palmer June 9th, 2008 @ 01:18 AM

    Alright, that's as I thought. So basically the long and short of it is: You can't use prototype's prettier-ways of defining methods if you want private members, you have to do it the old-fashioned way (with no inheritence, etc)

    So then this patch is /not/ re-doing things which are already possible, though it may be doing them in ways which are convoluted, slow, and not worth it.

    Might this be able to be cleaned up in such a way that it adds little to no overhead (at least unless its features are actually used?) I'll probably keep working at it.

  • Tobie Langel

    Tobie Langel July 23rd, 2009 @ 04:04 AM

    • State changed from “enhancement” to “wont_fix”
    • Tag set to needs_tests

    This ticket has been inactive for a very long time. I also believe this level of complexity is not necessary in Prototype itself. Closing as wont_fix.

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 02:26 AM

    • Tag changed from needs_tests to missing:tests

    [not-tagged:"needs_tests" tagged:"missing:tests" bulk edit command]

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 03:36 AM

    • Tag changed from missing:tests to needs:tests

    [not-tagged:"missing:tests" tagged:"needs:tests" bulk edit command]

  • cdesign (at btinternet)

    cdesign (at btinternet) February 16th, 2016 @ 11:50 PM

    • Importance changed from “” to “”

    Has this been implemented into prototype.js 1.7...it looks really excellent. Well done..

    Thanks Charlie

  • cdesign (at btinternet)

    cdesign (at btinternet) February 17th, 2016 @ 12:18 AM

    If it hasn't been merged into the current release prototype 1.7.3, how can I patch my current version?
    Where would I add the above patch?

    I am really looking forward to using these enhancements.
    I love the clean elegant methods & properties, like $private...

    Thanks

    Charlie

  • cdesign (at btinternet)

    cdesign (at btinternet) February 17th, 2016 @ 11:43 PM

    I have just scanned prototype 1.7.3, and this patch has not been merged. Is there anyway, we can implement these changes by the next minor version upgrade?

    Thanks again...

    Charlie

  • cdesign (at btinternet)

    cdesign (at btinternet) February 17th, 2016 @ 11:49 PM

    Tobie Langel. I do not agree with your view. I think this extension of the prototype class inheritance framework would be very valuable, and bring it in line with more traditional OO languages.
    There must be some way of creating private methods, using the same kind of methodology as is used with subclassing.

    Please can we implement this. I have looked carefully at the patch and it looks pretty much there...

    Thanks

    Charlie

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile ยป

The Prototype JavaScript library.

Shared Ticket Bins

Attachments

Pages