TOM.js — особая библиотека, для особых случаев

в 16:41, , рубрики: javascript, klass.js, requirejs, system.js, TOM.js, web, Веб-разработка, ооп, ооп js, Проектирование и рефакторинг

Приветствую всех.
Не знаю на сколько верно я описал данную библиотеку в заголовке, но рассказать я хочу именно о ней.

TOM.js — особая библиотека, для особых случаев - 1

Что это?

Библиотека 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

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

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

Автор: ange007

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js