Private members in CoffeeScript?

后端 未结 11 2037
太阳男子
太阳男子 2020-12-12 13:37

Does somebody know how to make private, non-static members in CoffeeScript? Currently I\'m doing this, which just uses a public variable starting with an underscore to clari

相关标签:
11条回答
  • 2020-12-12 13:51

    I'd like to show something even fancier

    class Thing extends EventEmitter
      constructor: ( nm) ->
        _name = nm
        Object.defineProperty @, 'name',
          get: ->
            _name
          set: (val) ->
            _name = val
          enumerable: true
          configurable: true
    

    Now you can do

    t = new Thing( 'Dropin')
    #  members can be accessed like properties with the protection from getter/setter functions!
    t.name = 'Dragout'  
    console.log t.name
    # no way to access the private member
    console.log t._name
    
    0 讨论(0)
  • 2020-12-12 13:51

    "class" in coffee scripts leads to an prototype based result. So even if you use a private variable it is shared between instances. You can do this:

    EventEmitter = ->
      privateName = ""
    
      setName: (name) -> privateName = name
      getName: -> privateName
    

    .. leads to

    emitter1 = new EventEmitter()
    emitter1.setName 'Name1'
    
    emitter2 = new EventEmitter()
    emitter2.setName 'Name2'
    
    console.log emitter1.getName() # 'Name1'
    console.log emitter2.getName() # 'Name2'
    

    But be careful to put the private members before the public functions, because coffee script returns the public functions as object. Look at the compiled Javascript:

    EventEmitter = function() {
      var privateName = "";
    
      return {
        setName: function(name) {
          return privateName = name;
        },
        getName: function() {
          return privateName;
        }
      };
    };
    
    0 讨论(0)
  • 2020-12-12 13:52

    There is one problem with Vitaly's answer and that is you cannot define variables that you want to be unique to the scope, if you made a private name that way and then changed it, the name value would change for every single instance of the class, so there is one way we can solve that problem

    # create a function that will pretend to be our class 
    MyClass = ->
    
        # this has created a new scope 
        # define our private varibles
        names = ['joe', 'jerry']
    
        # the names array will be different for every single instance of the class
        # so that solves our problem
    
        # define our REAL class
        class InnerMyClass 
    
            # test function 
            getNames: ->
                return names;
    
        # return new instance of our class 
        new InnerMyClass
    

    It's not impossible to access the the names array from outside unless you use getNames

    Test this out

    test = new MyClass;
    
    tempNames = test.getNames()
    
    tempNames # is ['joe', 'jerry']
    
    # add a new value 
    tempNames.push 'john'
    
    # now get the names again 
    newNames = test.getNames();
    
    # the value of newNames is now 
    ['joe', 'jerry', 'john']
    
    # now to check a new instance has a new clean names array 
    newInstance = new MyClass
    newInstance.getNames() # === ['joe', 'jerry']
    
    
    # test should not be affected
    test.getNames() # === ['joe', 'jerry', 'john']
    

    Compiled Javascript

    var MyClass;
    
    MyClass = function() {
      var names;
      names = ['joe', 'jerry'];
      MyClass = (function() {
    
        MyClass.name = 'MyClass';
    
        function MyClass() {}
    
        MyClass.prototype.getNames = function() {
          return names;
        };
    
        return MyClass;
    
      })();
      return new MyClass;
    };
    
    0 讨论(0)
  • 2020-12-12 13:53

    You can't do it easily with CoffeeScript classes, because they use the Javascript constructor pattern for creating classes.

    However, you could say something like this:

    callMe = (f) -> f()
    extend = (a, b) -> a[m] = b[m] for m of b; a
    
    class superclass
      constructor: (@extra) ->
      method: (x) -> alert "hello world! #{x}#{@extra}"
    
    subclass = (args...) -> extend (new superclass args...), callMe ->
      privateVar = 1
    
      getter: -> privateVar
      setter: (newVal) -> privateVar = newVal
      method2: (x) -> @method "#{x} foo and "
    
    instance = subclass 'bar'
    instance.setter 123
    instance2 = subclass 'baz'
    instance2.setter 432
    
    instance.method2 "#{instance.getter()} <-> #{instance2.getter()} ! also, "
    alert "but: #{instance.privateVar} <-> #{instance2.privateVar}"
    

    But you lose the greatness of CoffeeScript classes, because you can't inherit from a class created that way by any other way than by using extend() again. instanceof will stop working, and objecs created this way consume a little bit more memory. Also, you mustn't use the new and super keywords anymore.

    The point is, that the closures must be created every time a class is instantiated. The member closures in pure CoffeeScript classes are created only once - that is, when the class runtime "type" is constructed.

    0 讨论(0)
  • 2020-12-12 13:56

    Here is a solution that draws on several of the other answers here plus https://stackoverflow.com/a/7579956/1484513. It stores the private instance (non-static) variables in a private class (static) array and uses an object ID to know which element of that array contains the data belonging to each instance.

    # Add IDs to classes.
    (->
      i = 1
      Object.defineProperty Object.prototype, "__id", { writable:true }
      Object.defineProperty Object.prototype, "_id", { get: -> @__id ?= i++ }
    )()
    
    class MyClass
      # Private attribute storage.
      __ = []
    
      # Private class (static) variables.
      _a = null
      _b = null
    
      # Public instance attributes.
      c: null
    
      # Private functions.
      _getA = -> a
    
      # Public methods.
      getB: -> _b
      getD: -> __[@._id].d
    
      constructor: (a,b,@c,d) ->
        _a = a
        _b = b
    
        # Private instance attributes.
        __[@._id] = {d:d}
    
    # Test
    
    test1 = new MyClass 's', 't', 'u', 'v'
    console.log 'test1', test1.getB(), test1.c, test1.getD()  # test1 t u v
    
    test2 = new MyClass 'W', 'X', 'Y', 'Z'
    console.log 'test2', test2.getB(), test2.c, test2.getD()  # test2 X Y Z
    
    console.log 'test1', test1.getB(), test1.c, test1.getD()  # test1 X u v
    
    console.log test1.a         # undefined
    console.log test1._a        # undefined
    
    # Test sub-classes.
    
    class AnotherClass extends MyClass
    
    test1 = new AnotherClass 's', 't', 'u', 'v'
    console.log 'test1', test1.getB(), test1.c, test1.getD()  # test1 t u v
    
    test2 = new AnotherClass 'W', 'X', 'Y', 'Z'
    console.log 'test2', test2.getB(), test2.c, test2.getD()  # test2 X Y Z
    
    console.log 'test1', test1.getB(), test1.c, test1.getD()  # test1 X u v
    
    console.log test1.a         # undefined
    console.log test1._a        # undefined
    console.log test1.getA()    # fatal error
    
    0 讨论(0)
  • 2020-12-12 14:05

    classes are just functions so they create scopes. everything defined inside this scope won't be visible from the outside.

    class Foo
      # this will be our private method. it is invisible
      # outside of the current scope
      foo = -> "foo"
    
      # this will be our public method.
      # note that it is defined with ':' and not '='
      # '=' creates a *local* variable
      # : adds a property to the class prototype
      bar: -> foo()
    
    c = new Foo
    
    # this will return "foo"
    c.bar()
    
    # this will crash
    c.foo
    

    coffeescript compiles this into the following:

    (function() {
      var Foo, c;
    
      Foo = (function() {
        var foo;
    
        function Foo() {}
    
        foo = function() {
          return "foo";
        };
    
        Foo.prototype.bar = function() {
          return foo();
        };
    
        return Foo;
    
      })();
    
      c = new Foo;
    
      c.bar();
    
      c.foo();
    
    }).call(this);
    
    0 讨论(0)
提交回复
热议问题