Coffeescript: Getter/setter In Object Initializers
Solution 1:
No, not for now :(
From the CoffeeScript FAQ:
Q: Will you add feature X where feature X depends on a platform?
A: No, implementation-specific features are not allowed as a policy. Everything that you write in CoffeeScript should be supported and runnable on any current JavaScript implementation (in practice, this means the lowest common denominator is IE6). Thus, features such as the following will not be implemented: getters & setters, yield.
Some GitHub issues about getter & setter syntax: #64, #451, #1165 (there is some nice discussion in the last one).
I personally think that having getter & setter literal syntax would be a nice opt-in feature for CoffeeScript now that defineProperty
is part of the ECMAScript standard. The need for getters & setters in JavaScript can be questionable, but you're not forced to use them just because they exist.
Anyway, as you noticed, it's not that hard to implement a convenient wrapper function that calls Object.defineProperty
for class declarations. I personally would use the approach suggested in here:
Function::property = (prop, desc) ->
Object.defineProperty @prototype, prop, desc
classPersonconstructor: (@firstName, @lastName) ->
@property'fullName',
get: -> "#{@firstName}#{@lastName}"set: (name) -> [@firstName, @lastName] = name.split ' '
p = new Person 'Robert', 'Paulson'
console.log p.fullName # Robert Paulson
p.fullName = 'Space Monkey'
console.log p.lastName # Monkey
Or, maybe create two different methods:
Function::getter = (prop, get) ->
Object.defineProperty @prototype, prop, {get, configurable: yes}
Function::setter = (prop, set) ->
Object.defineProperty @prototype, prop, {set, configurable: yes}
classPersonconstructor: (@firstName, @lastName) ->
@getter'fullName', -> "#{@firstName}#{@lastName}"@setter'fullName', (name) -> [@firstName, @lastName] = name.split ' '
For plain objects you can just use Object.defineProperty
(or Object.defineProperties
;) ) on the object itself as Jason proposed. Maybe wrap that in a little function:
objectWithProperties=(obj)->ifobj.propertiesObject.definePropertiesobj,obj.propertiesdeleteobj.propertiesobjrectangle=objectWithPropertieswidth:4height:3properties:area:get:->@width*@heightconsole.logrectangle.area# 12rectangle.width=5console.logrectangle.area# 15
Solution 2:
Here's another approach for defining properties with getters and setters in CoffeeScript that maintains a relatively clean syntax without adding anything to the global Function prototype (which I'd rather not do):
classPersonconstructor: (@firstName, @lastName) ->
Object.defineProperties @prototype,
fullName:get: -> "#{@firstName}#{@lastName}"set: (name) -> [@firstName, @lastName] = name.split ' '
p = new Person 'Robert', 'Paulson'
console.log p.fullName # Robert Paulson
p.fullName = 'Space Monkey'
console.log p.lastName # Monkey
It works well with many properties. For example, here's a Rectangle class that is defined in terms of (x, y, width, height), but provides accessors for an alternative representation (x1, y1, x2, y2):
classRectangleconstructor: (@x, @y, @w, @h) ->
Object.defineProperties @prototype,
x1:
get: -> @xset: (@x) ->
x2:
get: -> @x + @wset: (x2) -> @w = x2 - @x
y1:
get: -> @yset: (@y) ->
y2:
get: -> @y + @hset: (y2) -> @w = y2 - @y
r = new Rectangle 5, 6, 10, 11
console.log r.x2 # 15
Here's the corresponding JavaScript code. Enjoy!
Solution 3:
You can use Object.defineProperty on straight JSON objects as well.
obj = {}
Object.defineProperty obj, 'foo',
get: ->
return'bar'
The get/set notation does not work for various reasons in CoffeeScript. The biggest being that the compiler has not been built to account for get/set notation.
Note that get/set is not supported by all browsers (specifically, IE). Also note that the new ECMA standards (ECMAScript5) mentions Object.defineProperty as the way to define properties with getters/setters.
Solution 4:
Like @curran, I prefer not to modify Function
prototype.
Here it is what I did in one of my projects :
Define somewhere an utility function which for a given class returns 2 functions allowing you to easily add getters and setters on the prototype of the class:
gs = (obj) ->
getter: (propName, getterFunction) ->
Object.defineProperty obj.prototype, propName,
get: getterFunction
configurable: true
enumerable: true
setter: (propName, setterFunction) ->
Object.defineProperty obj.prototype, propName,
set: setterFunction
configurable: true
enumerable: true
gs stands for getter and setter.
Then, you build and import the two functions configured for your class :
class Dog
{ getter, setter } = gs @
constructor: (name, age) ->
@_name = name
@_age = age
getter 'name', -> @_name
setter 'name', (name) ->
@_name = name
return
getter 'age', -> @_age
setter 'age', (age) ->
@_age = age
return
Solution 5:
An alternative approach:
get = (self, name, getter) ->
Object.defineProperty self, name, {get: getter}
set = (self, name, setter) ->
Object.defineProperty self, name, {set: setter}
prop = (self, name, {get, set}) ->
Object.defineProperty self, name, {get: get, set: set}
classDemoconstructor: (val1, val2, val3) ->
# getter only
get @, 'val1', -> val1
# setter only
set @, 'val2', (val) -> val2 = val
# getter and setter
prop @, 'val3',
get: -> val3
set: (val) -> val3 = val
Post a Comment for "Coffeescript: Getter/setter In Object Initializers"