Pregunta

¿Alguien sabe cómo hacer que los miembros privados, no estáticos en CoffeeScript? Actualmente estoy haciendo esto, que sólo utiliza una variable pública a partir de un guión para aclarar que no debe ser usado fuera de la clase:

class Thing extends EventEmitter
  constructor: (@_name) ->

  getName: -> @_name

Poner la variable en la clase hace que sea un miembro estático, pero ¿cómo puedo hacer que no estático? ¿Es incluso posible sin conseguir "fantasía"?

¿Fue útil?

Solución

¿Es incluso posible sin conseguir "fantasía"?

Es triste decirlo, que tendría que ser fantasía .

class Thing extends EventEmitter
  constructor: (name) ->
    @getName = -> name

Recuerde, "Es sólo JavaScript."

Otros consejos

clases son funciones sólo para que crean ámbitos. todo lo definido dentro de este alcance no será visible desde el exterior.

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 compila esto en lo siguiente:

(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);

Me gustaría mostrar algo aún más elegante

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

Ahora usted puede hacer

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

Hay un problema con la respuesta de Vitaly y que es que no se puede definir variables que desea ser único para el alcance, si ha cometido un nombre privado de esa manera y luego lo cambió, el valor del nombre cambiaría para cada instancia de la clase, por lo que es una manera podemos resolver ese problema

# 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

No es imposible acceder a la matriz los nombres desde el exterior a menos que utilice getNames

Prueba de esto

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']

Compilado 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;
};

Esta es una solución que se basa en varias de las otras respuestas aquí más https://stackoverflow.com/a/7579956/1484513 . Almacena las variables de instancia privada (no estático) en una matriz de clase privada (estática) y utiliza un ID de objeto para saber qué elemento de esa matriz contiene los datos que pertenecen a cada instancia.

# 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

Aquí es el mejor artículo que encontré sobre la configuración public static members, private static members, public and private members, y algunas otras cosas relacionadas. Cubre tanto los detalles y la comparación js vs coffee. Y para los históricos razones aquí está el mejor ejemplo de código de él:

# CoffeeScript

class Square

    # private static variable
    counter = 0

    # private static method
    countInstance = ->
        counter++; return

    # public static method
    @instanceCount = ->
        counter

    constructor: (side) ->

        countInstance()

        # side is already a private variable, 
        # we define a private variable `self` to avoid evil `this`

        self = this

        # private method
        logChange = ->
            console.log "Side is set to #{side}"

        # public methods
        self.setSide = (v) ->
            side = v
            logChange()

        self.area = ->
            side * side

s1 = new Square(2)
console.log s1.area()   # output 4

s2 = new Square(3)
console.log s2.area()   # output 9

s2.setSide 4            # output Side is set to 4
console.log s2.area()   # output 16

console.log Square.instanceCount() # output 2

Aquí es cómo se puede declarar miembros privados, no estáticos en CoffeeScript
Para tener una referencia completa, se puede echar un vistazo a https://github.com/vhmh2005/jsClass

class Class

  # private members
  # note: '=' is used to define private members
  # naming convention for private members is _camelCase

  _privateProperty = 0

  _privateMethod = (value) ->        
    _privateProperty = value
    return

  # example of _privateProperty set up in class constructor
  constructor: (privateProperty, @publicProperty) ->
    _privateProperty = privateProperty

"clase" en los scripts de café conduce a un resultado basado prototipo. Así que incluso si se utiliza una variable privada es compartida entre instancias. Usted puede hacer esto:

EventEmitter = ->
  privateName = ""

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

.. conduce a

emitter1 = new EventEmitter()
emitter1.setName 'Name1'

emitter2 = new EventEmitter()
emitter2.setName 'Name2'

console.log emitter1.getName() # 'Name1'
console.log emitter2.getName() # 'Name2'

Pero tenga cuidado de poner los miembros privados antes de las funciones públicas, porque coffeescript devuelve las funciones públicas como objeto. Mirada a la recopilada Javascript:

EventEmitter = function() {
  var privateName = "";

  return {
    setName: function(name) {
      return privateName = name;
    },
    getName: function() {
      return privateName;
    }
  };
};

Desde coffeescript compila a JavaScript la única manera que puede tener variables privadas es a través de los cierres.

class Animal
  foo = 2 # declare it inside the class so all prototypes share it through closure
  constructor: (value) ->
      foo = value

  test: (meters) ->
    alert foo

e = new Animal(5);
e.test() # 5

Esto compilará abajo a través del siguiente JavaScript:

var Animal, e;
Animal = (function() {
  var foo; // closured by test and the constructor
  foo = 2;
  function Animal(value) {
    foo = value;
  }
  Animal.prototype.test = function(meters) {
    return alert(foo);
  };
  return Animal;
})();

e = new Animal(5);
e.test(); // 5

Por supuesto, esto tiene todas las mismas limitaciones que todas las otras variables particulares se puede tener a través del uso de dispositivos de cierre, por ejemplo, métodos recién añadidos no tienen acceso a ellos, ya que no estaban definidas en el mismo ámbito.

Usted no puede hacerlo fácilmente con clases CoffeeScript, ya que utilizan el patrón constructor de Javascript para crear clases.

Sin embargo, se podría decir algo como esto:

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}"

Sin embargo, se pierde la grandeza de las clases CoffeeScript, porque no se puede heredar de una clase creado de esa manera por cualquier otra manera que mediante el uso extend () de nuevo. instanceof dejará de funcionar, y objecs creado de esta manera consume un poco más de memoria. Además, no se debe utilizar el nueva y Super palabras clave más.

El punto es, que los cierres se deben crear cada vez que se crea una instancia de una clase. Los cierres de miembros de las clases CoffeeScript puras se crean sólo una vez -., Es decir, cuando se construye el "tipo" de tiempo de ejecución de clase

Si desea que sólo memebers particulares separadas de público, simplemente se envuelve en la variable $

$:
        requirements:
              {}
        body: null
        definitions: null

y el uso @$.requirements

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top