LINUX.ORG.RU

JS. Необходимо избежать использование замыкания

 


0

1
$('#jquery_jplayer_' + last_index).onProgressChange(function(loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime) {
//Некий код
});

Мне необходимо вызвать нужную функцию с аргументами, которые плагин передает в анонимную функцию. Если сделать вызов нужной функции, то она будет вызвана, но аргументы не будут переданы (плагин передает только в анонимную функцию). Вызов нужной функции в замыкании невозможен, ибо эта функция там неопределенна. Что посоветуете?

Заранее благодарю.

★★

спасибо, сломал моск

Захватить в замыкание объект, которому принадлежит функция(window?) и через объект вызвать.
Передавать код функции в виде строки и eval'om её, eval'om.
А вообще фиг знает, что за плагин там у тебя. И чем отличается анонимная функция от замыкания.

Bad_ptr ★★★★★
()
Последнее исправление: Bad_ptr (всего исправлений: 2)
Ответ на: комментарий от Razip
                onProgressChange: function(fn) {
                    $(this).data("jPlayer.jsFn.onProgressChange", fn);
                },
                updateProgress: function(loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime) { // Called from Flash
                    var lbId = $(this).data("jPlayer.cssId.loadBar");
                    if (lbId != null) {
                        $("#" + lbId).width(loadPercent + "%");
                    }
                    var pbId = $(this).data("jPlayer.cssId.playBar");
                    if (pbId != null) {
                        $("#" + pbId).width(playedPercentRelative + "%");
                    }
                    var onProgressChangeFn = $(this).data("jPlayer.jsFn.onProgressChange");
                    if (onProgressChangeFn != null) {
                        onProgressChangeFn(loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime);
                    }
                    if (lbId != null || pbId != null || onProgressChangeFn != null) {
                        this.forceScreenUpdate();
                        return true;
                    } else {
                        return false;
                    }
                }
Razip ★★
() автор топика
Ответ на: комментарий от Razip

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

Razip ★★
() автор топика
Ответ на: комментарий от Bad_ptr
//Работаем с массивов плееров
                        for (var i = 0; i < json.length; i++) {
                            // Добавление полученных данных в выбранный селектор
                            $this.find($settings.selector).append('<div class="player_container"><div id="jquery_jplayer_' + last_index + '" class="jquery_jplayer"></div><ul class="player_controls"><li id="player_play_' + last_index + '" class="player_play"></li><li id="player_pause_' + last_index + '" class="player_pause"></li><li id="player_stop_' + last_index + '" class="player_stop"></li><li id="player_volume_min_' + last_index + '" class="player_volume_min"></li><li id="player_volume_max_' + last_index + '" class="player_volume_max"></li></ul><div class="player_progress"><div id="player_progress_load_bar_' + last_index + '" class="player_progress_load_bar"><div id="player_progress_play_bar_' + last_index + '" class="player_progress_play_bar"></div></div></div><div id="player_volume_bar_' + last_index + '" class="player_volume_bar"><div id="player_volume_bar_value_' + last_index + '" class="player_volume_bar_value"></div></div><div class="player_playlist_message"><div class="song_title">' + json[i].title + '</div><div id="play_time_' + last_index + '" class="play_time"></div><div id="total_time_' + last_index + '" class="total_time"></div></div></div>');

                            $('#jquery_jplayer_' + last_index).jPlayer({
                                ready: (function(last_index, song) {
                                    return function() {
                                        $('#jquery_jplayer_' + last_index).change(song);
                                    };
                                })(last_index, '/uploads/' + json[i].url),
                                cssPrefix: 'different_prefix_example'
                            });
                            $('#jquery_jplayer_' + last_index).jPlayerId('play', 'player_play_' + last_index);
                            $('#jquery_jplayer_' + last_index).jPlayerId('pause', 'player_pause_' + last_index);
                            $('#jquery_jplayer_' + last_index).jPlayerId('stop', 'player_stop_' + last_index);
                            $('#jquery_jplayer_' + last_index).jPlayerId('loadBar', 'player_progress_load_bar_' + last_index);
                            $('#jquery_jplayer_' + last_index).jPlayerId('playBar', 'player_progress_play_bar_' + last_index);
                            $('#jquery_jplayer_' + last_index).jPlayerId('volumeMin', 'player_volume_min_' + last_index);
                            $('#jquery_jplayer_' + last_index).jPlayerId('volumeMax', 'player_volume_max_' + last_index);
                            $('#jquery_jplayer_' + last_index).jPlayerId('volumeBar', 'player_volume_bar_' + last_index);
                            $('#jquery_jplayer_' + last_index).jPlayerId('volumeBarValue', 'player_volume_bar_value_' + last_index);
                            $('#jquery_jplayer_' + last_index).onProgressChange(function(loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime) {
                                window.myPlayedTime[last_index] = new Date(playedTime);
                                window.ptMin[last_index] = (window.myPlayedTime[last_index].getMinutes() < 10) ? '0' + window.myPlayedTime[last_index].getMinutes() : window.myPlayedTime[last_index].getMinutes();
                                window.ptSec[last_index] = (window.myPlayedTime[last_index].getSeconds() < 10) ? '0' + window.myPlayedTime[last_index].getSeconds() : window.myPlayedTime[last_index].getSeconds();
                                /*
                                 * Если выполнить метод jQuery .text() здесь,
                                 * то он не сработает
                                 */
                                $('#play_time_' + last_index).text(window.ptMin[last_index] + ':' + window.ptSec[last_index]);
                                window.myTotalTime[last_index] = new Date(totalTime);
                                window.ttMin[last_index] = (window.myTotalTime[last_index].getMinutes() < 10) ? '0' + window.myTotalTime[last_index].getMinutes() : window.myTotalTime[last_index].getMinutes();
                                window.ttSec[last_index] = (window.myTotalTime[last_index].getSeconds() < 10) ? '0' + window.myTotalTime[last_index].getSeconds() : window.myTotalTime[last_index].getSeconds();
                                $('#total_time_' + last_index).text(window.ttMin[last_index] + ':' + window.ttSec[last_index]);
                            });

                            //Устанавливаем обработчик клика
                            $('#player_play_' + last_index).click((function(last_index) {
                                return function() {
                                    if (window.last_id == undefined) {
                                        window.last_id = last_index;
                                    }

                                    if (window.last_id != last_index) {
                                        $('#jquery_jplayer_' + last_id).trigger('jPlayer.stop');
                                        window.last_id = last_index;
                                    }
                                }
                            })(last_index));

                            //Т.к. мы сгенерировали новый плеер, его ID уже
                            //занят, поэтому нужно инкрементировать последний ID
                            last_index++;

                            /*
                             * Если выполнить метод jQuery .text() здесь,
                             * то он сработает
                             */
                        }

Я расставил многострочные комментарии насчет того, что я имею ввиду.

Razip ★★
() автор топика
Ответ на: комментарий от Razip

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

Razip ★★
() автор топика
Ответ на: комментарий от Razip

Ааа, анонимная функция получает только последние значение, два раза перезаписывает одну переменную.

Razip ★★
() автор топика
Ответ на: комментарий от Razip

не передается текущая переменная, только после всех итераций.

да не передаётяся текущая переменная, только видимо, по ссылке и поэтому она изменяется.

Bad_ptr ★★★★★
()

ЯННП, откуда это строчка - код библиотеки или твой код? Что именно нужно, loadPercent, playedPercentRelative...? Какая функция «нужная»?

Kalashnikov ★★★
()
Ответ на: комментарий от Kalashnikov

Функция уже не нужна, забудьте о ней.

                            $('#jquery_jplayer_' + last_index).onProgressChange(function(loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime) {
                                window.myPlayedTime[last_index] = new Date(playedTime);
                                window.ptMin[last_index] = (window.myPlayedTime[last_index].getMinutes() < 10) ? '0' + window.myPlayedTime[last_index].getMinutes() : window.myPlayedTime[last_index].getMinutes();
                                window.ptSec[last_index] = (window.myPlayedTime[last_index].getSeconds() < 10) ? '0' + window.myPlayedTime[last_index].getSeconds() : window.myPlayedTime[last_index].getSeconds();
                                /*
                                 * Если выполнить метод jQuery .text() здесь,
                                 * то он не сработает
                                 */
                                $('#play_time_' + last_index).text(window.ptMin[last_index] + ':' + window.ptSec[last_index]);
                                window.myTotalTime[last_index] = new Date(totalTime);
                                window.ttMin[last_index] = (window.myTotalTime[last_index].getMinutes() < 10) ? '0' + window.myTotalTime[last_index].getMinutes() : window.myTotalTime[last_index].getMinutes();
                                window.ttSec[last_index] = (window.myTotalTime[last_index].getSeconds() < 10) ? '0' + window.myTotalTime[last_index].getSeconds() : window.myTotalTime[last_index].getSeconds();
                                $('#total_time_' + last_index).text(window.ttMin[last_index] + ':' + window.ttSec[last_index]);
                            });

Внутри onProgressChange() не вставляется текст через jQuery (метод .text()). Дело в том, что такой же код срабатывает, когда находится не в плагине.

В плагин jQuery переменная передается таким образом:

(function($) {

})(jQuery):
Razip ★★
() автор топика
Ответ на: комментарий от Razip

Ну короче ты понял.

#> cat test.js



var last = 0

funcarray = [];

for (var i = 0; i < 10; i++) {
    funcarray[i] = function() {
        console.log("last: " + last)
    }
    last += 1
}

for (var i = 0; i < funcarray.length; i++) {
    funcarray[i]()
}



last = 0
funcarray = [];

function copy_value(last) {
    return function() {
        console.log("last: " + last)
    }
}

for (var i = 0; i < 10; i++) {
    funcarray[i] = copy_value(last)
    last += 1
}

for (var i = 0; i < funcarray.length; i++) {
    funcarray[i]()
} 

#> node test.js

last: 10
last: 10
last: 10
last: 10
last: 10
last: 10
last: 10
last: 10
last: 10
last: 10
last: 0
last: 1
last: 2
last: 3
last: 4
last: 5
last: 6
last: 7
last: 8
last: 9

Bad_ptr ★★★★★
()
Последнее исправление: Bad_ptr (всего исправлений: 1)
Ответ на: Ну короче ты понял. от Bad_ptr

Я понимаю это. Обычно я делаю так:

                            $('#player_play_' + last_index).click((function(last_index) {
                                return function() {
                                    if (window.last_id == undefined) {
                                        window.last_id = last_index;
                                    }

                                    if (window.last_id != last_index) {
                                        $('#jquery_jplayer_' + last_id).trigger('jPlayer.stop');
                                        window.last_id = last_index;
                                    }
                                }
                            })(last_index));

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

Razip ★★
() автор топика
Ответ на: комментарий от Razip

И к тому же, еще, jQuery не вставляет текст.

Razip ★★
() автор топика
Ответ на: комментарий от Razip

Я весь говнокод не читал, но коллбэк onProgressChange наверно асинхронно вызывается, посмотри в каком контексте.

И что значит не сработает? Что в $('#play_time_' + last_index), что в $?

Kalashnikov ★★★
()
Ответ на: комментарий от Razip

Но тот коллбэк не превратить в такое чудо

почему же не превратить — превратить.

потому-что ему передаются аргументы плагином

так возвращай просто функцию с аргументами, так же как ты пишешь return function(){...} точно также можешь сделать return function(arg1,arg2 ...){...}.

Bad_ptr ★★★★★
()
Ответ на: комментарий от Bad_ptr

Ну хорошо, а каким образом передать текущее значение last_index в цикле:

function(loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime) {
                                return function() {
                                    //Некий код
                                };
                            });

Там-то(JS. Необходимо избежать использование замыкания (комментарий)) была анонимная функция в группирующих скобках и с вызовом, в котором был нужный мне аргумент, который должен был доступен. Здесь тоже в скобки взять и запускать, передавая нужный аргумент? Мне кажется такое не прокатит.

Razip ★★
() автор топика
Ответ на: комментарий от Razip

*который должен был быть доступен

Razip ★★
() автор топика
Ответ на: комментарий от Razip

будет как-то так

$('#jquery_jplayer_' + last_index).onProgressChange((function(last_index){
    return function(loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime) {
        window.myPlayedTime[last_index] = new Date(playedTime);
        window.ptMin[last_index] = (window.myPlayedTime[last_index].getMinutes() < 10) ? '0' + window.myPlayedTime[last_index].getMinutes() : window.myPlayedTime[last_index].getMinutes();
        window.ptSec[last_index] = (window.myPlayedTime[last_index].getSeconds() < 10) ? '0' + window.myPlayedTime[last_index].getSeconds() : window.myPlayedTime[last_index].getSeconds();
        $('#play_time_' + last_index).text(window.ptMin[last_index] + ':' + window.ptSec[last_index]);
        window.myTotalTime[last_index] = new Date(totalTime);
        window.ttMin[last_index] = (window.myTotalTime[last_index].getMinutes() < 10) ? '0' + window.myTotalTime[last_index].getMinutes() : window.myTotalTime[last_index].getMinutes();
        window.ttSec[last_index] = (window.myTotalTime[last_index].getSeconds() < 10) ? '0' + window.myTotalTime[last_index].getSeconds() : window.myTotalTime[last_index].getSeconds();
        $('#total_time_' + last_index).text(window.ttMin[last_index] + ':' + window.ttSec[last_index]);
    }})(last_index));
Bad_ptr ★★★★★
()
Ответ на: комментарий от Kalashnikov

$('#play_time_' + last_index) — селектор DOM-элемента в jQuery, в $ объект jQuery если не ошибаюсь (я не знаю как он называется). Насчет контекста загуглю.

Razip ★★
() автор топика
Ответ на: будет как-то так от Bad_ptr

Надо же, работает! Объясните, я не понял, плагин по прежнему передает в первую попавшуюся функцию? В данном случае:

(function(last_index)){

})(last_index);
Razip ★★
() автор топика
Ответ на: комментарий от Kalashnikov

Асинхронно, значит зря я обращался к объекту window. Спасибо за подсказку! :)

Razip ★★
() автор топика
Ответ на: комментарий от Razip

Объясните, я не понял

почитайте книжек каких-нибудь по жс и не только.)

onProgressChange(
(function(last_index)){
  return function(a,b) {

  }
})(last_index);)
Функция 'onProgressChange' принимает аргумент. При передаче аргумента в функцию происходит его вычисление. Т.е. вычисляется выражение
(function(last_index)){
  return function(a,b) {

  }
})(last_index);
Выражение это является вызовом функции, с параметром. сначала первая скобочка:
(function(last_index)){
  return function(a,b) {

  }
})
возвращает анонимную функцию приимающую 1 аргумент. потом вторая скобочка (last_index) — вычисляется значение переменной last_index и передаётся в эту функцию в качестве аргумента, функция выполняется, возвращая другую функцию, которая и передаётся в onProgressChange

Bad_ptr ★★★★★
()
Последнее исправление: Bad_ptr (всего исправлений: 2)
Ответ на: комментарий от Bad_ptr

Спасибо, буду разбираться. :)

Razip ★★
() автор топика
Ответ на: комментарий от Bad_ptr

Хм, то есть переменные те, которые по дефолту передавались в анонимную функцию:

Эти переменные — loadPercent, playedPercentRelative, playedPercentAbsolute, playedTime, totalTime.

Доступные в любом месте, внутри onProgressChange()?

Razip ★★
() автор топика
Ответ на: комментарий от Razip

ты взорвал мой мозг

смотри:

function test(a,b) { return a;}
это определение функции test, которая принимает 2 аргумента. имена этих аргументов могут быть любыми, они используются только внутри функции.

дальше допустим:

var c = 1;
var d = 2;

test(c,d);
тут сначала происходит вычисление аргументов, т.е. test(c,d) становится test(1,2);

дальше внутри функции test именованым параметрам присваиваются соответствующие значения, т.е. a = 1, b = 2; и return a -> return 1;

Это понятно.

теперь анонимные функции:

var anon_func = function(a,b) {return a;}
//anon_func теперь содержит функцию
var temp = anon_func(1,2)
//temp теперь равно 1
объявление анонимной функции и сразу её выполнение:
var temp = (function(a,b){return a;})(1,2)
// temp теперь равно 1
захват переменных:

Любая функция в жс является замыканием и может захватывать переменные:

var a = 1

function test() {
    alert("A: " + a);
}

test(); -> А: 1
a += 1
test(); -> A: 2

Т.е. переменные захватываются по ссылке(по имени).

Что если нужно захватить текущее значение переменной?

var test = (function(some_var){
return function(){alert("А: " + some_var);};})(a)

test(); -> А: 2
a + 1
test(); -> A: 2
Т.е. чтобы захватить значение, нужно передать переменную как параметр. Т.к. при передаче в функцию аргумента он сначала вычисляется, + потом внутрях функции создаётся новое окружение, в котором к имени параметра привязано значение вычисленного аргумента.
var a = 1;
var test = (function(some_var){
return function(){alert("А: " + some_var);};})(a);
тоже самое что
var a = 1;
var anon_func = function(some_var){
                  return function(){
                        alert("А: " + some_var);
                  };
                };

var test = anon_func(a); //а вычисляется в значение 1 и внутри функции создаётся новая переменная some_var со значением 1

Ещё пример:

var a = 1;
var b = 2;
var c = 3;
var d = 4;

function test(a,b) {
  // тут a не равно 1, b не равно 2, т.е. внешние переменные скрыты параметрами.
  // Когда мы тут пишем 'a += 1' мы обращаемся именно к параметру 'а', не к внешней переменной.
 // А вот c и d тут из внешнего окружения.

  var inner_func = function(c,d) {
     //тут a и b обращаются к параметрам функции тест, c и d к параметрам этой анонимной функции.
    c += 1;
    d += 1:
     return function(e,f,g,h) {
        "" + (e+a) + "; " + (f+b) + "; " + (g+c) + "; " (h+d);
     };
  };
  return inner_func(a,b);
}

var result = test(2,3)(1,2,3,4);
console.log(result);

Что тут может быть не понятно???)

Bad_ptr ★★★★★
()
Ответ на: ты взорвал мой мозг от Bad_ptr

Хорошее объяснение, но это-то откуда?

Любая функция в жс является замыканием и может захватывать переменные

test(); -> А: 1
a += 1
test(); -> A: 2

Это просто функция, которая вызывается из внешнего окружения и берёт переменную A *оттуда же*, что в примере и показано. Замыкание же позволяет функции обращаться к нелокальным переменным вне зависимости от окружения, в котором она выполняется (т.е. как в следующем примере). Другими словами, замыкание — это функция плюс фиксированный набор нелокальных переменных, требующихся для её работы.

Соответственно, для работы в любом окружении в замыкании должны быть не ссылки на переменные (которые могут уже не существовать), а их значения в момент создания замыкания. Ключевое слово: создание, поэтому чаще всего замыкание демонстрируют с помощью функции (создателя), которая возвращает вложенную функцию (в составе замыкания). В этом случае легко увидеть, что вложенная функция может работать уже *за пределами* блока кода, в котором она была объявлена и где её нелокальные переменные ещё были определены.

anonymous
()
Ответ на: комментарий от Bad_ptr

ещё интересный, может ТСу пример будет:

'use struct';

var last = 0,
    funcarray1 = [],
    funcarray2 = [];


for (var i = 0; i < 10; i++) {
    funcarray1[i] = function() {
        var l = last; // создание этой переменной будет происходить
        //тогда, когда эта функция будет вызвана, т.е. когда last
        //уже будет = 10
        console.log("last fa1: " + l);
    };
    funcarray2[i] =
        (function(){
            var l = last; //создаём локальную переменную
            // и присваиваем ей текущее значение last
            return function() {
                var b = l;
                console.log("last fa2: " + b);
            };
        }()
         //текущее значение получается благодаря тому что
         //анонимная функция вызывается прямо во время этого цикла
        );
    last += 1;
}

for (var i = 0; i < funcarray1.length; i++) {
    funcarray1[i]();
}
for (var i = 0; i < funcarray2.length; i++) {
    funcarray2[i]();
}

Bad_ptr ★★★★★
()
Ответ на: комментарий от Bad_ptr

Интересно. Кстати, разобрался в понимании того о чем спрашивал сначала.

Мы просто вызываем анонимную функцию, в которую передается текущая переменная в качестве аргумента. А если вызов функции стоит в аргументе — значит то, что возвратит функция — будет передано, вот мы и передали анонимную функцию. Потом jQuery-плагин вызывает эту функцию обратного вызова передавая ей значения.

Вот и все. ;)

Razip ★★
() автор топика
Ответ на: комментарий от Razip

То есть заодно и понял как работают функции обратного вызова.

Razip ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.