Кто-нибудь знает, как сделать частные нестатические члены в CoffeeScript? В настоящее время я делаю это, просто используя общедоступную переменную, начинающуюся с подчеркивания, чтобы пояснить, что ее не следует использовать вне класса:
class Thing extends EventEmitter
constructor: (@_name) ->
getName: -> @_name
Включение переменной в класс делает его статическим членом, но как я могу сделать его нестатическим? Можно ли вообще без "фантазии"?
name
виден только внутри закрытия конструктора. Взгляните на эту суть: gist.github.com/803810@getName = -> name
похоже, нарушает любое возможное наследованиеgetName
функции.getName
он общедоступен иname
доступен только из функции-конструктора, поэтому на самом деле он не является «частным» для объекта.классы - это просто функции, поэтому они создают области видимости. все, что определено внутри этой области, не будет видно снаружи.
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 компилирует это в следующее:
(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);
источник
foo.call(this)
,this
чтобы быть экземпляром функции. Вот почему попытка эмулировать классическое наследование в JavaScript становится непростой задачей.var
объявления, которые затенят их.Я бы хотел показать что-то более красивое
class Thing extends EventEmitter constructor: ( nm) -> _name = nm Object.defineProperty @, 'name', get: -> _name set: (val) -> _name = val enumerable: true configurable: true
Теперь ты можешь сделать
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
источник
Есть одна проблема с ответом Виталия, и это то, что вы не можете определить переменные, которые должны быть уникальными для области, если вы сделали частное имя таким образом, а затем изменили его, значение имени изменится для каждого экземпляра класса, так что есть один способ решить эту проблему
# 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
Возможно получить доступ к массиву имен извне, если вы не используете
getNames
Проверить это
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']
Скомпилированный 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; };
источник
Вот решение, основанное на нескольких других ответах здесь, а также на https://stackoverflow.com/a/7579956/1484513 . Он хранит переменные частного экземпляра (нестатические) в массиве частного класса (статический) и использует идентификатор объекта, чтобы узнать, какой элемент этого массива содержит данные, принадлежащие каждому экземпляру.
# 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
источник
Вот лучшая статья я нашел информацию о настройке
public static members
,private static members
,public and private members
, и некоторых других связанных вещах. Она охватывает много деталей иjs
противcoffee
сравнения. И по историческим причинам вот лучший пример кода из него:# 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
источник
Вот как вы можете объявить частные нестатические члены в Coffeescript.
Для полной справки вы можете взглянуть на 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
источник
«класс» в сценариях кофе приводит к результату, основанному на прототипе. Таким образом, даже если вы используете частную переменную, она распределяется между экземплярами. Ты можешь это сделать:
EventEmitter = -> privateName = "" setName: (name) -> privateName = name getName: -> privateName
.. приводит к
emitter1 = new EventEmitter() emitter1.setName 'Name1' emitter2 = new EventEmitter() emitter2.setName 'Name2' console.log emitter1.getName() # 'Name1' console.log emitter2.getName() # 'Name2'
Но будьте осторожны, чтобы поместить частные члены перед общедоступными функциями, потому что сценарий coffee возвращает общедоступные функции как объект. Взгляните на скомпилированный Javascript:
EventEmitter = function() { var privateName = ""; return { setName: function(name) { return privateName = name; }, getName: function() { return privateName; } }; };
источник
Поскольку сценарий coffee компилируется до JavaScript, единственный способ получить частные переменные - это замыкания.
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
Это будет скомпилировано с помощью следующего 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
Конечно, это имеет те же ограничения, что и все другие частные переменные, которые вы можете иметь с помощью замыканий, например, недавно добавленные методы не имеют к ним доступа, поскольку они не были определены в той же области.
источник
e = new Animal(5);f = new Animal(1);e.test()
один предупреждает, я хочу пять.С классами CoffeeScript это сделать нелегко, потому что они используют шаблон конструктора Javascript для создания классов.
Однако можно сказать что-то вроде этого:
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}"
Но вы теряете величие классов CoffeeScript, потому что вы не можете наследовать от класса, созданного таким образом, никаким другим способом, кроме как снова использовать extend (). instanceof перестанет работать, а созданные таким образом объекты потребляют немного больше памяти. Также нельзя использовать новые и супер ключевые слова .
Дело в том, что замыкания должны создаваться каждый раз, когда создается экземпляр класса. Замыкания членов в чистых классах CoffeeScript создаются только один раз - то есть, когда создается «тип» среды выполнения класса.
источник
Если вы хотите отделить только приватных участников от публичных, просто оберните их в $ variable
$: requirements: {} body: null definitions: null
и использовать
@$.requirements
источник