coffeescriptのプライベートメンバー?
-
11-10-2019 - |
質問
誰かがCoffeescriptでプライベートで非静的なメンバーを作る方法を知っていますか?現在、私はこれを行っています。これは、アンダースコアから始まるパブリック変数を使用して、クラスの外で使用すべきではないことを明確にしています。
class Thing extends EventEmitter
constructor: (@_name) ->
getName: -> @_name
クラスに変数を置くと、静的なメンバーになりますが、どうすればそれを非静的にすることができますか? 「空想」を得ずに可能ですか?
解決
「空想」を得ずに可能ですか?
言うのは悲しい、あなたはそうしなければならないだろう はで.
class Thing extends EventEmitter
constructor: (name) ->
@getName = -> name
覚えて、 「それはただのJavaScriptです。」
他のヒント
クラスは単なる関数であるため、スコープを作成します。このスコープ内で定義されているものはすべて、外部からは見えません。
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);
さらにもっとファンシーなものを見せたいです
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
Vitalyの答えには1つの問題があります。 個性的 スコープには、そのように称号を作成してから変更した場合、クラスのすべてのインスタンスで名前の値が変更されるため、その問題を解決できる方法が1つあります。
# 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. 。プライベートインスタンス(非静的)変数をプライベートクラス(静的)配列に保存し、オブジェクトIDを使用して、その配列のどの要素に各インスタンスに属するデータが含まれているかを知ります。
# 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
vs。 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'
ただし、コーヒースクリプトはパブリック機能をオブジェクトとして返すため、パブリックの機能の前にプライベートメンバーを配置するように注意してください。コンパイルされたJavaScriptを見てください:
EventEmitter = function() {
var privateName = "";
return {
setName: function(name) {
return privateName = name;
},
getName: function() {
return privateName;
}
};
};
コーヒースクリプトが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
もちろん、これには、閉鎖を使用することで得られる他のすべてのプライベート変数と同じ制限があります。たとえば、同じ範囲で定義されていないため、新しく追加された方法はそれらにアクセスできません。
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 作業が停止し、Objecsがこの方法で作成したメモリをもう少し消費します。また、使用してはなりません 新着 と 素晴らしい もうキーワード。
ポイントは、クラスがインスタンス化されるたびに閉鎖を作成する必要があることです。 Pure Coffeescriptクラスのメンバークロージャーは、クラスランタイム「タイプ」が構築されたときに1回だけ作成されます。
プライベートメンバーのみを公開したい場合は、$ variableでラップするだけです
$:
requirements:
{}
body: null
definitions: null
そして使用します @$.requirements