TOM.js — особлива бібліотека, для особливих випадків

Вітаю всіх.
Не знаю на скільки вірно я описав цю бібліотеку в заголовку, але я хочу розповісти саме про неї.



Що це?
Бібліотека TOM.js дає можливість полегшити такі завдання як:
  • завантаження/завантаження скриптів/стилів з залежностями
  • створення/спадкування класів
  • перехоплення функцій у межах програми

Навіщо це якщо є аналоги?
Я прекрасно обізнаний про те що є всілякі RequireJS, klass.js і інше, але дана бібліотека представляє з себе напрацювання за кілька років під конкретні завдання в проекті, над яким я працюю.

Наприклад функціонал перехоплення виклику функцій я ніде не зустрічав, але нам в проекті необхідний був даний функціонал для розробки розширень на всі випадки життя, а пізніше і для інших завдань. А там вже і створення класів з потрібним набором параметрів і функцій, ну і звичайно ж завантажувач файлів створений з урахуванням специфіки нашого проекту.

TOM.boot — завантаження модулів і скриптів з залежностями
Спочатку це була невелика бібліотека, яка за 4 роки була переписана вже кілька разів з-за неприємних багів з залежностями. В останній раз була спроба реалізації завантаження за допомогою RequireJS, з невеликою «надбудовою», але в підсумку ця «надбудова» вийшла такою закрученої (та й про залежності у RequireJS свої поняття) що виявилося легше реалізувати свій завантажувач але вже не допускаючи тих помилок, які були в попередніх реалізаціях.

-Що ж уміє дана частина бібліотеки?
  • Завантаження модулів і скриптів

    Для завантаження «модулів» (про них я розповім трохи нижче) і скриптів можна використовувати близько 5 варіацій викликів

    1 спосіб, завдання: завантажити /libraries/jquery/jquery.boot.js та /libraries/scroll/scroll.boot.js
    TOM.boot.load( 'libraries/*', [ 'jquery', 'scroll' ], function( ){ } );
    

    2 спосіб, завдання: завантажити /jquery/jquery.boot.js
    TOM.boot.load( '*', 'jquery', function( ){ } );
    

    3 спосіб, завдання: завантажити /jquery.boot.js
    TOM.boot.load( ", 'jquery', function( ){ } );
    

    4 спосіб, завдання: завантажити /jquery.js
    TOM.boot.load( ", 'jquery.js', function( ){ } );
    

    5 спосіб, завдання: завантажити code.jquery.com/jquery-1.12.0.min.js
    TOM.boot.load( ", 'https://code.jquery.com/jquery-1.12.0.min.js', function( ){ } );
    

    Як можна зрозуміти з прикладів — структура функції виклику наступна:
    TOM.boot.load( 'шлях до скрипта/модуля', 'ім'я файлу/модуля' {рядок або масив}, 'callback по закінченню завантаження' );
    

    Логіка підбору повного шляху тут проста — якщо в імені немає розширення значить ми завантажуємо модуль (*.boot.js), якщо є конкретний файл. А * (зірочка) в дорозі підставляє на це місце ім'я модуля/файлу, що дозволяє зберігати зрозумілу структуру директорій та файлів у великих програмах.

  • Завантаження скриптів з модуля з урахуванням залежностей

    Для початку слід розібрати що таке «модуль» в розумінні даної бібліотеки.
    Модуль — це файл *.boot.js в якому прописані конкретні файли і їх залежності від інших «модулів» і скриптів.

    Вміст *.boot.js виглядає наступним чином:
    TOM.boot.initiate( 'button', [
    { file: '*.style.css' }, // завантажуємо button.style.css
    { file: '*.interface.js' }, // завантажуємо button.interface.js
    { file: '*.core.js', require: '*.interface.js' }, // завантажуємо button.core.js з залежністю від button.interface.js
    { file: 'testButton.core.js', require: [ 'jquery', '*.core.js' ] } // завантажуємо testButton.core.js з залежностями
    ] );
    

    Тут структура має наступний вигляд:
    TOM.boot.initiate( 'ім'я модуля', 'список об'єктів завантажуваних файлів з параметрами' );
    

    Ну а самі об'єкти завантажуваних файлів мають таку структуру:
    • file — ім'я файлу, що завантажується, де * (зірочка) підставляє ім'я модуля
    • require — залежність (список залежностей) як від файлів поточного модуля, так і від інших модулів
    • initialize — функція (список функцій) яку слід виконати після завантаження скрипта
    • main — булевська змінна вказує що всі в даному модулі залежать від даного файлу
TOM.processor — перехоплення функцій, що виконуються всередині програми
Насправді перехоплення функцій буде відбуватися тільки там, де Вам це необхідно, тільки в тих об'єктах, які ви «пропроксируете».
У нашому проекті наприклад є 3 об'єкта які прописані у window і з якими ми працюємо, це наші так звані «області видимості»: api, core, interface саме з ними ми і працюємо, тому тільки їх і проксируем.

TOM.js за замовчуванням створює core та interface, але працювати з ними чи ні це особиста справа кожного.

Як з цим працювати?
  • Проксіювання

    Перше що необхідно зробити — це провести «проксіювання» потрібних об'єктів подібним чином:
    TOM.processor.proxy( core );
    TOM.processor.proxy( interface );
    

    Суть проксі серверів проста до неподобства — проходимо по об'єкту і обертаємо функції певним видом (додаємо pre-callback і post-callback).

  • Обробка/Перехоплення виклику функцій

    Після обробки потрібних об'єктів викликані всередині них функції — можна обробляти таким чином:
    // Обробка виклику створення кнопки
    TOM.processor.bind( 'pre-core.test.addTestButton', function( sender )
    { 
    // Якщо ми не хочемо насправді створювати кнопку - перериваємо її створення
    if( !confirm( 'Дійсно створити кнопку?' ) )
    {
    return false;
    }
    } );
    

    Обробник має таку структуру:
    TOM.processor.bind( '{pre або post}-ім'я функції виклику якої чекаємо', 'callback функція', 'параметри' );
    

    • pre або post — це обробка «до дзвінка» оригіналу функції, або після — відповідно
    • параметри — це об'єкт з налаштуваннями даного обробника
      • stage — аналог pre/post імені функції
      • label — «мітка» за якою ми зможемо зняти саме цей обробник, не зачіпаючи інші
      • priority — додавати даний обробник на початок або в кінець черги?
  • Інші можливості

    Крім безпосередньо можливості додавання і зняття «обробників», можна так само:
    • «порушувати фейкові події»:
      TOM.processor.signal( 'момент запуску (pre/post)', 'ім'я функції', 'об'єкт викликає функцію', 'аргументи' );
    • здійснювати одноразову обробку події:
      TOM.processor.one( 'перелік тих же аргументів що і в TOM.processor.bind' );


TOM.classes — створення та успадкування класів
Дана частина бібліотеки обігрує стандартний підхід до створення та успадкування класів в стандартному JavaScript, але з великою кількістю нюансів і напрацювань.

  • Як створити/успадкувати клас?

    • 1й спосіб
      TOM.classes.create(
      'область видимості / об'єкт в якому потрібно створювати клас',
      'ім'я створюваного класу',
      'клас від якого потрібно успадковуватись', 
      
      // Функція конструктор
      function constructor( )
      {
      },
      
      // -- Далі йдуть функції у вигляді аргументів -- //
      
      function foo( )
      {
      },
      
      function bar( )
      {
      }
      )
      
    • 2й спосіб
      TOM.classes.create(
      'область видимості / об'єкт в якому потрібно створювати клас',
      'ім'я створюваного класу',
      'клас від якого потрібно успадковуватись', 
      
      // Функція конструктор
      function constructor( )
      {
      },
      
      // Функції в масиві або на об'єкті
      [
      function foo( )
      {
      },
      
      function bar( )
      {
      }
      ]
      )
      
    • 3й спосіб — такий-же як і — але в масиві знаходиться ще і конструктор


  • Які особливості даного скрипта?

    Крім сумісності з TOM.processor, створених класах більш-менш адекватно працює виклик функцій з батьків, за допомогою:
    this.__parentCall__( ); // Виклик функції батьків виходячи з arguments.callee
    this.__parentFunction__( 'ім'я функції', 'аргументи' ); // Виклик функції за іменем
    

    І багато інших дрібниць.
    Примітка: Я знаю що arguments.callee це погано, і воно не працює у strict mode, але поки зручної заміни не придумав.


Демо сторінка: tredsnet.github.io/TOM
GitHub репозиторій: github.com/tredsnet/TOM

Бібліотека звичайно не дуже підготовлена для публікації, не «вычухан» код, не прибрані зайві коментарі та примітки, де можливо нестандартна поведінка (так як тестувалося тільки на нашому проекті). Але всьому свій час, можливо і в такому вигляді бібліотека буде комусь корисна, а в разі зацікавленості користувачів — можливо і розвиток у потрібному напрямку.

Спасибі що дочитали до кінця. Буду радий будь-яким коментарям, але прошу не забувати що бібліотека створювалася під конкретні потреби конкретного проекту.

Буде Вам корисна дана бібліотека?

/>
/>


<input type=«radio» id=«vv71585»
class=«radio js-field-data»
name=«variant[]»
value=«71585» />
Так, буде корисна вся бібліотека
<input type=«radio» id=«vv71587»
class=«radio js-field-data»
name=«variant[]»
value=«71587» />
Так, буде корисна тільки частина бібліотеки
<input type=«radio» id=«vv71589»
class=«radio js-field-data»
name=«variant[]»
value=«71589» />
Ні, використовую свої напрацювання
<input type=«radio» id=«vv71591»
class=«radio js-field-data»
name=«variant[]»
value=«71591» />
Ні, використовую інші бібліотеки/фреймворки
<input type=«radio» id=«vv71593»
class=«radio js-field-data»
name=«variant[]»
value=«71593» />
Ні, не займаюся проектами де це може бути корисним
<input type=«radio» id=«vv71595»
class=«radio js-field-data»
name=«variant[]»
value=«71595» />
Ні, це якийсь жах, а не бібліотека

Проголосувало 75 осіб. Утрималося 63 людини.


Тільки зареєстровані користувачі можуть брати участь в опитуванні. Увійдіть, будь ласка.


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.