251 читатель, 13 постов
Администрация
Модераторы
Разработка приложений с использованием Adobe Flex
this функции её родителя. Звучит просто, но все ли понимают, что это значит и как тяжело было раньше без этого?public class TestClass
{
var property : Number;
function updateValue(value : Number) : void
{
TestClass(this).property = value;
}
}var func : Function = new TestClass().updateValue;
func(555);
this будет экземпляр класса TestClass. Но я не об этом, есть более любопытные действия, которые можно производить над функциями в Action script, их мы их рассмотрим.ServerService, который принимает в конструктор ссылку на функцию, которая должна обработать ответ и мы выполняем типичную задачу обновления свойства исходного объекта:class Example
{
function updateItem(item : SomeObject) : void
{
_tempObject = item;
new ServerService(onGetResult).getResult(item.startValue);
}
function onGetResult(result : Object) : void
{
_tempObject.endValue = result;
}
private var _tempObject : SomeObject;
}function updateItem(item : SomeObject) : void
{
new ServerService(onGetResult).getResult(item.startValue);
function onGetResult(result : Object) : void
{
item.endValue = result;
}
}onGetResult имеет доступ ко всем переменным функции updateItem и к её аргументу item в частности. Такой прием во многих случаях может сократить объем кода и убрать негативный оттенок асинхронности. Кстати, в this функции onGetResult будет уже не экземпляр Example, а просто global.function updateItems(items : ArrayCollection) : void
{
for each (var item : SomeObject in items)
{
new ServerService(onGetResult).getResult(item.startValue);
}
function onGetResult(result : Object) : void
{
item.endValue = result;
}
}item будет ссылаться на последний элемент коллекции items и все данные присвоятся только ему, слишком много чести! В таких ситуациях не помогает ни сохраняемый контекст функции ни область видимости переменных родителя, тут нужно что-то другое.Loader-ом:function updateItems(items : ArrayCollection) : void
{
for each (var item : SomeObject in items)
{
new ValueLoader(item);
}
}
class ValueLoader
{
public function ValueLoader(item : SomeObject)
{
new ServerService(onGetResult).getResult(item.startValue);
function onGetResult(result : Object) : void
{
item.endValue = result;
}
}
}item для обновления его после ответа сервера, мы создаем над функцией обёртку — класс, которые способен запомнить в контексте всё что нужно. Так как конструктор класса всё та же функция, аргумент item без проблем будет доступен в функции onGetResult.Loader-ов, можно ввести универсальный тип — паттерн для многократного использования:class ContextFunction
{
public function ContextFunction(targetFunction : Function, ... args)
{
_contextArgumnets = args;
_targetFunction = targetFunction;
}
public function func(... args) : void
{
var targetArguments : Array =
args.concat(_contextArgumnets);
_targetFunction.apply(this, targetArguments);
}
private var _contextArgumnets : Array;
private var _targetFunction : Function;
}ContextFunction определяется ссылкой на функцию с конкретной логикой и набором неопределённых аргументов, которые получит функция, когда её кто-то вызовет. Так же, к этим аргументам добавятся ещё что-то, по желанию вызывающей сущности. Рассмотрим пример для прояснения:function updateItems(items : ArrayCollection) : void
{
for each (var item : SomeObject in items)
{
new ServerService(new ContextFunction(onGetResult, item).func).
getResult(item.startValue);
}
}
function onGetResult(result : Object, item : SomeObject) : void
{
item.endValue = result;
}Loader-ом, только более универсальное. Экземпляр ContextFunction сохраняет onGetResult, которая получит ответ от сервера, а также ссылку на item для которого запрашивалось серверное значение. То-есть, мы, отказываясь от контекста функции вообще, используем экземпляр вспомагательного класса, для сохранения нужных значений.
комментарии (42)
И надо иметь хорошие знания и опыт в цепочке видимости (scope-chain) и постоянно понимать что onGetResult — это вовсе не метод а функциональная кложура (не знаю как перевести closure, но слово «кожура» — кстати, подходит :)
В командной разработке, думаю, лучше не прибегать к таким методам, ато у новичков от постоянных багов сорвет башню.
На практике, даже в очень больших и запутанных бизнес-логикой проектах, подобные решения как раз таки упрощали код и его понимание. Как минимум на предмет отстутствия бесконечных временных приватных переменных классов для сохранения обрабатываемых объектов.
Такой не скромный вопрос, вы когда-нибудь работали в команде хотя бы 5+ человек?
Ранее всегда работал в командах от 3-4 человек. Я понимаю, что вы стандартно клоните, к тому, что такой код тяжело сопровождать новичкам на проектах — это абсурд. В вышеприведённом коде нет особо секретных костылей или дёрти-хаков, он прост как визуально так и семантически.
Да и ещё раз да, листенеры нужно удалять вручную, причём, желательно, в тех же местах где они были добавлены. Но, бывают ситуации, когда достаточно расчитывать на слабые ссылки. Если желаете могу приподнести простой пример.
1) реально обоснованно
2) невозможно использовать стандартный метод подписки, отписки.
Режим strict mode включён всегда, надеюсь у вас тоже.
Возьму пример из начала текста.
Функцию-то описать можно, но этот код не скомпилируется в том виде, в каком вы его привели, т.к. отсутствует конструктор, принимающий параметр. Будет сгенерирован по дефолту пустой конструктор, который не принимает аргументов, и при попытке туда что-то передать вылезет ошибка.
Я допускаю, что наличие правильного конструктора подразумевается, но это не для всех очевидно, некоторых читателей вы вводите в заблуждение.
Кстати вообще для создания таких функций я бы лучше воспользовался статическим методом класса, вместо того, чтобы для каждой новой функции плодить объекты, которые будут висеть в памяти до великого пришествия GC.
Ещё пример:
Далеко не сразу я нашёл объявление _tempObject. Когда я в первый раз читал статью, я решил что оно вообще пропущено, и думаю, не я один. Учитывая тему, к которой относится пример, сразу становится неочевидно, к какой области видимости относится данная переменная, что вносит большую путаницу. Просто потому что переменные и константы всегда определяются до описания методов. Хотя тут я скорее всего придираюсь к мелочам.
Надеюсь, вы воспримете это как конструктивную критику, или даже убедите меня в моей неправоте. Ибо споры между уважающими друг друга оппонентами — процесс довольно приятный и познавательный :)
Да, в первом примере можно использовать статический метод, а можно вообще ничего не использовать — пример фиктивен. Это просто предисловие к основной теме про контексты, не стоит его воспринимать буквально.
По-поводу _tempObject, даже не знаю как прокоментировать, это не то что-бы придирание к мелочам, это какое-то тонкое издевательство, надеюсь вы шутили =).
Про конструктор я говорил относительно этой строчки:
TestClass(this).property = value;
операция приведения типов, сдесь она не обязательна, я написал для наглядности.
Не следует путать запись выше с:
Какую логику вы в неё закладываете?
Это и есть суть примера, который не стоит принимать как пример для написания кода, только для понимания.
В as2 эта строчка бы выкинула эксепшен, так как метод исполнился бы не в контексте нашего класса, а в том контексте, откуда он был вызван.
*/
Один коммент мог заменить всю нашу беседу :)
flasher.ru/forum/showthread.php?t=122122
Про хендлеры было упоминание выше, я согласен, что полагаться на сборщик не стоит, да и дебажить такие конструкции не слишком удобно.
Кроме того, лично для меня польза того, что обработчик находится в вызывающим асинхронный запрос методе (а по сути в одной области видимости) не совсем очевида. Буду рад если объясните.
livedocs.adobe.com/flex/3/html/help.html?content=03_Language_and_Syntax_21.html
www.mikechambers.com/blog/2008/10/08/function-closures-and-this-in-actionscript-3/
www.transcendentaxis.com/dthompson/blog/archives/19#more-19
В чём неудобство отладки таких конструкций? Брейкпоинты в них обрабатываются Flex Builder-ом, стек трейс и состояние памяти показается. Где неудобства?
Да, основная суть предложеных подходов — сокращение объёма кода и улучшение семантики асинхронного кода (не разрывая её приватными переменными для временного хранения обрабатываемых объектов). Если вам это не нужно — не используйте.
Есть ещё несколько вариантов использования вложеных функций, например передавая их в методы массивов forEach(), filter() — им будут доступны все переменные родительской функции и это можно использовать для поэлементной обработки.