CoffeeScript: Getter / Setter en Inicializadores de objetos


ECMAScript nos permite definir getters o setters de la siguiente manera:

[text/javascript]

var object = {
  property: 7,
  get getable() { return this.property + 1; },
  set setable(x) { this.property = x / 2; }
};

Puedo trabajar si estoy usando una clase :

[texto/coffeescript]

"use strict"

Function::trigger = (prop, getter, setter) ->
      Object.defineProperty @::,
              get: getter
              set: setter               

class Class
      property: ''

      @trigger 'getable', ->
               'x'

      member: 0

Pero qué pasa si quiero definir trigger en el objeto directamente - sin usando defineProperty / -ies. Quiero hacer algo como (es no funciona de esa manera):

[texto / x-pseudo-coffeescript]

object =
  property: 'xhr'
  get getable: 'x'

Es trabajando en JavaScript sin ningún problema y no quiero que mis scripts retrocedan cuando estoy usando CoffeeScript. ¿No hay una manera de hacer esto tan cómodo como en JavaScript/ECMAScript? Gracias.

Author: fridojet, 2012-07-21

6 answers

No, no por ahora : (

Del CoffeeScript FAQ :

P: ¿Agregará la característica X donde la característica X depende de una plataforma?

A: No, las características específicas de la implementación no están permitidas como política. Todo lo que escriba en CoffeeScript debe ser compatible y ejecutable en cualquier implementación actual de JavaScript (en la práctica, esto significa que el denominador común más bajo es IE6). Por lo tanto, no se implementarán características como las siguientes: getters & armadores, cedan.

Algunos problemas de GitHub sobre la sintaxis getter & setter: #64, #451, #1165 (hay una buena discusión en el último).

Personalmente creo que tener sintaxis literal de getter & setter sería una buena característica de opt-in para CoffeeScript ahora que defineProperty es parte del estándar ECMAScript. La necesidad de getters y setters en JavaScript puede ser cuestionable, pero no está obligado a usarlos solo porque existan.


De todos modos, como se ha dado cuenta, no es tan difícil implementar una función de envoltura conveniente que llama Object.defineProperty para declaraciones de clase. Yo personalmente usaría el enfoque sugerido en aquí :

Function::property = (prop, desc) ->
  Object.defineProperty @prototype, prop, desc

class Person
  constructor: (@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

O, tal vez crear dos métodos diferentes:

Function::getter = (prop, get) ->
  Object.defineProperty @prototype, prop, {get, configurable: yes}

Function::setter = (prop, set) ->
  Object.defineProperty @prototype, prop, {set, configurable: yes}

class Person
  constructor: (@firstName, @lastName) ->
  @getter 'fullName', -> "#{@firstName} #{@lastName}"
  @setter 'fullName', (name) -> [@firstName, @lastName] = name.split ' '

Para objetos simples puede usar Object.defineProperty (o Object.defineProperties ;) ) sobre el objeto en sí como Jason propuso . Tal vez envolver eso en una pequeña función:

objectWithProperties = (obj) ->
  if obj.properties
    Object.defineProperties obj, obj.properties
    delete obj.properties
  obj

rectangle = objectWithProperties
  width: 4
  height: 3
  properties:
    area:
      get: -> @width * @height

console.log rectangle.area # 12
rectangle.width = 5
console.log rectangle.area # 15
 74
Author: epidemian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:29:54

Aquí hay otro enfoque para definir propiedades con getters y setters en CoffeeScript que mantiene una sintaxis relativamente limpia sin agregar nada al prototipo de función global (lo que prefiero no hacer):

class Person
  constructor: (@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

Funciona bien con muchas propiedades. Por ejemplo, aquí hay una clase de rectángulo que se define en términos de (x, y, width, height), pero proporciona accesores para una representación alternativa (x1, y1, x2, y2):

class Rectangle                                     
  constructor: (@x, @y, @w, @h) ->
  Object.defineProperties @prototype,
    x1:
      get: -> @x
      set: (@x) ->
    x2:
      get: -> @x + @w
      set: (x2) -> @w = x2 - @x
    y1:
      get: -> @y
      set: (@y) ->
    y2:
      get: -> @y + @h
      set: (y2) -> @w = y2 - @y

r = new Rectangle 5, 6, 10, 11
console.log r.x2 # 15

Aquí está el JavaScript correspondiente código. ¡Que lo disfrutes!

 29
Author: curran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-19 19:48:57

Puede usar Object.defineProperty también en objetos JSON rectos.

obj = {}
Object.defineProperty obj, 'foo',
    get: ->
        return 'bar'

La notación get/set no funciona por varias razones en CoffeeScript. El mayor es que el compilador no ha sido construido para tener en cuenta la notación get/set.

Tenga en cuenta que get/set no es compatible con todos los navegadores (específicamente, IE). También tenga en cuenta que los nuevos estándares ECMA (ECMAScript5) menciona Object.defineProperty como la forma de definir propiedades con getters / setters.

 7
Author: Jason L.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-31 18:08:52

Al igual que @curran, prefiero no modificar el prototipo Function. Aquí está lo que hice en uno de mis proyectos :

Defina en algún lugar una función de utilidad que para una clase dada devuelve 2 funciones que le permiten agregar fácilmente getters y setters en el prototipo de la clase:

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 representa getter y setter.

Luego, compila e importa las dos funciones configuradas para su clase :

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
 3
Author: Mickaël Gauvin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-29 08:15:37

Un enfoque alternativo:

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}

class Demo 
  constructor: (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
 1
Author: M K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-21 09:14:53

Gracias a los otros que han ido antes. Muy general y simplemente:

attribute = (self, name, getterSetterHash) ->
  Object.defineProperty self, name, getterSetterHash

class MyClass
  constructor: () ->
    attribute @, 'foo',
      get: -> @_foo ||= 'Foo' # Set the default value
      set: (who) -> @_foo = "Foo #{who}"

    attribute @, 'bar',
      get: -> @_bar ||= 'Bar'

    attribute @, 'baz',
      set: (who) -> @_baz = who


myClass = new MyClass()
alert(myClass.foo) # alerts "Foo"
myClass.foo = 'me' # uses the foo setter
alert(myClass.foo) # alerts "Foo me"
 1
Author: kwerle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-16 21:57:08