42
  • Summary

    I tried to achieve inheritance and encapsulation properly in javascript like it was in a class-based language such as c#.

    The ugly part is the protected members have multiple copies in the private instances which are only accessible via closure, and I don't have an idea except refreshing those members to the private instances.

    If it is possible, I want to get rid of both transmit and transfer in my code of Function.extend.

  • Update For people who are interested in citing or research, here's the source code repository:

    https://github.com/kenkins/Function.extend

  • The story

    Since assemblies may be a concept which is out of range of javascript, I don't take the internal modifier into account, but public, protected and private.

    public and private modifiers are not that difficult to achieve; but with inheritance, protected is significantly tricky. Yet it's not a recommended thing to do with javascript, most of articles I've read says prefix with a special character and document it.

    But it seems I'm persisted to make javascript to simulate class-based languages .. I stole this idea and implemented in my way, the code is at rear of this post.

    The idea behind the scene is to put higher accessibility with a higher prototype and access the highest one with a closure.

    Say we have three prototypes A, D and G, it looks like

    BANxt.png

    As it is not possible that an object is an instance of a type also of another type which is not in the prototype chain; the way I chosen is to chain the protected level horizontally and copy the members from the prototype of the declaring type. This makes nesting class possible, because the members declared on a less-derived type can be propagated to more-derived types; the transmit method in my code is to do this. If A, D and G have their own protected members, it would look like:

    bhcsI.png

    The closure for accessing the private instance, is this['']. It takes an argument which is for identifying a class. The modifiers holder is just the class identifier, named y in Function.extend and _ in the test code, it should not be exposed outside the class declaration. It is also used as a shortcut of this[''].

    _['base'] is in fact not only the base constructor invoker, but also the private instances creator. It creates the private instances and updates this[''] for each constructor with the inheritance, so it should always be called in the constructors.

    Although a private instance would have the access of the public members, it should not be used to alter them, since this[''] is not guaranteed to be invoked when accessing public members. But the accessing of private instance is; recent remembers the most recently accessed private instance, and update the protected members if there're changes.

    My question is, how can I get rid of this kind of refreshing the protected members? Are there better ideas to achieve the encapsulation more of the realistic?

    p.s.: I actually do not want a solution which uses non-standard methods/properties .. and it would be better there're polyfills if the used methods/properties are too fashion to the old browsers.


  • Function.extend

    Function.extend=function(base, factory) {
        factory.call(initializeClass);
        updateStaticMembersOfDerivedInnerClasses(y['public'].constructor);
        transfer(y['protected'], y['public']);
        return y['public'].constructor;
    
        function y($this) {
            return $this[''](y);
        }
    
        function transfer(target, source, descriptor) {
            if(target!==source?
                'undefined'!==typeof target?
                    'undefined'!==typeof source:
                        false:false) {
                var keys='undefined'!==typeof descriptor? descriptor:source;
    
                for(var key in keys) {
                    if(Object.prototype.hasOwnProperty.call(source, key)) {
                        target[key]=source[key];
                    }
                }
            }
        }
    
        function updateStaticMembersOfDerivedInnerClasses(outer) {
            var member, inner;
    
            for(var key in outer) {
                if(Object.prototype.hasOwnProperty.call(outer, key)?
                    (member=outer[key]) instanceof outer?
                        outer!==(inner=member.constructor):
                            false:false) {
                    transfer(inner, outer);
                }
            }
        }
    
        function initializeInstance() {
            var $this=Object.create(y['private']);
            var derivedGet=this[''];
            var recent=$this;
    
            this['']=function(x) {
                var value=y!==x? derivedGet.call(this, x):$this;
    
                if(value!==recent) {
                    transfer(value, recent, x['protected']);
                    recent=value;
                }
    
                transfer(value, this);
                return value;
            };
    
            base.apply(this, arguments);
            $this['']=this[''];
        }
    
        function initializeClass(derived) {
            y['public']=Object.create(base.prototype);
            y['public'].constructor=derived;
    
            if(Object.prototype.hasOwnProperty.call(base, 'transmit')) {
                base.transmit(y);
            }
            else {
                y['protected']=Object.create(y['public']);
            }
    
            y['private']=Object.create(y['protected']);
            y['base']=initializeInstance;
            transfer(derived, base);
    
            derived.transmit=function(x) {
                if(x['public'] instanceof derived) {
                    x['protected']=Object.create(y['protected']);
                    x['protected'].constructor=x['public'].constructor;
                }
            };
    
            derived.prototype=y['public'];
            return y;
        }
    };
    
  • test code

    'use strict';
    
    var BaseClass=Function.extend(Object, function () {
        var _=this(BaseClass);
    
        var NestedClass=Function.extend(BaseClass, function () {
            var _=this(NestedClass);
    
            function NestedClass(x, y, z) {
                _['base'].apply(this, arguments);
                _(this).Y=y;
                _(this).Z=z;
            }
    
            _['public'].SetX=function (x) {
                _(this).InternalSetX(x);
            };
    
            _['public'].GetX=function () {
                return _(this).InternalGetX();
            };
    
            _['public'].GetY=function () {
                return _(this).Y;
            };
    
            _['public'].SetZ=function (z) {
                _(this).Z=z;
            };
    
            _['public'].GetZ=function () {
                return _(this).Z;
            };
    
            _['private'].Y=0;
        });
    
        function BaseClass(x) {
            _['base'].apply(this, arguments);
            _(this).X=x;
        }
    
        _['protected'].InternalSetX=function (x) {
            _(this).X=x;
        };
    
        _['protected'].InternalGetX=function () {
            return _(this).X;
        };
    
        _['private'].X=0;
        _['protected'].Z=0;
    
        BaseClass.Sample=new NestedClass(1, 2, 3);
    });
    
    var DerivedClass=Function.extend(BaseClass, function () {
        var _=this(DerivedClass);
    
        function DerivedClass(x, y, z) {
            _['base'].apply(this, arguments);
        }
    });
    
    var o=DerivedClass.Sample;
    alert(o.GetX());
    alert(o.GetY());
    alert(o.GetZ());
    o.SetX(3);
    o.SetZ(1);
    alert(o.GetX());
    alert(o.GetY());
    alert(o.GetZ());
    
Ken Kin
  • 4,503
  • 3
  • 38
  • 76
  • Have you considered cross-compiler like Script# instead? Also consider if supporting `protected` actually worth it - "by convention" is generally enough for friendly teams, strict private/public is better for external users (http://www.parashift.com/c++-faq-lite/protected-data-not-evil.html)... – Alexei Levenkov Jan 15 '14 at 00:12
  • 10
    It looks like TypeScript supports access modifiers, if that interests you, though admittedly not protected. – Magus Jan 15 '14 at 00:28
  • 3
    No, it more like if you want write strongly typed code and still be able to use it with browser - C# -> JS may be more practical route. Side note: so far your nice and long question feels missing practical value, so some may consider it to be off-topic for SO. In any case 2-3 sentence summary in the beginning could make it better against TL;DR cases... – Alexei Levenkov Jan 15 '14 at 00:28
  • @AlexeiLevenkov: Yep, I've thought about `TL;DR` .. but not yet an idea of how to `TL;DR` it .. – Ken Kin Jan 15 '14 at 00:33
  • @KenKin: Interesting. Of course, since it also includes interfaces, if you're of the 'has a is always better than is a' variety, it'd be fine. I'm not sure I've reached quite that level, but a language which forces it to some extent sounds intriguing. – Magus Jan 15 '14 at 00:57
  • 1
    @AlexeiLevenkov: How do you think my revision of the `TL;DR` .. ? – Ken Kin Jan 15 '14 at 01:28
  • 11
    Is this for practical use, or just trying something fun? (If it’s the former, my suggestion would be not to try to force other programming languages’ idioms into JavaScript, as they don’t fit.) – Ry- Jan 16 '14 at 20:19
  • @minitech: I wish to put it into *for practical use*. But as there are still cons, I'd like to improve it and make it come true. – Ken Kin Jan 16 '14 at 20:23
  • you can use a getter pointing at a WeakMap to secretly store values between instances. – dandavis Jan 22 '14 at 02:17
  • @dandavis: MDN says it's an experimental technology .. but if you have an idea, maybe make it an answer? – Ken Kin Jan 22 '14 at 22:37
  • 1
    That is one of the best questions that i have seen on this site. – Paulo Roberto Rosa Jan 23 '14 at 11:43
  • 1
    @KenKin see my answer, but you will need some time to read it. – Paulo Roberto Rosa Jan 23 '14 at 13:13
  • 1
    You do natively get what is essentially public, private and protected (even static), why would you want to introduce non JS idioms into the language? – cstruter Jan 23 '14 at 13:48
  • 1
    I have a solution that does public, private, and protected along with proper inheritance and encapsulation. It will take me about two hours to make the post. – Blake Regalia Jan 23 '14 at 20:07
  • @BlakeRegalia: Thank you for willing to post your answer. Please also take the accessibility, visibility and the garbage collectability into account, the code I proposed in the question is implemented with these concerns. – Ken Kin Jan 23 '14 at 20:35
  • IMO, your question sounds like an [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) question. It's interesting, but it doesn't have an ultimate solution, which would be practical, efficient and readable. JavaScript is just not designed for that, and that's debatable whether it's a weak or strong part of the language. – noseratio Jan 28 '14 at 04:33
  • @Noseratio: If I understand xy problem correctly, it means asking y for solving x. But I think I asked precisely what I tried to solve. A solution simulates these access should not break the relationship of types in inheritance and encapsulation, and a solution simulates inheritance and encapsulation of a type-based language should make these access be defined like they were in that language. I proposed my code as an example of the implementation to show my research effort on this topic. – Ken Kin Jan 28 '14 at 12:32

5 Answers5

4

I also had a similar thought and decided to try write something. A vanilla js solution. Still early but I like what came out of it. You might find it interesting also.

It's not exactly c# but provides a more strict ecosystem. And some other advanced js features in a lightweight solution.

https://github.com/iamlothian/rucksack.js

This is not a solution to your code, but solution to your concept. If your goal was the get your idea to work then by all means continue as I am interested by the result.

If you like me just want a more structured js environment, then here is one I wrote with similar ambition to your questions concepts.

Part 2:

The idea here is to use closure and access restriction to create a pattern that restricts the way code can be used and changed after is has been defined. For the most part a lot of the hard work has been done. But the pattern is left for you to define.

Here is a quick mock example demonstrating how you might implement a public|protect|private inheritance. I am trying to decide weather i implement some of this as a built in feature or leave it up to users to implement their own object extension like i have in the example.

http://plnkr.co/edit/ao2hTyBV1b3nYIwr7ZS5

The implementation is in scripts.js. view you console to see what is going on.

What rucksack provides is a framework for creating detached modules of code. These modules are grouped into namespaces and can depend on each other. These dependencies are resolved lazily as defined, so that definition order is not really important. The resolution process provides some other useful features such as interfaces matching and sealed module.

current features:

  • Modular
  • Dependency Injection
  • Factory constructor (Instances Object)
  • Service constructor (Static Objects)
  • Lazy loading
  • Easy error logging (All error within modules are captured and can be passed on)
  • Namespaces
  • Sealable modules and namespaces (modules that can't be accessed from outside the namespace)
  • Global await event for module
  • Interface for optional config object
  • Optional strict interface checks for injection
Matthew.Lothian
  • 2,072
  • 17
  • 23
  • It does do more than you suggested, it aims to give developers more control over how their code is used and distributed. While providing similar consepts to a more strictly typed language. – Matthew.Lothian Jan 17 '14 at 03:54
  • 1
    As it's quite length I'm really sorry that I don't bother to read the full implementation .. maybe you'll extract the part relevant to this question .. ? – Ken Kin Jan 17 '14 at 04:15
  • 1
    As I am between flights on my mobile that will have to wait. I have updated my answer abit with a a short explanation. – Matthew.Lothian Jan 17 '14 at 08:40
  • 1
    You earned the **Round 1** bounty because I'm going to start **Round 2** .. I feel my decision is a bit unfair for other answers and wish that you would improve your answer. – Ken Kin Jan 24 '14 at 00:52
  • Object.freeze() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze may make this easier in the future but it will fail older browsers. Still an interesting addition the the language. – Matthew.Lothian Jan 24 '14 at 05:25
  • hmm sorry, what did you not understand. help me make this a clear as i can. – Matthew.Lothian Jan 24 '14 at 12:55
3

While the code with closure might solve what you want, I would go with the simpler Privileged methods as Crockford called them here.

Usage idea is simple:

  • Define privileged method on the base object (with limit 1 - allows to be called only once).
  • Privileged method returns a protected interface of itself (of a base object) which contains protected functions in it (probably those functions are privately defined in the base, and then get copied over to the protected interface object... or maybe the protected interface exists privately).
  • Each object extends its protected interface with its base object's protected interface and still exposes it through the privileged method.

You will end up with something like this:

function A() {
    var protected = {
        protectedA: function() { }
    };

    this.getProtected = (function() {
        var allow = true;

        //privileged function.
        return function() {
            if (allow) {
                allow = false;
                return protected;
            }
        };
    });
}

//B - derives from (extends) A
function B() {
    var base = {}; //acquiring a base omitted - depends on your implementation.

    var protected = {
        protectedB: function() { }
    };
    //"extend" simply copies new members into protected:
    protected = $.extend(protected, base.getProtected()); 

    this.getProtected = function() { 
        /* privileged function - similar to A.getProtected */ 
    };
}

JavaScript has limited abilities in this extent, so the protected sugar comes with some cost anyway.

Tengiz
  • 8,011
  • 30
  • 39
  • Thank you for answering. But I don't really understand the answer yet .. What would the `extend` method act like and how can I create a pattern from this which is reusable? – Ken Kin Jan 22 '14 at 21:55
  • extend just copies the properties over - just like jQuery.extend. I made an edit to indicate that. However, if you don't use jQuery, you will have to imitate that. As about reusability: your Function.extend must ensure every object has getProtected method. And the base type of your classes can return simply empty object {} as protected members' container. – Tengiz Jan 23 '14 at 15:30
  • I think this makes it less simulate a type-based language. – Ken Kin Jan 23 '14 at 16:26
  • 3
    It's a tradeoff. To me JavaScript is not type-based, it's object-based. At least this way I see things clearly and understand them simpler. As I said, JavaScript is not capable of fully imitating the type-based system anyway. – Tengiz Jan 23 '14 at 16:34
2

Javascript is a wide language, because you can do almost all the things do you want in a webpage, just by creating functions and finding ways to do it.

I can tell you that JavaScript are not a secure language, because you can easily access the most part of variables and functions, read them, and know how it works, just by acessing the .js file, included on the page.

My Tought: Some access modifiers was not created to use in javascript due to developers already know that maybe it can be useless, because JavaScript does not "Travel" into another places(pages), unless you use a session variable.

And about that modifiers:

  • Private

  • Protected

  • Public

I can tell you that i know some javascript modifiers that have some similarity to them, that are:

Local:

var Variable = 0;

Automatically, this is converted into a Integer variable, because it is receiving a Integer value, and too, this is a LOCAL variable because of the var modifier that declare this variable in a way that you cannot access the value of it, unless if you are inside the same function that this variable was declared.

Example:

If you declare these functions this way, with default modifiers:

function conflict(){
  i = 2;
  changeI();
  alert(i);
}
function changeI(){
  i = 0;
}

In this case the i is the same variable for the two functions.

So if you execute conflict(); you will get a alert resulting 0.

BUT, if you declare i using the var modifier:

function conflict(){
  var i = 2;
  changeI();
  alert(i);
}
function changeI(){
  var i = 0;
}

In this case, you have two i variables, because they are restricted to use only inside their function, so if you execute conflict(); now, you will get a alert with value of 2.

Class Variable:

this.Variable = "a";

This variable is automatically a String, because it is receiving a String value, Probably you already know what the this modifier does, but, i'll try to explain with my point of view, that is this variable is coming from the SuperClass or in javascript a "SuperFunction" that can be called a Class, or in other words, the "father" class.

A example:

function TClass()
{
  this.getVar = function()
  {
    try
    {
      return "test";
    }
    catch(err)
    {
      return false;
    }
  }
  this.alertVar = function()
  {
    try
    {
      alert(this.getVar());
    }
    catch(err)
    {
      alert('error');
    }
  }
}
var $Class = new TClass();

As you see above, i created a Class TClass and some variables containing functions into it (javascript closure) and added the modifier this. to them, to make them bound to the TClass and as you see on the alertVar() function, i access on the alert(this.getVar()); the function that are from the TClass that is equal to this in this context.

And this part: var $Class = new TClass(); i am creating the class as you probably knew that, to have access to its methods, doing this i am possible to execute, to test:

$Class.alertVar();

And getting as result, an alertbox containing "test", as you can see:

the test alert

Note that you can't access the TClass methods in another ways, you only can access it creating the class and accessing by it.

So i hope that you did understand the usability of the this modifier.

Global:

window.Variable = true;

Automatically javascript declare this variable is a boolean, because it is receiving a Boolean value. The window modifier as it says, you can access it whatever you are on the window that you are, because javascript variables when declared, they go to the DOM into the window, see what is DOM:

DOM(Document Object Model): The DOM, is a multi-platform that represents how the html,xhtml, and xml markup's are organized and read by the browser that you are using. In other words, if you access the DOM you can see every propery, every variable, or such of thing that exists on the browser at the moment.

Different from another variables, the window variables can have assigned another value and access the actual value, from whatever you are, inside a function or not, inside a js file or not.

Example of Global(window):

Execute on the onLoad event of a page a code that declares a window variable, or declare it yourself using browser console:

window.Variable = true;

Then, add a JS File containing this function, or create it yourself just by executing the code on the browser console:

function testGlobalVar(){
  if (Variable)
    alert("it works!");
}

When you execute testGlobalVar() you will get the alert, but it is only because you declared it as `window´ otherwise, you will get nothing.

Default Modifier:

Variable = 0.5

Automatically this Variable is declared as Float beacuse it is receiving a Float value. I dont know if you already know, but javascript variables declared as the usual way, have a default modifier that makes the variable similar to window variables, but you cannot access it from whatever you are, but in most cases you can acess it, particulary, i dont know all the cases that you cannot access it, but i know that you cant when you loaded a js file and it was declared inside it. Only if you run a function that declares it, and after that try to acesss.

By the way, i see that you want to know modifiers that match the three that you said, but at my tought some of that modifiers that i told you can be used to do the same that your c# modifiers do.

I hope that you understand what i'm saying.

Ah, and if you was confused when you saw a function inside a variable, study Javascript Closures, you will understand after that :).

Paulo Roberto Rosa
  • 3,071
  • 5
  • 28
  • 53
  • Why? You asked about: how to implement c# access modifiers in javascript, and i told you all the existing access modifiers in javascript, so huh – Paulo Roberto Rosa Jan 23 '14 at 16:15
  • 1
    If using these methods that i explained detailed above, you cannot solve your question, then give up. Because there is no other way, as javascript is a nested scope language. – Paulo Roberto Rosa Jan 23 '14 at 16:18
  • Yes, I think you explained in detail. But as far as I know, javascript doesn't have an existing access modifier that equivalent to *protected* in type-base languages. What I'm trying to do is more than that, as I described in the content more detailed than the title, though .. – Ken Kin Jan 23 '14 at 16:56
  • i have read the whole question again, and see.. probably you can't do at the "normal way" but you can do some cheat, like creating a `setInterval();` who keeps setting the default value to the protected variable, this way the value will be always the same but this isnt a good solution, by the way it may be the only one, since you cannot restrict a variable the way that you cannot alter the value... javascript are not restricted. – Paulo Roberto Rosa Jan 23 '14 at 17:38
1

How the parent and child classes interact with each other

  • An extending child class calls super.call, a function that constructs an instance of it's parent.

  • A parent class shares it's protected members (both fields and functions) to it's extending subclass by using this.share in it's constructor.

  • A subclass can also call super.fetch(), which returns the object of fields/functions that the parent class passed to this.share


To illustrate my technique, the following code demonstrates some essentials to OOP with a simple example of a class Dog extends Animal

Some core functions for this object oriented model

// runs in both node.js and browser
var global_namespace = ('undefined'==typeof module)? window: global;

// put a no-operation function in the value for `share` in case nothing is extending a class
var not_extendable = {share:function(){}};

// when something is extending a class...
var extendable = function(constructor) {

    // create a space for protected fields
    var protected_space = {};

    // the following is what will get passed as `this` to the parent constructor
    var sharing = {
        share: function(fields) {
            protected_space = fields;
        },
    };

    // the following is what will get passed as the first arg to the child constructor
    return {

        // enables child to call its parent's constructor
        call: function(args) {
            return constructor.apply(sharing, args);
        },

        // allows child to access protected fields shared by its parent
        fetch: function() {
            return protected_space;
        },
    };
};

Animal class

// class definition for `Animal`
(function(namespace) {

    // construct an instance of this class
    var constructor = function(name, weight, info) {

        // private fields
        var color = (info && info.color) || 'unknown';

        // for protected fields
        var protect = {
            weight: weight,
            noise: function() {
                return 'nothing';
            },
        };

        // share the protected fields with any subclass that might be extending this
        this.share(protect);

        // public fields and methods
        return {
            speak: function() {
                console.log(name+' says '+protect.noise());
            },
            feed: function() {
                console.log(name+' is not hungry');
            },
            weigh: function() {
                console.log(name+' weighs '+protect.weight+' lbs');
            },
            toString: function() {
                return '{Animal}';
            },
        };
    };


    // handle calls to: `Animal()`
    namespace.Animal = function() {

        // called with new operator: `new Animal(...)`
        if(this !== namespace) {

            // construct simple instance of this class
            return constructor.apply(not_extendable, arguments);
        }

        // static call: `Animal(...)`, means the caller wants to extend this class
        else {

            // reference child constructor
            var child_constructor = arguments[0];

            // return a wrapped constructor function
            return function() {

                // call child constructor and allow it to call the super constructor
                return child_constructor.apply({}, [extendable(constructor), arguments]);
            };
        }
    };

})(global_namespace);

Dog class

// class definition for `Dog`
(function(namespace) {

    // class `Dog` extends class `Animal`
    var constructor = Animal(function(super_class, args) {

        // private field
        var been_fed = false;

        // call super's constructor
        var operator = super_class.call(args);

        // inherit parent's protected members
        var parent = super_class.fetch();

        // override a protected method
        parent.noise = function() {
            return 'bark!';
        };

        // override a public method
        operator.feed = function() {
            been_fed = true;
            parent.weight += 5;
        };

        // extend a public method
        var super_weigh = operator.weigh;
        operator.weigh = function() {
            super_weigh();
            if(been_fed) console.log('\t'+args[0]+' has been eating :)');
            else console.log('\t'+args[0]+' has not been fed yet');
        };

        // override another public method
        operator.toString = function() {
            return '{Dog}';
        },

        // return the operator (interfacable instance object)
        return operator;
    });


    // handle calls to: `Dog()`
    namespace.Dog = function() {

        // called with new operator: `new Dog()`
        if(this !== namespace) {
            return constructor.apply(this, arguments);
        }

        // static call: `Dog()`
        else {

            // we do no allow extending class `Dog`
            return false;
        }
    };

})(global_namespace);

So now, we can do this:

var giraffe = new Animal('Mr. Giraffe', 720);
giraffe.speak(); // "Mr. Giraffe says nothing"
giraffe.weigh(); // "Mr. Giraffe weighs 720 lbs"

var buddy = new Dog('Buddy', 50);
buddy.speak(); // "Buddy says bark!"
buddy.weigh(); // "Buddy weighs 50 lbs"
                   //   "Buddy has not been fed yet"
buddy.feed();
buddy.weigh(); // "Buddy weighs 55 lbs"
                   //   "Buddy has been eating :)"

This allows private, protected and public fields/functions. Both protected and public field/functions may be overwritten and extended.

console.log(giraffe); // "{Animal}"
console.log(buddy);   // "{Dog}" 
Blake Regalia
  • 2,677
  • 2
  • 20
  • 29
1

I have been working on another interesting javascript project and implemented something that may be closer to what you are looking for.

Implement.js

Interested in you thoughts.

Matthew.Lothian
  • 2,072
  • 17
  • 23