Хуки JS в LS, краткое руководство для тех, кто запутался
Сегодня обратился за помощью товарищей всем известный разработчик avadim — не может вникнуть в дзен механизма хуков, которые работают на бэкенде (ЖС). Поэтому решил оформить этот ответ в виде краткого поста — может кому нибудь будет полезно.
Механизм делегирования кода ЖС в ЛС можно осуществлять такими наиболее часто употребляемыми методами в зависимости от поставленной задачи:
Рассмотрим кратко каждый из приемов.
Пример кода:
Создает запись для выполнения кода __здесь__ (где он расположен), вторым параметром передается массив параметров ([obj, block]) и указатель контекста (this). В твоем случае (для avadim ) это просто контекст так описан (где-то написано thisArg = this выше в коде).
вот, а теперь этот хук можно перехватить с помощью плагина и вызвать свой код:
Пример кода:
Хук на который цепляемся (js_hook_super_name), а также анонимная функция, в которую передаются параметры (obj, block), которые объявлены в самом хуке. Третий возможный параметр — приоритет. Контекст в этой анонимной функции будет тот, на который указывает третий параметр объявления хука (в данном примере — this)
Когда я написал статью, я понял что, возможно, описание маркеров нужно было начинать с возможности инжекта кода, но, пожалуй, оставлю текст таким как есть. Не останавливайтесь если что-то не совсем понятно — читайте далее. Маркеры и их параметры я расписал очень детально.
Маркер очень похож на работу хуков за исключением того что сама функция будет модифицирована в месте маркера или в начале функции (в зависимости от параметров вызова инжекта).
Пример кода:
Создает дополнительное место в функции (где он объявлен) для выполнения кода. Не обязателен. Маркера может и не быть вовсе, но и в такую функцию код добавить можно — в самое её начало (после открывающей скобки ({). Понятней станет после прочтения вызова инжекта кода (см. ниже).
А теперь как этот код может быть модифицирован:
Пример кода:
Первый параметр вызова:
— имя функции куда нужно инжектить код. Может быть: строкой ('ls.MODULE.METHOD', полной функцией (ls.MODULE.METHOD) или массивом ([ls.MODULE, 'METHOD']), где первый параметр — модуль, второй — строка, название метода.
Второй параметр вызова — собственно код и третий параметр (строка, не обязательный) — указание имени маркера (в данном примере — 'SomePlace'), куда вставлять свой код в функции (первый параметр вызова). Если не указать третий параметр, то код (2-й параметр вызова) будет вставлен в начало функции (1-й параметр вызова) сразу после открывающей скобки ({), в противном случае код будет помещен на место маркера ('SomePlace') в этой функции (1-й параметр вызова).
Другими словами: код в анонимной функции (2-й параметр вызова) будет «перенесен» в место маркера или на начало функции как будто он там и был написан с самого начала. Это позволяет управлять ходом обработки функции в т.ч. сделать return false чтобы прервать выполнение метода если нужно. Хук (см. выше п. 1 вступления) прервать выполнение метода не может, только оперировать с набором параметров, которые переданы в функцию хука.
В ЛС существует два нормальных механизма дополнения ЖС кода своим функционалом и один метод хардкора, который использовать не стоит т.к. исчезает полная совместимость с другими плагинами.
Возможно, это кому-то поможет писать более правильные плагины без делегирования функционала шаблона (я бы вообще запретил в каталоге шаблоны с делегированием, но иногда без делегирования — никак)
Хочу заметить что замечательный механизм хуков в ЛС придумал пользователь 1d10t и впервые сообщил здесь.
UPD: Топик дополнен описанием маркеров и исправлены пара неточностей в описании.
UPD 2: Здесь можно найти отредактированную версию этой статьи.
Вступление
Механизм делегирования кода ЖС в ЛС можно осуществлять такими наиболее часто употребляемыми методами в зависимости от поставленной задачи:
- Переопределить всю функцию (атата!)
В своем коде просто переопределить весь метод:
ls.MODULE.METHOD = function (param1, param2) { /* party hard */ }
Желательно не использовать данный подход, он показан для примера.
- Механизм хуков
- Механизм маркеров
Рассмотрим кратко каждый из приемов.
1. Хуки
Объявление хука (места, где может быть выполнен произвольный код)
Пример кода:
ls.hook.run ('js_hook_super_name', [obj, block], this);
Создает запись для выполнения кода __здесь__ (где он расположен), вторым параметром передается массив параметров ([obj, block]) и указатель контекста (this). В твоем случае (для avadim ) это просто контекст так описан (где-то написано thisArg = this выше в коде).
вот, а теперь этот хук можно перехватить с помощью плагина и вызвать свой код:
Использование хука (вызов кода в место, где объявлен хук)
Пример кода:
ls.hook.add ('js_hook_super_name', function (obj, block) { /* code here */ });
Хук на который цепляемся (js_hook_super_name), а также анонимная функция, в которую передаются параметры (obj, block), которые объявлены в самом хуке. Третий возможный параметр — приоритет. Контекст в этой анонимной функции будет тот, на который указывает третий параметр объявления хука (в данном примере — this)
2. Маркеры
Когда я написал статью, я понял что, возможно, описание маркеров нужно было начинать с возможности инжекта кода, но, пожалуй, оставлю текст таким как есть. Не останавливайтесь если что-то не совсем понятно — читайте далее. Маркеры и их параметры я расписал очень детально.
Маркер очень похож на работу хуков за исключением того что сама функция будет модифицирована в месте маркера или в начале функции (в зависимости от параметров вызова инжекта).
Объявление маркера (места, где может быть модифицирована оригинальная функция)
Пример кода:
ls.hook.marker ('SomePlace');
Создает дополнительное место в функции (где он объявлен) для выполнения кода. Не обязателен. Маркера может и не быть вовсе, но и в такую функцию код добавить можно — в самое её начало (после открывающей скобки ({). Понятней станет после прочтения вызова инжекта кода (см. ниже).
А теперь как этот код может быть модифицирован:
Инжект кода в начало функции или место, где в ней объявлен маркер
Пример кода:
ls.hook.inject ([ls.MODULE, 'METHOD'], function () { /* i`m INSIDE of func and maybe on marker place */ });
Первый параметр вызова:
[ls.MODULE, 'METHOD']
— имя функции куда нужно инжектить код. Может быть: строкой ('ls.MODULE.METHOD', полной функцией (ls.MODULE.METHOD) или массивом ([ls.MODULE, 'METHOD']), где первый параметр — модуль, второй — строка, название метода.
Второй параметр вызова — собственно код и третий параметр (строка, не обязательный) — указание имени маркера (в данном примере — 'SomePlace'), куда вставлять свой код в функции (первый параметр вызова). Если не указать третий параметр, то код (2-й параметр вызова) будет вставлен в начало функции (1-й параметр вызова) сразу после открывающей скобки ({), в противном случае код будет помещен на место маркера ('SomePlace') в этой функции (1-й параметр вызова).
Другими словами: код в анонимной функции (2-й параметр вызова) будет «перенесен» в место маркера или на начало функции как будто он там и был написан с самого начала. Это позволяет управлять ходом обработки функции в т.ч. сделать return false чтобы прервать выполнение метода если нужно. Хук (см. выше п. 1 вступления) прервать выполнение метода не может, только оперировать с набором параметров, которые переданы в функцию хука.
Итог
В ЛС существует два нормальных механизма дополнения ЖС кода своим функционалом и один метод хардкора, который использовать не стоит т.к. исчезает полная совместимость с другими плагинами.
Возможно, это кому-то поможет писать более правильные плагины без делегирования функционала шаблона (
Хочу заметить что замечательный механизм хуков в ЛС придумал пользователь 1d10t и впервые сообщил здесь.
UPD: Топик дополнен описанием маркеров и исправлены пара неточностей в описании.
UPD 2: Здесь можно найти отредактированную версию этой статьи.
Зачастую поездка на поезде выходит быстрее, чем на самолете. Это связанно с большой удаленностью аэропортов и долгим ожиданием вылета. Сэкономить время можно на заказе такси в аэропорт Домодедово, которое доставит вас точно в срок перед вылетом.
8 комментариев
Нафига два принципиально разных механизма использовать для одного и того же действия в одном функциональном методе? Просто потому что так когда-то решили и ничо менять не будем?
Для меня странно, что абсолютно произвольная модификация нативного кода на лету — это считается нормально, а переопределение метода (банальнейшая вещь!) называется хардкодом и не рекомендована. Почему? И что мешает в хуках сделать обработку возвращаемого значения?