Объясните, почему функция работает именно так.
Всем привет.
Есть вопрос: function addThree(x) { return x + 3; } function composed(func) { return function(x) { return func(func(x)) } } var answer = composed(addThree)(4); И не могу понять, что за странная передача аргумента в функцию - composed(addThree)(4) ?? Почему не вот так composed(addThree(4)) - я уже понял, что так нельзя, попробовал. Просто еще не сталкивался с подобным способом написания. Что в таком случае происходит? Ведь функция composed принимает только один параметр - func? Тогда что за странная запись (addThree)(4) ? Новичок. Не судите строго :) Спасибо. |
первая функция передаётся как аргумент, и возвращается новая функция, которая вызовет первую, передав ей первый аргумент.
т.е. при первой скобке возвратится функция, которая будет вызывать переданную и передавать ей первый аргумент, а вторая скобка вызывает её. почему запоминается первая функция? это называется замыкание. |
спасибо
|
Вопрос не по этой теме, но по примеру из данного раздела раздела "Декораторы" сайта learn.javascript.ru:
01 function doublingDecorator(f) { 02 return function() { 03 return 2*f.apply(this, arguments); // (*) 04 }; 05 } 06 07 // Использование: 08 09 function sum(a, b) { 10 return a + b; 11 } 12 13 sum = doublingDecorator(sum); 14 15 alert( sum(1,2) ); // 6 16 alert( sum(2,3) ); // 10 в строке 13 мы записываем в переменную sum результат, и теперь данная переменная не должна указывать на функцию function sum(a, b) {...} Кто может указать (где на сайте или в книге Флэнагана) этот момент можно понять, можно и самостоятельно объяснить. |
Цитата:
Она сохраняется благодаря замыканию. Статья, по которой Я понял замыкания ( я перечитал их около 10 штук), находится на хабре. |
melky, хорошо начинать с js - такие вещи как замыкания и прототипы кажутся очевидными и элементарными с самого начала.)
С другой стороны если берёшся за другой язык - уже "нормальное" ООП кажется тёмным лесом и вообще непонятно нафига козе баян.)) |
Цитата:
|
Может и так, но я еще не до конца въехал.
Замыкание - Объект с переменными внешней функции можно использовать из внутренней функции. (Илья Кантор) Однако все эти замыкания представляют интерес лишь в только что рассмотренной ситуации, когда вложенная функция экспортируется за пределы области видимости, в которой она была определена. Вложенные функции, используемые таким образом, нередко явно называют замыканиями. (Дэвид Флэнаган) Если, здесь это все используется, то может кто-нибудь терминами глобальных объектов, областей видимости и этапами инициализации это все объяснить. И еще, это нормальный вариант вот так делать или это пример "индусского кода" (надеюсь поданного специально чтоб лучше запомнилось, когда закипит мозг) ? ( сразу уточню, что не хотел сказать, что Илья Кантор выдает "индусский код", просто для новичков опытные говорят на "индусском") :). |
Нормально. Но дело вкуса. Я, например, люблю замыкания и "замыкаю" всё подряд, а кто-то не любит.))
|
Я люблю и обожаю, но еще не все примеры разобрал.
Может, объясниш этот случай. |
Цитата:
|
Цитата:
|
Что там разбирать, всё элементарно.
function doublingDecorator(f) { return function() { return 2*f.apply(this, arguments); // (*) }; } // Использование: function sum(a, b) { return a + b; } sum = doublingDecorator(sum); alert( sum(1,2) ); // 6 alert( sum(2,3) ); // 10 Вызываем: doublingDecorator(sum) function doublingDecorator(f) { //,,, } В данном случае f = sum, т.е. условно f = function(a, b) { return a + b; }Собсно эта f находится в scope(области видимости) текущего вызова данной функции. //... return function() { return 2*f.apply(this, arguments); // (*) }это по сути то же самое что: function temp() { return 2*f.apply(this, arguments); // (*) } return tempпросто не создаётся лишних переменных, а идёт возврат на ходу созданной функции. И поскольку функция, создаваемая внутри другой функции, видит все внутренние переменные, она замыкает их все на себя, тем самым сохраняя их в доступности даже после завершения работы материнской функции. Последним действием: sum = doublingDecorator(sum);затирается первоначальная ссылка на sum, т.е. sum = temp. В простом случае это заставило бы сборщик мусора стереть саму память о функции на которую раньше ссылалась sum, но у нас сохранилась ещё одна ссылка на оную - переменная f, замкнутая внутри новосозданной функции temp и, соответственно, вполне работоспособная. f.apply(this, arguments);.apply же просто вызывает нашу функцию f с теми аргументами, что переданы в temp(+подменяет this, но это отдельная тема). В данном же случае можно было поступить и так: function temp(a,b) { return 2*f(a, b); // (*) }но изначальный вариант универсальнее, ибо не привязан к конкретной функции sum и может использоваться с другими. Ещё небольшой примерчик пользы замыканий: function act( num ){ return { minus : function( n ){ return num - n }, plus : function( n ){ return num + n }, } } a = 10; a = act(a); alert( a.plus( 3 ) ); //13 alert( a.minus( -1 ) ); //12 Это работает, т.к. обе функции видят одну и ту же переменную num, что при вызове стала равной a, и, т.к. act уже отработала, эта переменная больше никому недоступна. Не знаю, помог ли я или ещё сильнее запутал, но пускай будет.) |
спасибо, прояснилось
а есть возможность описать как обратиться к f чтоб посмотреть что там (полный путь типа window.-%:";&+%%/!!.#*/&/.f () {return...} (в моем и своем примере) этот момент меня и смущает - неявное создание каких-то промежуточных объектов или непонятное место нахождение замкнутых переменных. Надеюсь я понятно выразился? Хочется понять весь механизм, потому что понять код - я пойму, но сам не "замкну". |
В этом суть замыкания. Эта переменная больше никому не доступна, кроме созданных внутри функций.
|
Цитата:
Цитата:
|
Хочется материться.
Такое впечатление, как будто теорию относительности или Фрейда пытаешься понять, половину приходится принять на веру - раз работает, значит так оно и есть. Но, понял как это все в данном мной примере работает. Самая большая сложность - это учитывать время и ход событий. Типа прошла инициализация (интерпритация / компиляция / (?*%*?-ция) и сложились вот такие объедки, которые находятся в обновленных старых ссылках. Типа в строке 13 происходит перезапись в ссылку sum новой функции которая есть "самостоятельной", а к функции описанной 09 function sum(a, b) { 10 return a + b; обращений производиться уже не будет никогда. :help: Если не лень, можете над моим примером поизвращаться - функционал оставить, а код еще "перезамкнуть" и сделать лаконичнее, используя самые извращенные способы? |
Javascript просто выполняется потоком по порядку, потому не надо ни о каких сложностях париться, как написано так и сработает.)
(исключая некоторые заморочки с var, setTimeout - но это отдельная тема) Цитата:
doublingDecorator(sum)- в функцию doublingDecorator передаётся ссылка на функцию function sum(a, b) { return a + b; }и внутренняя переменная f тоже становится ссылкой на оную. После чего действием sum = doublingDecorator(sum), sum становится ссылкой уже на новую функцию (temp) созданную внутри doublingDecorator, при этом ссылка на оригинальную всё ещё остаётся во внутренней переменной f. |
Kivi, думаю, тебе стоит почитать про замыкания.
|
Цитата:
|
Цитата:
Но уже разобрался, зацените. Я думал, что каждый раз, когда происходит что-то подобное: 15 alert( sum(1,2) ); // 6 16 alert( sum(2,3) ); // 10 интерпретатор проходит весь скрипт сначала, а тут ссылка уже указывает на другую функцию (объект). Но теперь понял, что сформируется уже другая функция и будет вызываться "модифицированная" doublingDecorator. Теперь еще раз перечитал указанный раздел и вижу - ответ там есть, но вот этого нюанса - взгляда с необходимого мне ракурса не было, а уточнение заняло страницу форума. Спасибо за помощь. Просьба остается в силе - буду рад рассмотреть аналоги исполнения. |
Вот еще хрень.
Источник комментарий на habrahabr.ru var y = 5; var x = function(){ return y; }; var z = function(t){ var y = 10; return t(); } z(x); Почему в строке 7 return t();? Что это значит? Где можно об этом прочитать? |
Цитата:
1. y - number 2. x - function 3. z - function z(x); -> вызывается функция z, в которую в качестве аргумента передается другая фунция (x), имя которой (имя аргумента) задается как t. Лучше скажи, почему y = 5 :) var y = 5; var x = function(){ return y; }; var z = function(t){ var y = 10; return t(); } alert( z(x) ); |
По-моему у в
3 return y; - есть window.y=5, а в 6 var y = 10; - это function Lexical Environment. Сначала х(), а потом уже t(), используют window.y=5. На этом примере, можешь показать как ты анализируешь области видимости. Так как это было (дословно), когда просматривал этот код, используешь ли какие-то наборы правил типа этих для this (в разделе "Теория"). Может схемку какую рисуешь? |
Цитата:
// 1 function() { // 2 function() { // 3 } } Достаточно запомнить одно: Дэвид Флэнаган: Цитата:
|
Встречал где-нибдь описание такого
var some_function = new Function('console.log(my_var)'); (именно 'console.log(my_var)') ? Источник - комментарий на habrahabr.ru |
Цитата:
конструктор Function [[Scope]] для new Function |
Дэвид Флэнаган:
Инструкция return может также использоваться без выражения, тогда она про сто прерывает исполнение функции, не возвращая значение. Например: function display_object(obj) { [I] // Сначала убедимся в корректности нашего аргумента // В случае некорректности пропускаем остаток функции[/I] if (obj == null) return; [I] // Здесь находится оставшаяся часть функции...[/I] } И еще пример function foo() { if (false) { var x = 1; } return; var y = 1; } Вопрос: Почему return не в конце? Если поменять местами строки 5 и 6 что-то изменится? |
Цитата:
Цитата:
|
Для возврата значения используется директива return. Она может находиться в любом месте функции.
Как только до нее доходит управление — функция завершается и значение передается обратно. Есть совет как быть внимательнее, отличный от "Будь внимательнее!" ?:) |
Опять хрень!!! Только, кажется, уже все - разобрался и опять...
Здесь (на http://learn.javascript.ru Использование замыканий есть пример: function makeArmy() { var shooters = []; for(var i=0; i<10; i++) { var shooter = function() { // функция-стрелок alert(i); // выводит свой номер }; shooters.push(shooter); } return shooters; } var army = makeArmy(); army[0](); // стрелок выводит 10, а должен 0 army[5](); // стрелок выводит 10... // .. все стрелки выводят 10 вместо 0,1,2...9ну и объяснение К моменту вызова army[0](), функция makeArmy уже закончила работу. Цикл завершился, последнее значение было i=10. Все понятно. А вот чуть ниже чем тут. Еще пример: for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 1000); }и тоже объясняется: Данный код не будет выводить числа с 0 до 9, вместо этого число 10 будет выведено десять раз. Анонимная функция сохраняет ссылку на i и когда будет вызвана функция console.log, цикл for уже закончит свою работу, а в i будет содержаться 10. Моё объяснение (по методу прогиба подреальность): скрипт прошел - все ссылки определились, и не смотря, что в сценарии вывод идет параллельно циклу, на самом деле все не так - вывод в консоль идет в конце. Вопрос: Где почитать когда события по коду идут "одновременно" а когда "по-особенному"? |
Nerv-у
Чувствую, что нужно переходить на ru-board.com - там движение побольше, а не один ты будешь за всех отдуваться. |
function top(){ for(var i = 0; i < 10; i++) { setTimeout(function() { console.log(i); }, 1000); } } Тут всё просто, присмотритесь внимательнее: function() { console.log(i); }console.log(i) выводит не конкретное число, а внутреннюю переменную i из области видимости функции top(или глобальной если цикл находится вне функции). А поскольку это происходит через 1000мс(1с), когда функция top уже отработала, то обращаясь к i функция получает значение внутренней переменной i, которое та приобрела к концу работы функции top, т.е. - 10. Разложить это можно ещё так: for(var i = 0; i < 10; i++) {} //спустя 1000мс console.log(i); console.log(i); console.log(i); console.log(i); console.log(i); console.log(i); console.log(i); console.log(i); console.log(i); console.log(i); По поводу ru-board.com не скажите, жабаскрипетров нам не так много, а компетентных и подавно.) |
А почему так с кодом
for(var i = 0; i < 10; i++) {} //спустя 1000мс console.log(i); console.log(i); console.log(i); console.log(i);ведь (из моего примера) он в теле цикла в {}. Разве не должно быть так: for i=1 console.log(1); for i=2 console.log(2); ... for i=10 console.log(10); поледовательно ? |
Kivi, потому, что значение i нигде не фиксируется. Это можно прочитать как: вывести значение переменной i через n миллисекунд. А через n миллисекунд оно будет равно чему?
Цитата:
Переходить или нет, решать тебе. Могу только отметить, что здесь js мозги очень даже неплохие (это я не о себе). |
Так вот я и хочу узнать механизм.
Внес некоторые изменения в код: for ( var k = 0; k < 10999; k++ ) { document.write( k ) document.write( "<br>" ); setTimeout(function() { document.write( k ); }, 2000 ); }чтоб цикл не успел закончиться, а например document.write(1) должен бы уже напечататься. А все равно ничего. ведь setTimeout идет как тело цикла и пока оно не выполнится не k не должно меняться. Или какое k<XXXXXXX нужно поставить чтоб заработало? |
document.write нельзя использовать в функциях исполняемых не сразу, а по событию или таймеру.
document.write - это особый способ вывода, который выводит код по мере загрузки страницы напрямую в тело(т.е. ещё не живой DOM, а просто в обрабатываемый на ходу код), и после того как страница сформирована и документ закрыт его использовать нельзя. Т.е. document.write можно использовать даже так: <script> document.write('<in'); document.write('pu'); document.write('t'); document.write(' valu'); document.write('e="t'); document.write('ext'); document.write('">'); </script>в отличие от innerHTML который работает уже на живую, а потому каждая выводимая часть обрабатывается сразу как полноценная: <script> function appendHTML( html, node ){ var d = document, fragment = d.createDocumentFragment(), container = d.createElement('div'); container.innerHTML = html; while( container.hasChildNodes() ) fragment.appendChild( container.firstChild ); (node || d.body || d.documentElement).appendChild( fragment ); return node } appendHTML('<in'); appendHTML('pu'); appendHTML('t'); appendHTML(' valu'); appendHTML('e="t'); appendHTML('ext'); appendHTML('">'); </script> Цитата:
Так вот - это не возможно, т.к. асинхронность в js виртуальна и всё исполняется в одном потоке. Вызов же функции по событию или по таймеру, в случае если другой код ещё выполняется, ставит эту функцию в очередь выполнения сразу после окончания текущего непрерывного блока кода. Циклы же относятся как раз тем блокам, что нельзя прерывать. Соответственно сколько бы не итерировался цикл, функция по таймеру не будет вызвана пока оный не закончится. По этому в описании setTimeout всегда пишут не: "вызывает функцию через n", а "вызывает функцию не раньше чем через n", или типа того. Цитата:
Также возможно вас смущает сам синтаксис for? Чтоб было понятнее: function top(){ for(var i = 0; i < 10; i++) { //... } }это то же самое что: function top(){ var i = 0; while(i < 10) { i++; //... } }Т.е. i в цикле не имеет никаких особых свойств, это такая же обычная переменная. |
Часовой пояс GMT +3, время: 18:45. |