Pull to refresh

Comments 37

Люди, я тут — новенький, поэтому могу немного «косячить». Извиняюсь за «много букв», но статья по-моему завершена по своей структуре.

И ещё: я не знаю как, но очень хотелось бы перенести её в блог «Javascript»
Спасибо всем за карму. Перенёс в Javascript.
что-то как-то все чересчур сложно )

Ext.override(Function, {
  decorate: function(fPre, fPost) {
    var _this = this;
    return function () {
      if (fPre) fPre.call(this, arguments);
      var res = _this.apply(this, arguments);
      if (fPost) fPost.call(this, res, arguments);
      return res;
    };
  }
});


>>> var add = function (a, b) {return a+b;}
>>> add1 = add.decorate(function(args){console.info('function called with args:', args)}, function(res){console.info('end returned:',res)})
function()
>>> add1(2,5)
function called with args: [2, 5]
end returned: 7
7
Согласен, в чём-то у меня сложней, чем у ExtJS :)

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

А во-вторых, мой механизм является более универсальным:
вот смотрите, код Ext'а использует _this, который просто вызывается внутри декоратора. В то время, как мой декоратор имеет внутри себя ссылку на оригинальную ф-цию, что важно. Мой декоратор по сути полностью «оборачивает» исходную ф-цию, и манипулирует ей внутри себя как хочет. В то время, как Ext-овский вариант просто добавляет вызовы «до» и «после».

Ну и в моём варианте я могу не просто «навешивать» вызовы «до» и «после», но и убирать их. И это не вызовет code mess.
дело не в Ext, это просто сокращенная запись

Function.prototype.decorate = function () ...
А, вы про сам Ext.override?

Так ведь приведённый мой Function.method(...) — в точности то же самое, только аргументы передаются не Object'ом, как в Ext.override, а именнем+значением.
ну остальной код я сам придумал, а не взял из Ext )
А, вот оно что :)
В зачатках мой код был очень похож на ваш. Но чем дальше в лес, тем толще партизаны.

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

Глянул в тот же Ext, на объект Observable, и понял, что необходимо иметь addListener и removeListener.

Ну и идея пошла дальше Ж)
Да, и моя реализация before('smth', ...) — может отменить выполнение основной ф-ции )
Отличная вещь.
С помощью этого можно трекать вызовы в браузерах, где нету нормальных JS отладчиков. Хотя, помоему, автор задумывал её для другого :)
Ели честно, то ноги у идеи растут именно оттуда, откуда вы сказали :)
Мне надо было сделать debug вызова некоторых ф-ций в IE. Можно, конечно, просто «остановить» поток коммандой debugger. Но что если в браузере нет отладчика, или нет доступа к исходному коду?
Вообщем, если эта статья народу понравится, я мог бы подумать над применениями моего кода.
Собственно, можете предлагать, а я смогу это учесть :)
Про исходный код вы что-то не то говорите. В чужом можно точки остановки ставить.
Можно. Но что, если у вас нет возможности их ставить? Например, у вас — Internet Explorer 6, без script debugger-плагина. Или Opera 8.5
Вот и я про то же. Если есть дебаггер, то нет проблемы доступа к исходному коду. В любом другом случае, если нет доступа к исходному коду и нет дебаггера, то ваш метод тоже не поможет.
Ну, ключевое слово debugger может ничего и не даст, если отсутствует хоть какой-то debugger. Однако в любом браузере можно написать нечто следующее:
javascript:f=function(x){alert(x)};f=f.decorate(function(x){alert('decorator: '+x); arguments.callee.old.apply(this, arguments)});f(5);

* This source code was highlighted with Source Code Highlighter.

прямо в адресной строке любого браузера. Так и дебажить, простым alert() :-)
Век живи — век учись… Спасибо.
Отличная статья. Не без недочетов, но тем не менее, плюс и топику, и в карму.

Недостатки (имхо):
1) Изменение базовых объектов (Function, Array, Object, etc.) — порочная практика.
Пример:
Вот вы объявили метод Function.prototype.method, который расчитывает, что к нему данные придут в порядке «имя нового метода», function. А кто-то другой написал метод с таким же названием, только ожидает данных в обратном порядке. Вам и этому кому-то все равно — вы-то разберетесь, что происходит. А кто-нибудь третий, подключив и ваш, и чужой код, оба ваших кода, получит кашу, и будет не знать, куда бежать.

2) Путица вида «класс, класс Function, ой, в Javascript нет классов» в пункте «Нырнем на пару метров вглубь». Лучше бы определиться.
>> 1) Изменение базовых объектов (Function, Array, Object, etc.) — порочная практика.
Совершенно согласен. Но всё-таки Function.method, Function.decorate и Function.restore — красиво. От остального можно избавиться.

>> 2) Путица вида «класс, класс Function, ой, в Javascript нет классов» в пункте «Нырнем на пару метров вглубь». Лучше бы определиться.
Извиняюсь, мне просто было удобно использовать слово «класс». Википедия пишет так:
Javascript uses prototypes instead of classes for defining object properties, including methods, and inheritance. It is possible to simulate many class-based features with prototypes in Javascript.
А значит классов здесь никаких нет :)
Вы со словом «класс» создаёте сильную путаницу в тексте. Даже если отвлечься от того, что в JS нет классов, саму по себе функцию (Object, Function, YourClassName) обычно не называют классом, а называют конструктором класса. Сам класс — это нечто более абстрактное: совокупность конструктора, его прототипа и всяких других вещей.
Ну хорошо, в будущем я постараюсь не использовать слово "класс" в контексте Javascript :)

Я навёл справки в документации ECMA-262: там используются следующие термины для функций:
1) Метод — функция, хранимая в свойстве объекта.
2) Конструктор — объект типа Function, который создаёт и инициализирует объекты.
3) А также функцию можно называть объектом типа Function.

Заметьте: просто конструктор, но не конструктор класса, и это принципиально.

Почитать ещё можно тут: http://javascript.ru/ecma/part4
У вас какая-то путаница образовалась. В коде функции strictArgs вы пишете «var types = arguments», а в пояснении ниже «var types = [].slice.apply(arguments)».
Спасибо, что заметили. Сначала я использовал [].slice.apply(arguments), но потом подумал, что это излишне, и заменил на просто arguments. Но получается, что заменил не везде :-) Я это исправил.
Нет там путаницы. Это аргументы разных функций (обратите внимание на то что возвращается тоже ф-ция)
UFO just landed and posted this here
Раз уж вы дали здесь рекламу своего фреймворка Ajaxpect, напишу на него свою небольшую критику :-)
Я почитал ваш код и выделю следующее:
Плюсы:
1) Отвязанность от прототипов стандартных объектов, вроде Object и Function (всё делается через объект Ajaxpect)
2)Продвинутый и довольно удобный поиск методов в объекте:
* можно задать имя метода
* можно задать функцию-фильтр, которая возвращает true, если переданное в неё имя метода соответствует неким условиям
* можно задать RegExp, который также фильтрует имена методов объекта
Теперь минусы:
1) Самое плохое — нет возможности снять навешенный декоратор. Сейчас у вас это можно сделать только внутри самого декоратора, но довольно хитро. А если навесить декоратор дважды, то такая возможность и вовсе исчезнет.
2) Само разделение методов addBefore, addAfter и addAroundнедоработано. К примеру:
* addAfter легко выкидывается и реализуется через addAround, т.к. вы передаёте в декоратор и оригинальную ф-цию, и её аргументы
* addBefore — вы сначала выполняете функцию-декоратор, которая по сути возвращает изменённые arguments в оригинальную ф-цию, и тут же вызываете оригинальную ф-цию. Декоратор «before» должен иметь возможность остановить выполнение оригинальной ф-ции.

Это основное, что я заметил.

Теперь давайте сравним с моим творением — у меня всё с точностью до наоборот:
Минусы:
1) Я «засорил» стандартный объект Function.
2) У меня самый простой способ поиска метода: по имени.
3) Да, и у меня нет в явном виде вашего addAround. Но он легко реализуем у меня через Function.decorate(...).
Но плюсы:
1) Есть removeBefore(...) и removeAfter(...).
2) «Хорошая» организация стэка listener'ов
3) «Хорошая» реализация addBefore: если хоть один из addBefore-listener'ов возвращает false, оригинальная ф-ция не выполнится.

По мощности моя реализация равна вашей. но я бы вам советовал разложить всё по полочкам и учесть мои замечания. К тому же, я написал эту статью в ознакомительных целях. По сути мой код можно оформить в отдельный framework, но я пока не хочу этого делать :-)
Критиковал я Ajaxpect версии 0.9.0, скачал которую в 11:30 UTC time с code.google.com/p/ajaxpect/, чтобы потом на меня не наехали, что ничего такого в коде нет :)
UFO just landed and posted this here
Ясно. Ну значит есть повод ещё разок обновить код ;-)
Класс! С помощь вашего метода можно реализовать аспектно-ориентированный подход для Javascript.
Угу. Но я побоялся использовать словосочетание «аспектно-ориентированный подход» в своей статье, т.к. ничего об этом в ней не писал.
Только прочитав название статьи, сразу подумал про АОП.
UFO just landed and posted this here
UFO just landed and posted this here
статья потрясная! узнал новые вещи про js. очень полезно!
чувак, ты крут…
очень интересная статья, спасибо
ЗЫ Javascript меня не перестает удивлять…
Sign up to leave a comment.

Articles