33 votes

CoffeeScript : Getter/Setter dans les initialisateurs d'objets

ECMAScript nous permet de définir des getters ou setters comme suit :

[texte/javascript]

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

Je peux contourner le problème si j'utilise une classe :

[texte/coffeescript]

"use strict"

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

class Class
      property: ''

      @trigger 'getable', ->
               'x'

      member: 0

Mais que se passe-t-il si je veux définir un déclencheur sur le site de la directement l'objet - sans en utilisant defineProperty / - ies . Je veux faire quelque chose comme (c'est ne fonctionne pas de cette façon) :

[texte/x-pseudo-coffeescript]

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

Cela fonctionne en JavaScript sans aucun problème et je ne veux pas que mes scripts régressent lorsque j'utilise CoffeeScript. N'y a-t-il pas un moyen de faire cela aussi confortablement qu'en JavaScript /ECMAScript ? Merci.

68voto

epidemian Points 10113

Non, pas pour le moment :(

De la FAQ sur CoffeeScript :

Q : Allez-vous ajouter la fonctionnalité X lorsque cette dernière dépend d'une plateforme ?

R : Non, les fonctionnalités spécifiques à une implémentation ne sont pas autorisées en tant que politique. Tout ce que vous écrivez en CoffeeScript doit être pris en charge et exécutable sur n'importe quelle implémentation JavaScript actuelle (en pratique, cela signifie que le plus petit dénominateur commun est IE6). Ainsi, les fonctionnalités suivantes ne seront pas implémentées : getters & setters, yield.

Quelques problèmes sur GitHub concernant la syntaxe getter & setter : #64 , #451 , #1165 (il y a une bonne discussion dans le dernier).

Je pense personnellement qu'avoir une syntaxe littérale getter & setter serait une fonctionnalité intéressante pour CoffeeScript maintenant que defineProperty es fait partie de la norme ECMAScript . La nécessité des getters & setters en JavaScript peut être discutable, mais vous n'êtes pas obligé de les utiliser simplement parce qu'ils existent.


Quoi qu'il en soit, comme vous l'avez remarqué, il n'est pas très difficile d'implémenter une fonction d'enveloppe pratique qui appelle Object.defineProperty pour les déclarations de classes. Personnellement, j'utiliserais l'approche suggérée dans 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

Ou alors, créez deux méthodes différentes :

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

Pour les objets simples, vous pouvez simplement utiliser Object.defineProperty (ou Object.defineProperties ;) ) sur l'objet lui-même comme Jason a proposé . On pourrait peut-être l'envelopper dans une petite fonction :

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

23voto

curran Points 141

Voici une autre approche pour définir des propriétés avec des getters et des setters dans CoffeeScript qui conserve une syntaxe relativement propre sans ajouter quoi que ce soit au prototype global de la fonction (ce que je préfère ne pas faire) :

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

Il fonctionne bien avec de nombreuses propriétés. Par exemple, voici une classe Rectangle qui est définie en termes de (x, y, largeur, hauteur), mais qui fournit des accesseurs pour une représentation alternative (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

Voici le code JavaScript correspondant . Profitez-en !

5voto

Mickaël Gauvin Points 113

Comme @curran, je préfère ne pas modifier Function prototype. Voici ce que j'ai fait dans un de mes projets :

Définissez quelque part une fonction utilitaire qui, pour une classe donnée, renvoie 2 fonctions vous permettant d'ajouter facilement des getters et setters sur le prototype de la classe :

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 signifie g etter et s etter.

Ensuite, vous construisez et importez les deux fonctions configurées pour votre classe :

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

4voto

Jason L. Points 1546

Vous pouvez également utiliser Object.defineProperty sur des objets JSON droits.

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

La notation get/set ne fonctionne pas pour diverses raisons dans CoffeeScript. La plus importante est que le compilateur n'a pas été conçu pour prendre en compte la notation get/set.

Notez que get/set n'est pas pris en charge par tous les navigateurs (en particulier, IE). Notez également que les nouvelles normes de l'ECMA (ECMAScript5) mentionnent Object.defineProperty comme moyen de définir des propriétés avec des getters/setters.

1voto

M K Points 510

Une approche alternative :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X