-
-
Save davepeck/4236746 to your computer and use it in GitHub Desktop.
Function::property = (prop, desc) -> | |
Object.defineProperty this.prototype, prop, desc | |
class Foo | |
@property 'neat' | |
get: -> | |
42 | |
class Bar extends Foo | |
@property 'neat' | |
get: -> | |
super | |
# 42 | |
alert(new Foo().neat) | |
# exception | |
alert(new Bar().neat) |
Function::property = (prop, desc) -> | |
Object.defineProperty this.prototype, prop, desc | |
class Foo | |
@property 'neat' | |
get: -> | |
42 | |
class Bar extends Foo | |
@property 'neat' | |
get: -> | |
Bar.__super__.neat | |
# 42 | |
alert(new Foo().neat) | |
# 42 | |
alert(new Bar().neat) |
I also recommend the following if you're going this route:
# Will make IE7/8 happy -- although do we care?
if Object.prototype.__defineGetter and not Object.defineProperty
Object.defineProperty = (obj, prop, desc) ->
if "get" of desc
obj.__defineGetter__ prop, desc.get
if "set" of desc
obj.__defineSetter__ prop, desc.set
# Will make IE7/8 happy -- although do we care?
😄
If i had to guess, i'd say that the intersection between developers who want to use getters and setters in CoffeeScript and developers that care for IE7/8 compatibility is probably pretty small. But nice consideration nevertheless.
About the second example, although it's not broken like the first one, it won't work for the general case either: if the Foo's 'neat' property depended on something stored as an own property of the instance, calling .neat
in the superclass prototype won't work. For example:
Function::property = (prop, desc) ->
Object.defineProperty this.prototype, prop, desc
class Foo
constructor: (@neatPrefix) ->
@property 'neat'
get: ->
"#{@neatPrefix} 42"
class Bar extends Foo
@property 'neat'
get: ->
Bar.__super__.neat
#42
alert (new Foo 'uber').neat # -> "uber 42"
#42
alert (new Bar 'uber').neat # -> "undefined 42"
The only way i found to make Bar's 'neat' work is to replace Foo.__super__.neat
for (Object.getOwnPropertyDescriptor Bar.__super__, 'neat').get.apply @
. Not very pretty 😅
I'm not sure trying to make inheritance and getters/setters work at the same time in CS over the current semantics of JS is very plausible. ECMAScript 6 adds a lot of classical OO to the mixture, so i'd expect that having a working super
with getters and setters over that that instead would be much much easier.
The only way i found to make Bar's 'neat' work is to replace
Foo.__super__.neat
for(Object.getOwnPropertyDescriptor Bar.__super__, 'neat').get.apply @
. Not very pretty
Yep, I ended up running into this too and settled on a similar pattern. It ain't pretty, at all. Alas.
How about this?
class Person
constructor: (@firstName, @lastName) ->
@get 'fullName', -> @firstName + ' ' + @lastName
class Captain extends Person
@get 'fullName', -> 'Capt. ' + @getSuper 'fullName'
johnDoe = new Person 'John', 'Doe'
console.log johnDoe.fullName # -> "John Doe"
jackSparrow = new Captain 'Jack', 'Sparrow'
console.log jackSparrow.fullName # -> "Capt. Jack Sparrow"
# Setup
Function::get = (prop, get) ->
Object.defineProperty @prototype, prop, {get}
Function::set = (prop, set) ->
Object.defineProperty @prototype, prop, {set}
Object::getSuper = (prop) ->
(Object.getOwnPropertyDescriptor @constructor.__super__, prop).get.apply @
Object::setSuper = (prop) ->
(Object.getOwnPropertyDescriptor @constructor.__super__, prop).set.apply @
Better setup (non-enumerable)
Object.defineProperty Function.prototype, 'get', value: (prop, get) ->
Object.defineProperty @prototype, prop, {get}
Object.defineProperty Function.prototype, 'set', value: (prop, set) ->
Object.defineProperty @prototype, prop, {set}
Object.defineProperty Object.prototype, 'getSuper', value: (prop) ->
(Object.getOwnPropertyDescriptor @constructor.__super__, prop).get.apply @
Object.defineProperty Object.prototype, 'setSuper', value: (prop) ->
(Object.getOwnPropertyDescriptor @constructor.__super__, prop).set.apply @
Better setup (non-enumerable)
Object.defineProperty Function.prototype, 'get',
configurable: yes
writable: yes
value: (prop, get) ->
Object.defineProperty @prototype, prop, {get}
Object.defineProperty Function.prototype, 'set',
configurable: yes
writable: yes
value: (prop, set) ->
Object.defineProperty @prototype, prop, {set}
Object.defineProperty Object.prototype, 'getSuper',
configurable: yes
writable: yes
value: (prop) ->
(Object.getOwnPropertyDescriptor @constructor.__super__, prop).get.apply @
Object.defineProperty Object.prototype, 'setSuper',
configurable: yes
writable: yes
value: (prop) ->
(Object.getOwnPropertyDescriptor @constructor.__super__, prop).set.apply @
Hi all and thx for the example(s),
@ben-x9:
It works with setters when one changes the getter-definition
Object.defineProperty Function.prototype, 'get',
configurable: yes
writable: yes
value: (prop, get) ->
Object.defineProperty @prototype, prop, {
get: get
configurable: yes
writable: yes
}
to allow a following setter-definition. The code as-is throws 'Cannot redefine property ' in Chrome.
cheers, Andreas
Just something to watch out for if you like ES5 properties and coffeescript.