2011-01-13 34 views
85

Có ai đó biết cách tạo thành viên riêng, không tĩnh trong CoffeeScript không? Hiện tại tôi đang thực hiện việc này, sử dụng biến công khai bắt đầu bằng dấu gạch dưới để làm rõ rằng nó không được sử dụng bên ngoài lớp học:Thành viên riêng tư trong CoffeeScript?

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

    getName: -> @_name 

Đưa biến vào lớp làm cho nó trở thành thành viên tĩnh, nhưng làm thế nào tôi có thể làm cho nó không tĩnh? Nó thậm chí có thể mà không nhận được "ưa thích"?

Trả lời

20

Thậm chí có thể không nhận được "ưa thích" không?

Rất tiếc, bạn phải là ưa thích.

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

Hãy nhớ rằng, "Chỉ là JavaScript".

+1

... và vì vậy bạn phải làm điều đó như bạn sẽ làm điều đó trong JS. Dễ quên nó khi nó ẩn đằng sau tất cả đường đó, cảm ơn! – thejh

+4

Điều đó thực sự riêng tư? Bạn vẫn có thể truy cập nó bên ngoài lớp học. a = Thing ('a') sau đó a.getName() trả về giá trị và a.getName = -> 'b' đặt nó. – Amir

+0

'getName' không phải là riêng tư. 'name' là. – matyr

0

Vì tập lệnh cà phê biên dịch thành JavaScript, cách duy nhất bạn có thể có biến riêng là thông qua các đóng.

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 

này sẽ biên dịch xuống qua JavaScript sau:

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 

Tất nhiên điều này có tất cả các hạn chế tương tự như tất cả các biến tin khác mà bạn có thể có thông qua việc sử dụng đóng cửa, ví dụ, mới các phương thức được thêm vào không có quyền truy cập vào chúng vì chúng không được định nghĩa trong cùng một phạm vi.

+9

Đó là loại thành viên tĩnh. 'e = new Animal (5); f = new Animal (1); e.test()' cảnh báo một, tôi muốn năm. – thejh

+0

@thejh Oh, xin lỗi sau đó, tôi thấy lỗi hiện tại, đoán đã quá muộn để nghĩ về công cụ này ngày hôm qua. –

+0

@thejh Điều đó đã xảy ra với tôi, tôi đã cố gắng giải quyết vấn đề đó trong câu trả lời của tôi. – iConnor

204

các lớp chỉ là các chức năng để chúng tạo phạm vi. mọi thứ được xác định bên trong phạm vi này sẽ không hiển thị từ bên ngoài.

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 biên dịch này như sau:

(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

thx, đây là lời giải thích tốt nhưng có lỗi đánh máy, nên c = new Foo – qmmr

+9

Cần lưu ý rằng các biến riêng tư này ** không ** có sẵn cho các lớp con. –

+45

Cũng cần lưu ý rằng các phương thức 'riêng tư' sẽ cần phải được gọi là 'foo.call (this)' để cho 'this' trở thành instance của hàm. Đây là lý do tại sao cố gắng để mô phỏng thừa kế cổ điển trong JavaScript được lông. –

0

Bạn không thể làm điều đó một cách dễ dàng với các lớp CoffeeScript, bởi vì họ sử dụng các mô hình xây dựng Javascript để tạo các lớp.

Tuy nhiên, bạn có thể nói một cái gì đó như thế này:

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

Nhưng bạn mất sự vĩ đại của lớp CoffeeScript, bởi vì bạn không thể kế thừa từ một lớp được tạo ra theo cách đó bởi bất kỳ cách nào khác hơn bằng cách sử dụng kéo dài () lần nữa. instanceof sẽ ngừng hoạt động và các objec được tạo theo cách này sẽ tiêu thụ nhiều bộ nhớ hơn một chút. Ngoài ra, bạn không được sử dụng mớisiêu từ khóa nữa.

Vấn đề là phải đóng cửa mỗi khi một lớp được khởi tạo. Việc đóng thành viên trong các lớp CoffeeScript thuần túy chỉ được tạo một lần - tức là khi lớp "kiểu" thời gian chạy được xây dựng.

10

Tôi muốn hiển thị một cái gì đó thậm chí fancier

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

Bây giờ bạn có thể làm

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 
-3

Nếu bạn muốn memebers tin chỉ riêng từ công chúng, chỉ cần quấn nó trong biến $

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

và sử dụng @$.requirements

2

Có một vấn đề với câu trả lời của Vitaly và đó là bạn không thể xác định biến mà bạn muốn là duy nhất với phạm vi, nếu bạn đã đặt tên riêng theo cách đó rồi thay đổi nó, giá trị tên sẽ thay đổi cho mỗi thể hiện của lớp, do đó là một trong những cách mà chúng tôi có thể giải quyết vấn đề đó

# 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 

đó không phải là không thể truy cập vào các mảng tên từ bên ngoài, trừ khi bạn sử dụng getNames

thử nghiệm này ra

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

Biên soạn 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

Tôi thích việc triển khai này. Có bất kỳ hạn chế nào không? – Erik5388

1

Đây là cách bạn có thể khai báo các thành viên tư nhân, không tĩnh tại Coffeescript
Để tham khảo đầy đủ, bạn có thể có một cái nhìn tại 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 
2

Dưới đây là một giải pháp dựa trên một số câu trả lời khác ở đây cộng với https://stackoverflow.com/a/7579956/1484513. Nó lưu trữ các biến cá thể (không tĩnh) riêng trong một mảng riêng (tĩnh) và sử dụng một ID đối tượng để biết phần tử nào của mảng đó chứa dữ liệu thuộc về mỗi cá thể.

# 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 
1

Here's bài viết tốt nhất mà tôi tìm thấy về việc thiết public static members, private static members, public and private members, và một số nội dung liên quan khác. Nó bao gồm nhiều chi tiết và js so với số coffee so sánh. Và đối với những lịch sử lý do đây là đoạn mã nguồn tốt nhất từ ​​nó:

# 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 
1

"class" trong kịch bản cà phê dẫn đến một kết quả dựa nguyên mẫu. Vì vậy, ngay cả khi bạn sử dụng một biến riêng, nó được chia sẻ giữa các cá thể. Bạn có thể làm điều này:

EventEmitter = -> 
    privateName = "" 

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

.. dẫn đến

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

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

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

Nhưng hãy cẩn thận để đưa các thành viên cá nhân trước khi các chức năng công cộng, vì kịch bản cà phê trả về chức năng công cộng như đối tượng. Xem Javascript được biên dịch:

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

    return { 
    setName: function(name) { 
     return privateName = name; 
    }, 
    getName: function() { 
     return privateName; 
    } 
    }; 
}; 
Các vấn đề liên quan