Константы

Часть 3

Есть несколько популярных вариантов реализации констант в CoffeeScript. Для остального есть ES6, настоятельно рекомендую использовать именно его.

Вариант 1

class Some
  @MY_DEFINE = 42

  @getVar: ->
    @MY_DEFINE

  getVar: ->
    @contructor.MY_DEFINE # или Some.MY_DEFINE

В таком варианте есть как плюсы, так и минусы:

Плюсы:

  • Константу (теоретическую, т.к. это просто переменная) можно получить из любого места, обратившись к Some.MY_DEFINE

Минусы:

  • Использовать не всегда удобно
  • Это переменная (т.е. можно перезаписать), а использование __defineGetter__ и аналогичных конструкций создания геттеров - только усложнит чтение.

Вариант номер два

class Some
  MY_DEFINE = 42

  @getVar: ->
    MY_DEFINE

  getVar: ->
    MY_DEFINE

Плюсы:

  • Внутри класса выглядит великолепно, читаемо и пользоваться очень удобно

Минусы:

  • Одноразовая, т.к. ограничена только текущей (и вложенными) областью видимости, за пределами класса получить её значение невозможно
  • Невозможно реализовать геттеры\сеттеры, чтобы оградить значение от изменений

Есть ещё варианты, вроде использования джаваскриптовых const MY_DEFINE = 42 в обратных одинарных кавычках (там где буква "Ё"), или добавления функций в прототип Function, которые будут регистрировать константы с помощью геттеров\сеттеров, но это малопопулярные техники, так что я о них промолчу и лучше предложу свой вариант (чуть более приближенный к реальности):

Третий вариант

class Ajax
  define AJAX_UNSENT:   0
  define AJAX_OPENED:   1
  define AJAX_HEADERS_RECEIVED: 2
  define AJAX_LOADING:  3
  define AJAX_READY:    4

  request: ->
    # некоторый код
    if xhr.status is AJAX_READY
      # делать что-нибудь

Реализация самой функции:

window.define = (args) ->
  for name, val of args
    continue if window[name]?
    do (name, val) ->
      unless window.__defineGetter__? # IE Fix
        return window[name] = val

      window.__defineGetter__ name, -> val
      window.__defineSetter__ name, -> throw new Error "Can not redeclare define #{name}. Define already exists."


# Ну и можно добавить функцию проверки на существование
window.defined = (name) ->
  return window.__lookupGetter__(name)? && window[name]?

Происходит следующее: Вызываем функцию, куда передаём объект. Далее мы в window регистрируем геттер, который будет возвращать нужное значение и сеттер, который блокирует возможность перезаписи константы.

Плюсы:

  • Внутри классов также выглядит очень красиво и читаемо
  • Можно получить в любом месте кода

Минусы:

  • Висит в window - глобалсы никогда не были хорошим решением, но я не думаю, что это так уж существенно в свете возможных выигрышей по читаемости и удобству кода, а проблемы коллизий решаются обычными префиксами.