Пространства имён

Часть 1

CoffeeScript поистине удивительный язык, который позволяет взглянуть на JavaScript с совершенно иной и намного более притягательной стороны. Давным давно, когда я только начинал заниматься фронт-эндом - меня буквально силками заставляли писать именно на нём (корпоративный стандарт), сейчас же я не могу писать на языке оригинала.

Пространства имён

P.S. На всякий случай хочу напомнить, что эта статья 2014го года и на момент дополнительной редактуры прошло более двух лет. Просто используйте CommonJS. А для тех, кто хочет поностальгировать...

Первое что хотелось бы видеть - это пространства имён. Какому разработчику не хочется видеть аккуратно расположенный код не только в файловой системе, но и в иерархии классов? Гугление подсказало несколько решений, и самым элегантным было использование литералов объектов в качестве имён пространств:

namespace MyApplication:Some:
  class Any
    # Этот класс будет располагаться в window.MyApplication.Some.Any

namespace global:
  class Some
    # Этот класс будет лежать в window.Some

За время, проведённое за штурвалом этого препроцессора, накопилось довольно много "хотелок", которые желалось бы увидеть в JS ES5, некоторые из которых мне удалось претворить в жизнь.

Происходит следующее: Мы вызываем функцию namespace и отправляем туда объект {MyApplication:{Some: сам_класс}}

Единственное "но" - подобное решение обладало некоторыми проблемами, а именно - требовалось строгая последовательность подключения файлов (вначале с пространством MyApplication, затем с MyApplication.Some и т.д.), а так же хардкор регулярками с получением имени класса. Я постарался избавиться от их фатальных недостатков, и в результате получился такой код:

window.namespace = ->
  args   = arguments[0]
  target = global || window
  loop
    for subpackage, obj of args
      target = target[subpackage] or= {}
      args   = obj
    break unless typeof args is 'object'

  Class  = args
  target = window if arguments[0].hasOwnProperty 'global'
  name   = unless Class.name? # IE Fix
    args.toString().match(/^function\s(\w+)\(/)[1]
  else
    Class.name
  proto        = target[name] or undefined
  target[name] = Class

  if proto?
    target[name][i] = proto[i] for i of proto
      

Код уже проверенный на боевых проектах, не тормозит особо и ничего, кроме огромной кучи удовольствия, не принёс.