Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
66 views
in Technique[技术] by (71.8m points)

How to implement C# access modifiers in javascript?

  • 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());
    
question from:https://stackoverflow.com/questions/21126505/how-to-implement-c-sharp-access-modifiers-in-javascript

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...