Просмотр полной версии : Функции внутри функций - почему это зло?
Добрый день,
извините за назойливость, но мне необходимо понять - почему плохо создавать функции внутри функций?
И как правильно организовать следующий код?
public function loadImg():Void {
var mclLoader:MovieClipLoader = new MovieClipLoader();
var oLoadListener:Object = new Object();
mclLoader.addListener(oLoadListener);
function onLoadError(mcTarget:MovieClip, sErrorCode:String):Void {}
}
Поиск довольно внятно ответил мне, что это "*****код", но хочется обоснования...:wacko:
class имя класса {
var mclLoader:MovieClipLoader = new MovieClipLoader ();
public function конструктор класса () {
mclLoader.addListener(this);
}
public function loadImg():Void {
}
private function onLoadError (mcTarget:MovieClip, sErrorCode:String):Void {
}
}
dashaaa, ну во-первых, приведенный вами код не будет работать, просто потому что onLoadError не принадлежит объекту oLoadListener. А вложенные функции плохи тем, что во первых, плывет область видимости и начинаются проблемы с обращением к различным свойствам и объектам, во вторых, вместо того, что создать метод один раз в памяти, он постоянно пересоздается новый.
Дело в том, что функция просто выполняется при вызове, и выполнившись, возвращает результат или меняет чтото вне себя. После того, как она выполнилась, все ее внутренние переменные больше недоступны, точнее их уже нет. Все эти переменные - mclLoader, oLoadListener, и функция onLoadError() существуют лишь до тех пор, пока выполняется loadImg(), потому что это ее внутренние переменные. Они как пассажиры пролетающего вагона - могут разговаривать друг с другом, но Вы, на полустаночке), ни словом с ними не перекинетесь. Пролетая мимо, они Вам могут крикнуть "загрузи картинку и отдай Васе на пятом сиденье", но пока Вы сбегаете за картинкой - ни Васи, ни сиденья.
(из моего поста могло создаться впечатление, что дело во времени, но на самом деле конечно в областях видимости - т.е. нормальная организация подразумевает, что Вася стоит с Вами на полустаночке. Т.е. функция не должна быть внутри другой функции)
class имя класса {
var mclLoader:MovieClipLoader = new MovieClipLoader ();
public function конструктор класса () {
mclLoader.addListener(this);
}
public function loadImg():Void {
}
private function onLoadError (mcTarget:MovieClip, sErrorCode:String):Void {
}
}
Compiler Error, line 2 - A class's instance variables may only be initialized to compile-time constant expressions.
Но вообще-то идея ясна, спасибо.
__etc, спасибо, с областью видимости уже столкнулась, и то, что метод пересоздаётся - это аргумент.
Wolsh, спасибо, понятно =)
Mnilionic
29.10.2008, 20:42
создавайте экземпляр MovieClipLoader в конструкторе класса
chingachgoog
29.10.2008, 21:28
Функция внутри функции - это нормальное явление, просто надо знать для чего это нужно.
То, что "поезд уходит" тоже не совсем верно:
function A(){
var x="123"
B()
function B(){
link=this
this.x
}
}
A()
trace(link.x)
А вообще посмотрите тему Битва гигантов... (http://flasher.ru/forum/showthread.php?t=113128), там как раз ближе к концу объекты активации исследуются.
Mnilionic
29.10.2008, 21:35
хы-хы
в делигаторе функция внутри функции, но там правда ссылка на неё живая
Division
29.10.2008, 21:47
Функция внутри функции - это нормальное явление, просто надо знать для чего это нужно.
А для чего нужно, если не секрет? Мне вообще кажется что это довольно удобно в некоторых случаях. Например, если большая функци выполняет много однотипных действий, их можно вынести во вложенную функцию. ИМХО.
А для чего нужно, если не секрет?Только для реализации Delegate. Других оправданных примеров использования я не вижу.
Мне вообще кажется что это довольно удобно в некоторых случаях. Например, если большая функци выполняет много однотипных действий, их можно вынести во вложенную функцию.Выше было сказано, что это приводит к неоправданному расходу памяти. Делайте приватный метод и выносите в него.
chingachgoog, мы с Вами как-то спорили про клипы и кнопки, так вот это примерно из той же серии. Когда человек чётко представляет что и как работает, он безусловно просто выберет действительно оптимальный вариант для решения своей задачи. Например, создаст ядерное оружие вместо электростанции))) Правда при этом другие будут на него косо смотреть, примерно как я смотрел бы на коллегу, принёсшего мне такое вот рэндзю из активаций. Почти всегда можно найти способ, в народе именуемый хаком, и взломать головы своим соратникам, пытающимся где-нибудь в 2659-й строке понять, откуда что взялось. А можно просто сделать так, как удобно, понятно и предсказуемо. Хитрые головоломные ходы должны использоваться только там, где без них никак. Делегате - да, отличный пример такой необходимости. Для упрощения этой необходимости он и создан))))
chingachgoog
30.10.2008, 02:15
Область видимости никуда "не плывет" и почему должна "расходоваться память"? Просто область видимости иная (собственно это и причина применения) и память занята функцией временно.
Но механизм действия функции в функции очень сложен, придумать для чего это надо - не просто. Потому распространен стереотип - "это зло", "*****код" и т.д. А это просто еще одна из редко используемых возможностей флеша, к тому же, мало кому понятная.
PS Попытался вспомнить когда я такую конструкцию применял на практике - не получилось :)
chingachgoog, можно у себя в подъезде гадить, утверждая, что со временем испарится и выветрится, а можно ходить в специально отведенные для этого места. И так называемый стереотип, на является примером ясного и красивого написания кода, который не вызывает проблем и вопросов "а почему не работает", также как вшивание шрифта через библиотеку. А расход памяти создать можно запросто, сохранив ссылку на функцию.
chingachgoog
30.10.2008, 09:42
iNils, я уловил аналогию между словами "*****код" и "гадить в подъезде", но не между "гадить в подъезде" и "вложенные функции".
Да, вложенные функции не есть "ясное написание кода, которые не вызывает проблем". Но разве, например, умножение на двое побитовым сдвигом - это "ясное написание кода, которое не вызывает проблем"? В таком случае умножение побитовым сдвигом - это "гадить в подъезде", т.к. есть "специально отведенное место" - простая запись "*2".
chingachgoog, а при чем тут побитовый сдвиг? Это как школьник, которого мапа ругает за двойку, начинает оправдываться словами "А вот у Васи родители его за двойку не ругают". Не подтягивать в качестве примера то, о чем речь и не заходила, вынуждая нас оправдываться за кого-то там, кто так делает и тут вообще не присутствует.
Побитовый сдвиг используется тогда, когда нужно думать именно категорией сдвига, а не умножения. Как пример, работа с RGB и его составляющими.
†‡Paladin‡†
30.10.2008, 12:20
Функции в функции не считаю чем-то из ряда вон. В некоторых случаях такая реализация только улучшит читаемость кода.
http://habrahabr.ru/blogs/webdev/38642/ - советую прочитать про то, как это используется в javascript`е
chingachgoog
30.10.2008, 12:44
Речь зашла о "ясном написание кода, которое не вызывает проблем". И тут побитовый сдвиг (примененный, скажем для увеличения производительности расчета, по сравнению с простым *2) никакой ясности не внесет и запросто вызовет проблемы. Ну и что? Применять побитовый сдвиг только при работе с RGB?
Нельзя Васи ставить двойку за то, что он решил задачу способом иным, чем предполагал учитель:
Сэр Эрнеcт Резерфорд, президент Королевской академии и лауреат Нобелевской премии по физике рассказывал такую историю:
Однажды к нему обратился коллега за помощью. Он собирался поставить самую низкую оценку по физике одному из своих студентов, в то время как тот утверждал, что заслуживает высшего балла. Оба — преподаватель и студент — согласились положиться на суждение третьего лица, незаинтересованного арбитра. Выбор пал на Резерфорда. Экзаменационный вопрос гласил: «Объясните, каким образом можно измерить высоту здания с помощью барометра?».
Ответ студента был таким: «Нужно подняться с барометром на крышу здания, спустить барометр вниз на длинной верёвке, а затем втянуть его обратно и измерить длину верёвки, которая и покажет точную высоту здания».
Случай был и впрямь сложный, так как ответ был абсолютно полным и верным! С другой стороны, экзамен был по физике, а ответ имел мало общего с применением знаний в этой области.
Резерфорд предложил студенту попытаться ответить ещё раз. Дав ему шесть минут на подготовку, он предупредил его, что ответ должен демонстрировать знание физических законов. По истечении пяти минут студент так и не написал ничего в экзаменационном листе. Резерфорд спросил его, сдаётся ли он, но тот заявил, что у него есть несколько решений проблемы, и он просто выбирает лучшее.
Заинтересовавшись, Резерфорд попросил молодого человека приступить к ответу, не дожидаясь истечения отведённого срока. Новый ответ на вопрос гласил: «Поднимитесь с барометром на крышу и бросьте его вниз, замеряя время падения. Затем, используя формулу, вычислите высоту здания».
Тут Резерфорд спросил своего коллегу преподавателя, доволен ли он этим ответом. Тот, наконец, сдался, признав ответ удовлетворительным. Однако студент упоминал, что знает несколько ответов, и его попросили открыть их.
— Есть несколько способов измерить высоту здания с помощью барометра, — начал студент. — Например, можно выйти на улицу в солнечный день и измерить высоту барометра и его тени, а также измерить длину тени здания. Затем, решив несложную пропорцию, определить высоту самого здания.
— Неплохо, — сказал Резерфорд. — Есть и другие способы?
— Да. Есть очень простой способ, который, уверен, вам понравится. Вы берёте барометр в руки и поднимаетесь по лестнице, прикладывая барометр к стене и делая отметки. Сосчитав количество этих отметок и умножив его на размер барометра, вы получите высоту здания. Вполне очевидный метод.
— Если вы хотите более сложный способ, — продолжал он, — то привяжите к барометру шнурок и, раскачивая его, как маятник, определите величину гравитации у основания здания и на его крыше. Из разницы между этими величинами, в принципе, можно вычислить высоту здания. В этом же случае, привязав к барометру шнурок, вы можете подняться с вашим маятником на крышу и, раскачивая его, вычислить высоту здания по периоду прецессии.
— Наконец, — заключил он, — среди множества прочих способов решения данной проблемы лучшим, пожалуй, является такой: возьмите барометр с собой, найдите управляющего и скажите ему: «Господин управляющий, у меня есть замечательный барометр. Он ваш, если вы скажете мне высоту этого здания».
Тут Резерфорд спросил студента, неужели он действительно не знал общепринятого решения этой задачи. Он признался, что знал, но сказал при этом, что сыт по горло школой и колледжем, где учителя навязывают ученикам свой способ мышления.
Студент этот был Нильс Бор (1885–1962), датский физик, лауреат Нобелевской премии 1922 г.
В некоторых случаях такая реализация только улучшит читаемость кода. В каких?
Нельзя Васи ставить двойку за то, что он решил задачу способом иным, чем предполагал учитель:Слышу звон, да не знаю где он. Цитировать надо к месту, по смыслу, а не по общему слову "двойка".
chingachgoog
30.10.2008, 13:18
Слышу звон, да не знаю где он.
Когда я слышу звон типа "*****код", "быдлокод" (возможны вариации), то скороее всего звонарь либо очень высокого мнения о себе и судит по своим стереотипам (типа как учитель Бора), либо просто не понимает смысла кода (очень характерно для вложенных функций и связанного с этим объектов активации).
add:
Цитировать надо к месту, по смыслу, а не по общему слову "двойка".
Там не было цитаты - это просто ассоциация :)
chingachgoog, спутать пример с двойкой, смысл которого в том, что надо за себя отвечеть, с примером о низкой оценкой за не закостенелость мышления, является либо полным не пониманием о чем идет речь, либо разведением демагогии. Если первое, то ладно, люди ошибаются, если второе, то с этим во флейм, а здесь это будет пресекаться.
Не, ну если измерять высоту здания веревкой с привязанным грузом - пример незакостенелости мышления в пику показаниям барометра - то да, дошли до демагогии и я самоудаляюсь. chingachgoog, Вы даже не поняли, что в своей борьбе за свободу индейцев привели пример самого настоящего быдлокода, и то что его автор - Нильс Бор - отнюдь не делает чести ни "коду" ни Бору. Если бы товарищ Бор в этой ситуации предложил НОВЫЙ метод, тогда да, можно поговорить о незакостенелости. А все эти "методы" оба физика прекрасно знали, просто не были таким быдлом, чтоб забивать микроскопом гвозди. В следующий раз для демагогии выбирайте примеры поудачней.
Так. Ставим жирную точку . в вопросе о двойках, подъездах, смещениях и тп. И возвращаемся к вопросу о функции в функции.
Если есть примеры оправданного применения, то вперед.
chingachgoog
30.10.2008, 15:09
Если требуется функцией обработать локальные переменные другой функции, то ничего лучше встроенной функции нет - можно не беспокоится о перекрывании имен переменных.
OK. Применение замыканий (closure), упомянутое †‡Paladin‡†, иногда способно улучшить читабельность. Пример два года висит в моей подписи))) Думаю, никто не будет спорить что запись mdb.onPress = playSong("MyDyingBride");
depmode.onPress = playSong("DepecheMode");
intro.onPress = playSong("Intro");выглядит читабельной и легко воспринимается, хотя синтаксически неверна, т.к. в ней обработчику присваивается результат выполнения функции playSong(). Но если playSong() возвращает функцию(!), замкнув в ее коде переданный параметр - идентификатор песни, то всё здорово. Когда таких вызовов много, имхо проще написать один замыкатель, который при инициализации кода заполнит все обработчики нужными функциями. Может и быдловато, ну так и я не программист)))) Есть опасения, что память здесь действительно страдает)
chingachgoog
30.10.2008, 15:57
Вычисления с локальными переменными в функции А должны быть достаточно громоздкими, чтобы потребовалась функция В. Это теория, а практического примера, как я говорил, у меня нет. Наверное еще не попадалась необходимая громоздкая обработка внутренних переменных вкупе с необходимостью изолировать ее.
var per="внешняя"
function A(par){
var per="внутренняя А"
function B(){
this.per
var per="внутренняя В"
trace ("B:\r"+per+"\r"+this.per)
}
trace ("А:\r"+per+"\r"+this.per)
B()
C()
}
A()
function C(){
this.per
var per="внутренняя C"
trace ("C:\r"+per+"\r"+this.per)
}
add:
Wolsh, у вас выходит аналог делегата, а это типа "не считается" :)
mc1.onPress=f_set("1")
mc2.onPress=f_set("2")
function f_set(par){
function setPar(){
trace(par)
}
return setPar
}
Данный код годится только для понимания, как это работает. А практической ценности в нем ноль. Там не только отсутствие фантазии в названии переменных (вспоминается D12 со своими z zz zzz zzzzzzz), но и потенциальная путаница в самих переменных.
laurisgutmanis
30.10.2008, 16:39
Смысл инициализировать каждый раз заново функцию - непонятен.
Объяснить довольно сложно, почему так нельзя(не следует, не правильно и т.д.).
И простит меня iNils: но как объяснить человеку, что казарму следует мыть половой щёткой, а не зубной. Это довольно сложно. И доказать тоже.
Как минимум при применении методов "инициализация функции в функции" получается избыточная работа программы. При больших и серьёзных проектах это не допустимо.
А если боишься забыть или потеряться:
1) Называй функцию вменяемо - благо от размера имени функции во Flash ничего не меняется
2) Пиши комментарии
Такие вопросы часто встречаю у своих учеников(правда по PHP).
chingachgoog
30.10.2008, 17:55
Данный код годится только для понимания, как это работает. А практической ценности в нем ноль. Там ...потенциальная путаница в самих переменных.
iNils, извини, что не оправдал надежд на фантазию с названием переменных :) но код именно для понимания, что путаницы с переменными можно избежать при помощи внутренних функций.
Практическую ценность я не встречал, а специально выдумывать не охота.
но код именно для понимания, что путаницы с переменными можно избежать при помощи внутренних функций
Опять театр абсурда.
Подскажите, как в такой ситуации правильно организовать функции, чтобы не было проблем с вложенными функциями и с областями видимости:
class Room {
static var xmlName:String = "1.xml";
var xmlDoc:XML;
var xml_rootNode:XMLNode;
public function parsXML():Void {
trace(xml_rootNode);
}
public function Init():Void {
xmlDoc.load(xmlName);
// ошибка - обработчик внутри функции.
// хотя может и нет - обработчик нужен 1 раз и удаляется после использования
// ?
xmlDoc.onLoad = function():Void {
if (this.status == 0) {
trace("Успешно");
xml_rootNode = this.firstChild;
// !!!!! ошибка - эта функция "не видна" отсюда
parsXML();
} else {
trace("Ошибка при чтении xml "+this.status);
}
};
}
public function Room() {
xmlDoc = new XML();
}
}
import mx.utils.Delegate;
class Room {
static var xmlName:String = "1.xml";
var xmlDoc:XML;
var xml_rootNode:XMLNode;
public function parsXML():Void {
trace(xml_rootNode);
}
public function Init():Void {
xmlDoc.load(xmlName);
// ошибка - обработчик внутри функции.
// хотя может и нет - обработчик нужен 1 раз и удаляется после использования
// ?
}
public function Room() {
xmlDoc = new XML();
xmlDoc.onLoad = Delegate.create (this, onLoadXML);
}
private function onLoadXML (success:Boolean):Void
//// this теперь ссылается на экземпляр класса Room
if (success) {
trace("Успешно");
xml_rootNode = xmlDoc.firstChild;
// !!!!! ошибка - эта функция "не видна" отсюда
parsXML();
} else {
trace("Ошибка при чтении xml "+xmlDoc.status);
}
};
}
Спасибо большое! Я до этого понятия не имела о Delegate.
faust.d503
27.11.2008, 19:35
А зачем использовать Delegate, по-моему есть более простой и удобный способ:
class Room {
public function Room() {
var self:Room = this
xmlDoc = new XML();
xmlDoc.onLoad = function(){
self.onLoadXML();
}
}
private function onLoadXML (success:Boolean):Void
};
}
faust.d503, это кривой и неудобный способ.
faust.d503
28.11.2008, 13:18
__etc
Расскажите, чем он кривой и неудобный и чем лучше Delegate. Нет конечно можно подключить дополнительный класс, чтобы повысить вес программы, но зачем.
Кстати написать функцию прямо внутри конструктора, тоже вполне логичный способ, если парсер используется один раз. Ибо размножать количество методов и переменных класса пусть, и приватных, не всегда хорошо.
Такой обработчик нельзя удалить, он будет висеть в памяти до упора, плюс требует self-ссылки, которая тоже живет в памяти, потому что живет функция.
faust.d503
28.11.2008, 13:32
Такой обработчик нельзя удалить
Почему?
class Room {
public function Room() {
var self:Room = this;
xmlDoc = new XML();
xmlDoc.onLoad = function(){
self.onLoadXML();
//удаляем)
delete this.onLoad;
}
}
private function onLoadXML (success:Boolean):Void
};
}
Да, но тогда анонимная функция с большой вероятностью останется жить, потому что есть self-ссылка, используемая внутри.
О, уже костыли пошли, а было "более простой и удобный способ".
chingachgoog
28.11.2008, 13:51
А по-моему, если что и называть пренебрежительно "костылем", то как раз сам Delegate.
И то и другое костыль, но self-ссылки — костыль в несколько раз больше, и не один.
И то и другое костыль, но self-ссылки — костыль в несколько раз больше, и не один.Абсолютно согласен. Преимущество делагата в том, что мы не меняем область видимости this в любом месте класса.
faust.d503
28.11.2008, 14:22
Да, но тогда анонимная функция с большой вероятностью останется жить, потому что есть self-ссылка, используемая внутри.
self объявлено внутри конструктора, анонимная функция существует там же. Как они остануться жить после выполнения кода констуктора мне с трудом понятно.
На self ссылка после выполнения конструктора убивается, остается ссылка внутри безымянной функции, на которую ссылка отсутствует, по идее должна убиться.
Как и что убивается при использования Delegate мне неизвестно, если известно вам, поясните пожалуйста.
А вообще рассуждения "с большей вероятностью" чистая демагогия.
self объявлено внутри конструктора, анонимная функция существует там же. Как они остануться жить после выполнения кода констуктора мне с трудом понятно.
На self ссылка после выполнения конструктора убивается, остается ссылка внутри безымянной функции, на которую ссылка отсутствует, по идее должна убиться.Все поубивались после конструктора. И куда же тогда придет сообщении о загрузке?
chingachgoog
28.11.2008, 16:11
А что вообще делегат делает? Возвращает функцию оболочку с нужной функцией вызова apply:
class Room {
public function Room() {
xmlDoc = new XML();
xmlDoc.onLoad = function (){
return onLoadXML.apply(this, arguments);
}
}
private function onLoadXML (success){}
}
По-любому генерится лишняя функция.
return onLoadXML.apply(this, arguments);
Ага, только в этом коде область видимости не изменится.
По-любому генерится лишняя функция.
Так она генерится в обоих костылях.
chingachgoog
28.11.2008, 17:29
Ага, только в этом коде область видимости не изменится.
Да, это просто схема работы делегата. (код нерабочий, если кто не понял. Чтобы сделать его рабочим надо добавить ссылку на функцию onLoadXML. Например, так: xmlDoc.onLoadXML=onLoadXML)
Но суть в создании лишней функции-оболочки.
Чтобы сделать его рабочим надо добавить ссылку на функцию onLoadXML. Например, так: xmlDoc.onLoadXML=onLoadXML)
Опять же, this внутри onLoadXML будет ссылаться на xmlDoc, а не на экземпляр Room. Снова область видимости будет неправильной.
chingachgoog
28.11.2008, 19:38
Опять же, this внутри onLoadXML будет ссылаться на xmlDoc, а не на экземпляр Room. Снова область видимости будет неправильной.
Наоборот, правильной. Вместо вымученной делегатом.
Сразу видно, что обработчик onLoad принадлежит объекту xmlDoc и соответственно this внутри обработчика принадлежит xmlDoc.
Код без делегатов более понятный и логичный. Не зря же делегаты - пользовательский класс, а не встроенный.
chingachgoog, боже мой, вы снова начали гнуть свою извращенную линию мысли.
Любой метод, описанный в классе, обязан вызываться в области видимости этого класса, без вариантов. Если вы считаете иначе — ваши проблемы.
Сразу видно, что обработчик onLoad принадлежит объекту xmlDoc и соответственно this внутри обработчика принадлежит xmlDoc.Любой нормальный редактор на this вам выдаст автокомплитом свойства/методы класса, а не xml. А mtasc начнет ругаться на несоответствие типов.
Код без делегатов более понятный и логичный. Не зря же делегаты - пользовательский класс, а не встроенный.Может вам и понятный. А программистам нет, поэтому в as3 делегат уже не нужен, там область видимости не меняется.
chingachgoog
28.11.2008, 20:40
Если вы считаете иначе — ваши проблемы.
А у меня проблем нет - я знаю, на что у меня в каждом конкретном случае this ссылается.
Может вам и понятный. А программистам нет, поэтому в as3 делегат уже не нужен, там область видимости не меняется.
То что в AS3 нет вымороченных делегатов, как раз и говорит, что они программистам были непонятны. Впрочем, в AS3 своих "делегатов" хватает...
То что в AS3 нет вымороченных делегатов, как раз и говорит, что они программистам были непонятны. Впрочем, в AS3 своих "делегатов" хватает...Прекращайте нести чушь.
А у меня проблем нет - я знаю, на что у меня в каждом конкретном случае this ссылается.
Пока вы пишете и видите свой код только у себя в каморке на коленке — пожалуйста. Но показывать свой код, а тем более учить других писать в подобной форме я вам настоятельно не рекомендую. И упаси боже кому-нибудь придется модифицировать ваш код. Лучше сразу повеситься.
То что в AS3 нет вымороченных делегатов, как раз и говорит, что они программистам были непонятны. Впрочем, в AS3 своих "делегатов" хватает...
Бред.
Работает на vBulletin ® версия 3.7.3. Copyright ©2000-2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Copyright © 1999-2008 Flasher.ru. All rights reserved.