绑定要在Function构造函数中使用的函数

时间:2014-04-21 15:40:50

标签: javascript function binding constructor callback

我打算创建一个"预处理"在调用回调之前调用的函数。换句话说,调用回调应遵循以下模式:预处理函数 - >打回来。为了"插入"这样一个预处理函数,我可以简单地创建一个闭包,重写闭包内的回调,以便调用预处理函数,然后在重写的回调结束时,调用原始回调。

var end = function(init) {
    /*
        In here, init is processed.
        Init contains multiple callbacks.
        One callback is chosen to be invoked.
    */
    init.callback();
};

var closure = function(init) {
    var old = init.callback;
    init.callback = function() {
        /*
            Do the preprocessual stuff
        */
        console.log("The preprocessual functionality has now taken place.");
        return old.apply(this, Array.prototype.slice.call(arguments));
    };

    return end.apply(this, Array.prototype.slice.call(arguments));
};

closure({
    /*among other properties*/
    callback: function() {
        console.log("The preprocessual callback must have been invoked when 'end' invokes me.");
    }
});

但是,我有多个回调,而我只有一个预处理功能。每次调用这些回调之前都应该调用相同的预处理函数。为了不必为每个单独的可能回调编写预处理回调,我在闭包中创建了一个循环,将变量old分配给下一个回调,然后使用Function构造函数重写回调。

一切仍然有效。但是,我不再能够在我最初可以访问的回调函数中使用非全局变量。以下崩溃声称variable未定义(根据https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function)。

(function() {
    var end = function(init) {
        /*
            In here, init is processed.
            Init contains multiple callbacks.
            One callback is chosen to be invoked.
        */
        init.callback();
    };

    var closure = function(init) {
        var old = init.callback;
        init.callback = new Function(
            "\
                /*\
                    Do the preprocessual stuff\
                */\
                console.log(\"The preprocessual functionality has now taken place.\");\
                return " + old + ".apply(this, Array.prototype.slice.call(arguments));\
            "
        );

        return end.apply(this, Array.prototype.slice.call(arguments));
    };

    var variable = "value";

    closure({
        /*among other properties*/
        callback: function() {
            console.log("The preprocessual callback must have been invoked when 'end' invokes me.");
            console.log(variable);
        }
    });
})();

然后我想,让我们在回调函数的回调中尝试bind我需要的变量。然后我遇到了一个非常奇怪的问题。由于某种原因,将范围/参数绑定到回调函数(函数构造函数几乎没有用),会导致奇怪的错误。这种错误的一个小例子:

此作品

var callback = function() {
    console.log(arguments);
};

callback = new Function(
    "\
    return " + callback + ".apply(this, Array.prototype.slice.call(arguments));\
    "
);

callback(1, 2, 3);

这不起作用

var callback = function() {
    console.log(arguments);
}.bind(this);

callback = new Function(
    "\
    return " + callback + ".apply(this, Array.prototype.slice.call(arguments));\
    "
);

callback(1, 2, 3);

如果我将回调分配给另一个变量(如old)并在函数构造函数中使用old并不重要,如果我完全使用它并不重要Function构造函数中的不同绑定函数。任何绑定函数(无论是否使用变量引用)都会在元素列表"之后给出错误:" SyntaxError:missing]。

事实上,即便失败

callback = new Function(
    "\
    return " + (function() {}.bind(this)) + ".apply(this, Array.prototype.slice.call(arguments));\
    "
);

callback(1, 2, 3);

我无法弄清楚为什么会这样。非常有用的帮助。

根据要求,实际用例:

var
    ajax = function(init) {
        for (var i = 0, callbacks = ["success", "error"]; i < callbacks.length; i++) {
            if (init.hasOwnProperty(callbacks[i] + "Callback")) {
                init[callbacks[i] + "Callback"] = new Function("responseText",
                    "\
                    /*\
                        Preprocessual callback takes place here (among other things, messages from the server are inserted in the document)\ 
                    */\
                    \
                    return " + init[callbacks[i] + "Callback"] + ".apply(this, Array.prototype.slice.call(arguments));\
                    "
                );
            }
        }

        // This is the actual ajax function, which can operate independently of the project (in contrary, the preprocessual callback needs to know about where to insert messages in the document)
        return cregora.ajax.apply(this, Array.prototype.slice.call(arguments));
    }
;

(function() {
    // some scope with variables..

    ajax({
        url: "url",
        callbackThis: this,
        successCallback: function(responseText) {
            console.log("I need some variables available in this scope");
        },
        errorCallback: function() {
            console.log("I need some variables available in this scope");
        }
    });
})();

1 个答案:

答案 0 :(得分:1)

正如我所料,你实际上过分复杂了这个问题 您可以构建一个更高阶函数,而不是使用函数构造函数,该函数返回适当的处理程序并自动包装函数(如预处理器)。

var callbackWrapper = function (callback) {
    // Returns new anonymous function that acts as the handler
    return function responseHandler (responseText) {
        // Do your pre-processing
        console.log(responseText);
        callback.apply(this, Array.prototype.slice.call(arguments));
    };
};

var ajax = function(init) {
    for (var i = 0, callbacks = ["success", "error"]; i < callbacks.length; i++) {
        var callbackName = callbacks[i] + "Callback";
        if (init.hasOwnProperty(callbackName)) {
            var callback = init[callbackName];
            init[callbackName] = callbackWrapper(callback);
        }
    }

    // This is the actual ajax function, which can operate in independent of the project (for example, the preprocessual callback needs to know about where to insert messages in the document)
    return cregora.ajax.apply(this, Array.prototype.slice.call(arguments));
};


(function() {
    // some scope with variables..

    ajax({
        url: "url",
        callbackThis: this,
        successCallback: function(responseText) {
            console.log("I need some variables available in this scope");
        },
        errorCallback: function() {
            console.log("I need some variables available in this scope");
        }
    });
})();

如果您在意,您甚至可以更改callbackWrapper,以便每次使用完全相同的预处理器功能:

var callbackWrapper = (function createCallbackWrapper () {
    var preProcessor = function (responseText) {
        console.log(responseText);
    };

    return function callbackWrapper (callback) {
        // Returns new anonymous function that acts as the handler
        return function responseHandler (responseText) {
            var args = Array.prototype.slice.call(arguments);
            preProcessor.apply(this, args);
            callback.apply(this, args);
        };
    };
})();

现在,通过绑定原始回调函数,您将完全没有问题。

关于这个问题的更多解释:

使用fn + ".apply(...)"时,JS会将原始函数转换为字符串。这就是为什么你很难访问闭包变量,或任何不在你的var closure函数范围或全局范围内的东西。

在你的情况下它也失败了,因为在函数上调用.bind之后,它的字符串表示变为"function () { [native code] }"。 这当然不是一个有效的功能体,会给你带来很多麻烦。

转换为字符串是实际问题,而且它是一个不容易解决的问题。出于这个原因,使用new Function几乎从来都不是正确的解决方案,一旦你发现自己使用它,你应该假设你在推理中犯了一个错误。如果你没有,而new Function确实是唯一的解决方案,那么你就知道了。