Protected Protocol

David Bruant bruant.d at gmail.com
Wed Apr 4 14:08:06 PDT 2012


As a followup, I figured a way to repair prototype equality (and native
instanceof) and updated the gist [1].

Please note the addition of EvilPerson and EvilCompSavvyPerson.
Interestingly, with the exact same declaration, I was able to make a
"subclass" of 2 different "classes" in 2 lines. It was not a planned use
case and I don't know to what extent it can be used in real life, but it
helped me to easily find "secret" leaks while I was working on repairing
prototype equality.

David

[1] https://gist.github.com/2279059

Le 02/04/2012 00:28, David Bruant a écrit :
> Hi,
>
> A while ago, I posted a challenge on es-discuss [1]. It was a challenge
> related to code reuse, modularity and composition. There has been an
> interesting suggestion [2] [3] which left me a bit unsatisfied since
> relying on constructor arguments while it may not be always possible to
> do that.
> What I describe as the "protected protocol" is my response to my own
> challenge.
> Before explaining it, I'd like to take a step back and discuss about the
> object oriented properties of JavaScript.
>
>
> Two expected properties of object-oriented languages are encapsulation
> and inheritance.
>
> Encapsulation allows to define objects which can only be interacted with
> through a given an interface, thus hiding implementation details. In the
> long run, encapsulation makes the maintenance of components easier since
> the implementation of an object can be changed without affecting its
> clients.
>
> Encapsulation can be implemented in JavaScript. One usual way is to do
> as follow:
>
>     function Storage(){
>         var array = Array.prototype.slice.call(arguments, 0);
>         
>         return {
>             add: function(e){
>                 array.push(e);
>             },
>             read: function(i){
>                 return array[+i];
>             }
>         };
>     }
>
> Here, access to the array is restricted to the interface ("add" and
> "read" methods).
>
>
> Inheritance allows to better reuse code by specializing classes.
> This can be implemented as well, usually with this pattern:
>
>     function Storage(){
>         this.array = Array.prototype.slice.call(arguments, 0);
>     }
>     
>     Storage.prototype = {
>         add: function(e){
>             this.array.push(e);
>         },
>         read: function(i){
>             return this.array[+i];
>         }
>     };
>
>
>     function StorageWithDeletion(){
>         Storage.call(this); // equivalent to "super";
>     }
>     
>     StorageWithDeletion.prototype = Object.create(Storage.prototype);
>     StorageWithDeletion.prototype.delete = function(i){
>         delete this.array[+i];
>     };
>     
>
> To define a storageWithDeletion, there is no need to redefine the add
> and read method. To extend Storage, StorageWithDeletion here only needs
> access to the Storage function in the global scope (or Storage module
> when these will be deployed). This has the nice property that Storage
> and StorageWithDeletion can be defined in different files since they
> don't need to share a common function scope
>
> I have made obvious by juxtaposing both examples that JavaScript
> requires from the programmer to choose between encapsulation and
> inheritance. Indeed, as demonstarted above, encapsulation requires a
> shared scope.
>
> Solutions where Storage and StorageWithDeletion would be defined with a
> shared scope could be explored, but it would require to define both in
> the same file, which, I don't want to be a necessity.
>
> The trick of the protected protocol is that encapsulation does not
> really require a shared scope, it's just the most natural way to do it
> in JavaScript.
>
> When a method located in the prototype chain of an object is called, its
> 'this' references the object from which the method has been called.
> Programmers have naturally stored the state of an object as own (public)
> properties of the object as it's the most natural way to bind
> information to an object.
> It doesn't have to be this way, though. WeakMaps offer an
> straightforward API to bind information to an object and it is not
> necessary to share this WeakMap with the rest of the world.
>
>
> The protected protocol and an example of how to use it can be found at
> [4] (which is a bit different than a version I tweeted earlier [5]).
>
> The most important part in my opinion is that the Person.js and
> ComputerSavvyPerson.js are defined in 2 different files. The only thing
> the latter needs is a reference to the constructor it inherits from
> (here, Person).
>
> Things that are expected to be secret can be kept as such. First, a
> Person's secret doesn't leak, second, even a ComputerSavvyPerson's
> secret doesn't leak. A bit more tricky was to make that defining a new
> subclass doesn't leak secrets of the base class.
>
> In my opinion, the boilerplate code for this to work is minimal enough,
> but I'm interesting on feedback on this aspect. Regardless, the code of
> the constructor and of the prototype methods are clean of boilerplate
> code and only require to use the "Protected(this)" convention when
> intending to work with private-to-the-inheritance-tree parts of the
> object-state.
>
> The implementation of the 2 necessary functions take 60 lines which is
> rather reasonable.
>
> So all the properties I expected are here. On the negative side,
> prototype object equality had to be broken for the sake of preventing
> leaks of "protected" properties, so instanceof is necessarily broken. I
> think it's not a big deal since I could reimplement one myself if really
> necessary.
>
> It's ready for private names and these would need to be defined inside
> the "declaration code" in order for them not to leak outside.
> I intuit that assuming class syntax can be embedded in a function, I
> could wrap it and add support for the Protected convention if I want to.
>
> Anecdotes:
> 1) In Person.js, I've been burned by non-lexical this when writing the
> code that increased the age over time. We really need functions with
> lexical this :-)
> 2) After some feedback by Brendan on Twitter, I've created a
> WeakMap.prototype.getset function following a discussion we had on
> es-discuss about setting a value when there is not already one.
>
>
> I'm interested in any feedback, opinions or questions you'd have.
>
> Thanks,
>
> David
>
> [1] https://mail.mozilla.org/pipermail/es-discuss/2012-March/021292.html
> [2] https://mail.mozilla.org/pipermail/es-discuss/2012-March/021304.html
> [3] https://gist.github.com/2053624
> [4] https://gist.github.com/2279059
> [5]
> https://gist.github.com/c7d4947b41936680810d/a531bd4412b67dc0fed180d1d776314bf8c4747b



More information about the es-discuss mailing list