Javascript 理解闭包:构造将函数排队的元函数

Javascript 理解闭包:构造将函数排队的元函数,javascript,function,lambda,closures,Javascript,Function,Lambda,Closures,在解决问题方面,我有一个完全有效的解决方案,我刚刚在这里完成: // synchronous dynamic script loading. // takes an array of js url's to be loaded in that specific order. // assembles an array of functions that are referenced more directly rather than // using only nested closure

在解决问题方面,我有一个完全有效的解决方案,我刚刚在这里完成:

// synchronous dynamic script loading. 
// takes an array of js url's to be loaded in that specific order. 
// assembles an array of functions that are referenced more directly rather than 
// using only nested closures. I couldn't get it going with the closures and gave up on it. 

function js_load(resources, cb_done) {
    var cb_list = []; // this is not space optimal but nobody gives a damn 
    array_each(resources, function(r, i) {
        cb_list[i] = function() {
            var x = document.body.appendChild(document.createElement('script'));
            x.src = r;
            console.log("loading "+r);
            x.onload = function() { 
                console.log("js_load: loaded "+r); 
                if (i === resources.length-1) {
                    cb_done();
                } else {
                    cb_list[i+1]();
                }
            }; 
        };
    });
    cb_list[0]();
}
我对此非常满意,因为它现在做了我想要的事情,并且可能比我的第一种方法(如果它成功的话)更容易调试

但我无法克服的是为什么我永远无法让它发挥作用

它看起来像这样

function js_load(resources, cb_done) {
    var cur_cont = cb_done;
    // So this is an iterative approach that makes a nested "function stack" where 
    // the inner functions are hidden inside the closures. 
    array_each_reverse(resources, function(r) {
        // the stack of callbacks must be assembled in reverse order
        var tmp_f = function() {
            var x = document.body.appendChild(document.createElement('script'));
            x.src = r;
            console.log("loading "+r);
            x.onload = function() { console.log("js_load: loaded "+r); cur_cont(); }; // TODO: get rid of this function creation once we know it works right 
        };
        cur_cont = tmp_f; // Trying here to not make the function recursive. We're generating a closure with it inside. Doesn't seem to have worked :(
    });
    cur_cont();
}
它一直试图在一个无限循环中调用自己,还有其他一些奇怪的事情,在调试过程中,很难识别函数是哪个函数以及函数中包含什么

我没有深入研究代码,但似乎
jQuery.queue
也实现了与我的工作机制类似的机制(使用数组跟踪连续队列),而不是只使用闭包

我的问题是:是否有可能构建一个Javascript函数,它可以将一个函数作为参数,并通过构建封装它自己创建的函数的闭包,用一系列其他函数来增强它

这真的很难描述。但我相信有人对它有一个正确的理论支持的数学术语

上述代码引用的p.S.是这些例程

// iterates through array (which as you know is a hash), via a for loop over integers
// f receives args (value, index)
function array_each(arr, f) {
    var l = arr.length; // will die if you modify the array in the loop function. BEWARE
    for (var i=0; i<l; ++i) {
        f(arr[i], i);
    }
}

function array_each_reverse(arr, f) {
    var l = arr.length; // will die if you modify the array in the loop function. BEWARE
    for (var i=l-1; i>=0; --i) {
        f(arr[i], i);
    }
}
//通过整数上的for循环遍历数组(如您所知,这是一个散列)
//f接收参数(值、索引)
函数数组_每个(arr,f){
var l=arr.length;//如果在循环函数中修改数组,则会导致死亡。小心
对于(变量i=0;i=0;--i){
f(arr[i],i);
}
}

问题在于如何为每个新函数设置
cur\u cont
的值,并在
onload
回调中调用
cur\u cont
。当您进行类似于
tmp\u f
的闭包时,任何自由变量(如
cur\u cont
)都不会“冻结”到其当前值。如果
cur\u cont
发生任何变化,则
tmp\u f
中对其的任何引用都将引用新的更新值。由于您不断地将
cur_cont
更改为您刚刚创建的新
tmp_f
函数,因此对其他函数的引用将丢失。然后,当执行并完成
cur\u cont
时,再次调用
cur\u cont
。这正是刚刚完成执行的同一个函数-因此是无限循环

在这种情况下,需要将自由变量的值保存在闭包中,最简单的方法是创建一个新函数,并使用希望保留的值调用它。通过调用此新函数,将为该运行创建一个新变量,该变量将保留所需的值

function js_load(resources, cb_done) { var cur_cont = cb_done; array_each_reverse(resources, function(r) { // the stack of callbacks must be assembled in reverse order // Make a new function, and pass the current value of the `cur_cont` // variable to it, so we have the correct value in later executions. // Within this function, use `done` instead of `cur_cont`; cur_cont = (function(done) { // Make a new function that calls `done` when it is finished, and return it. // This function will become the new `cur_cont`. return function() { var x = document.body.appendChild(document.createElement('script')); x.src = r; console.log("loading "+r); x.onload = function() { console.log("js_load: loaded "+r); done(); }; }; })(cur_cont); }); // Start executing the function chain cur_cont(); }
请注意,
reduce
reduceRight
不适用于较旧的浏览器(仅供参考:JavaScript有一个内置的
[1,2,3].forEach
function您的函数之所以递归,是因为您没有对
cur\u cont
的值创建闭包。当处理程序执行时,它们都使用
cur\u cont
的最后一个值。太好了!感谢关于Array.reduce的提示。这里使用的是很好的ol'FP概念:我确实用了很艰难的方法来解决这个问题
function js_load(resources, done) {
    var queue = resources.reduceRight(function(done, r) {
        return function() {
            var x = document.body.appendChild(document.createElement('script'));
            x.src = r;
            console.log("loading "+r);
            x.onload = function() {
                console.log("js_load: loaded "+r);
                done();
            };
        };
    }, done);

    queue();
};