Что такое декораторы javascript

Декораторы в JavaScript

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

Что меня не устраивает в известных реализациях?

Реализации, предлагаемые по первым ссылкам в Google, работают не тем образом, как это работает в Python. Во многих статьях предлагается создать объект, заполнить его поля функциями и осуществлять вызовы отдекорированных через эти поля.
На первой странице есть несколько ссылок, где используются методы, сходные с моими,
Есть ещё много реализаций, но они мне неинтересны.
Расскажу наиболее правильную с моей точки зрения.

Нормальный декоратор

Итак, что такое декоратор?
Декоратор, это функция, которая добавляет функции-аргументу функционала.

Пример из Python:

def superfunc(n=2): print("Megclosure created") def clos(func): print("Megclosure used") def clos1(*args, **kwargs): print("Megclosured function",func.__name__) res = func(*args, **kwargs) return (res**n) return clos1 return clos def a(par): return par+9; b = (superfunc())(a) @superfunc(3) def c(txt): return len(txt)+1 print ( b(1),c("abc"*3)) 

Что мы тут делаем?
Мы тут создаём функцию, которая создаёт другую функцию, которая выполняет то, что нам нужно, и вызывает декорируемую функцию.

Как это будет на JavaScript

Итак, мы хотим вызвать функцию, передав ей функцию и, возможно, дополнительные параметры, и получить отдекорированную функцию b, т.е.

function decorator() < . >. function a() < . >. var b=decorator(a, arg1, arg2. argN);

Приведу участок кода из одной своей библиотеки.

function withVars(f, variables) < var args = Array.prototype.slice.call(arguments, 1);// получаем массив аргументов функции, за исключением самой функции, причём именно МАССИВ return function () >; 
function a() < var str =""; for(var i=0;ialert(str); > a(1,2,4,5,"six");//1 2 3 4 5 six var b=withVars(a,4,8,15,16,23,42); b(1,2,4,5,"six");//1 2 3 4 5 six 4 8 15 16 23 42 

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

Array.prototype.push.apply(p, args); f.apply(this, p);
Array.prototype.push.apply(args,p); f.apply(this, args); 

Возможные применения

    Допустим вам нужно вызвать callback2 из callback1,callback3 из callback2, и т.д., и при этом передать некоторые данные из одного колбека в другой. Мы не можем передать дополнительную информацию в объекте-источнике событий. Например, когда мы используем GM_xmlhttpRequest. Короче, источник событий спрятан от нас, а наружу торчит функция, которую мы можем вызывать, но которая не сохраняет произвольную информацию для передачи в коллбек. Событий будет много и возникать они будут возникать «параллельно», так что создать переменную — не вариант.

function cb1(evt)< . GM_xmlhttpRequest(< method: "GET", url: url2, onload: withVars(cb2,processed2) >); > function cb2(evt)< . GM_xmlhttpRequest(< method: "GET", url: url3, onload: withVars(cb3,processed3) >); > function cb3(evt)< . GM_xmlhttpRequest(< method: "GET", url: url4, onload: withVars(cb4,processed4) >); > function cb3(evt) < . >GM_xmlhttpRequest(< method: "GET", url: url1, onload: cb1 >); 
var mult=fstrValidate(function(a,b)< return a*b; >,"%d%d"); 

Что почитать

UPD1: Добавил несколько применений.
UPD2: немного исправил код

Источник

Функции-обёртки, декораторы

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/call-apply-decorators.

JavaScript предоставляет удивительно гибкие возможности по работе с функциями: их можно передавать, в них можно записывать данные как в объекты, у них есть свои встроенные методы…

Конечно, этим нужно уметь пользоваться. В этой главе, чтобы более глубоко понимать работу с функциями, мы рассмотрим создание функций-обёрток или, иначе говоря, «декораторов».

Декоратор – приём программирования, который позволяет взять существующую функцию и изменить/расширить её поведение.

Декоратор получает функцию и возвращает обёртку, которая делает что-то своё «вокруг» вызова основной функции.

bind – привязка контекста

Один простой декоратор вы уже видели ранее – это функция bind:

function bind(func, context) < return function() < return func.apply(context, arguments); >; >

Вызов bind(func, context) возвращает обёртку, которая ставит this и передаёт основную работу функции func .

Декоратор-таймер

Создадим более сложный декоратор, замеряющий время выполнения функции.

Он будет называться timingDecorator и получать функцию вместе с «названием таймера», а возвращать – функцию-обёртку, которая измеряет время и прибавляет его в специальный объект timer по свойству-названию.

function f(x) <> // любая функция var timers = <>; // объект для таймеров // отдекорировали f = timingDecorator(f, "myFunc"); // запускаем f(1); f(2); f(3); // функция работает как раньше, но время подсчитывается alert( timers.myFunc ); // общее время выполнения всех вызовов f

При помощи декоратора timingDecorator мы сможем взять произвольную функцию и одним движением руки прикрутить к ней измеритель времени.

var timers = <>; // прибавит время выполнения f к таймеру timers[timer] function timingDecorator(f, timer) < return function() < var start = performance.now(); var result = f.apply(this, arguments); // (*) if (!timers[timer]) timers[timer] = 0; timers[timer] += performance.now() - start; return result; >> // функция может быть произвольной, например такой: var fibonacci = function f(n) < return (n >2) ? f(n - 1) + f(n - 2) : 1; > // использование: завернём fibonacci в декоратор fibonacci = timingDecorator(fibonacci, "fibo"); // неоднократные вызовы. alert( fibonacci(10) ); // 55 alert( fibonacci(20) ); // 6765 // . // в любой момент можно получить общее количество времени на вызовы alert( timers.fibo + 'мс' );

Обратим внимание на строку (*) внутри декоратора, которая и осуществляет передачу вызова:

var result = f.apply(this, arguments); // (*)

Этот приём называется «форвардинг вызова» (от англ. forwarding): текущий контекст и аргументы через apply передаются в функцию f , так что изнутри f всё выглядит так, как была вызвана она напрямую, а не декоратор.

Декоратор для проверки типа

В JavaScript, как правило, пренебрегают проверками типа. В функцию, которая должна получать число, может быть передана строка, булево значение или даже объект.

function sum(a, b) < return a + b; >// передадим в функцию для сложения чисел нечисловые значения alert( sum(true, < name: "Вася", age: 35 >) ); // true[Object object]

Функция «как-то» отработала, но в реальной жизни передача в sum подобных значений, скорее всего, будет следствием программной ошибки. Всё-таки sum предназначена для суммирования чисел, а не объектов.

Многие языки программирования позволяют прямо в объявлении функции указать, какие типы данных имеют параметры. И это удобно, поскольку повышает надёжность кода.

В JavaScript же проверку типов приходится делать дополнительным кодом в начале функции, который во-первых обычно лень писать, а во-вторых он увеличивает общий объем текста, тем самым ухудшая читаемость.

Декораторы способны упростить рутинные, повторяющиеся задачи, вынести их из кода функции.

Например, создадим декоратор, который принимает функцию и массив, который описывает для какого аргумента какую проверку типа применять:

// вспомогательная функция для проверки на число function checkNumber(value) < return typeof value == 'number'; >// декоратор, проверяющий типы для f // второй аргумент checks - массив с функциями для проверки function typeCheck(f, checks) < return function() < for (var i = 0; i < arguments.length; i++) < if (!checks[i](arguments[i])) < alert( "Некорректный тип аргумента номер " + i ); return; >> return f.apply(this, arguments); > > function sum(a, b) < return a + b; >// обернём декоратор для проверки sum = typeCheck(sum, [checkNumber, checkNumber]); // оба аргумента - числа // пользуемся функцией как обычно alert( sum(1, 2) ); // 3, все хорошо // а вот так - будет ошибка sum(true, null); // некорректный аргумент номер 0 sum(1, ["array", "in", "sum. "]); // некорректный аргумент номер 1

Конечно, этот декоратор можно ещё расширять, улучшать, дописывать проверки, но… Вы уже поняли принцип, не правда ли?

Один раз пишем декоратор и дальше просто применяем эту функциональность везде, где нужно.

Декоратор проверки доступа

И наконец посмотрим ещё один, последний пример.

Предположим, у нас есть функция isAdmin() , которая возвращает true , если у посетителя есть права администратора.

Можно создать декоратор checkPermissionDecorator , который добавляет в любую функцию проверку прав:

Например, создадим декоратор checkPermissionDecorator(f) . Он будет возвращать обёртку, которая передаёт вызов f в том случае, если у посетителя достаточно прав:

function checkPermissionDecorator(f) < return function() < if (isAdmin()) < return f.apply(this, arguments); >alert( 'Недостаточно прав' ); > >

Источник

Читайте также:  Java start exe program
Оцените статью