8

我需要结合 JavaScriptcall()apply()方法的强大功能。我遇到的问题是call()保留了对 的正确引用this,但是当我需要它作为函数参数发送时,将我拥有的参数数组作为数组发送。使用数组时,该apply()方法将参数发送给函数就好了,但我不知道如何向它发送对该方法似乎自然可以访问的正确this引用call()

下面是我拥有的代码的简化版本,它可能看起来毫无用处,但它是一个很好的理解要点的方法:

// AN OBJECT THAT HOLDS SOME FUNCTIONS
var main = {};
main.the_number = 15;
main.some_function = function(arg1, arg2, arg3){
    // WOULD VERY MUCH LIKE THIS TO PRINT '15' TO THE SCREEN
    alert(this.the_number);
    // DO SOME STUFF WITH THE ARGUMENTS
    ... 
};
// THIS STORES FUNCTIONS FOR LATER.
//  'hub' has no direct knowledge of 'main'
var hub = {};
hub.methods = [];
hub.methods.push(main.some_function);
hub.do_methods = function(arguments_array){
    for(var i=0; i<this.methods.length; i++){
        // With this one, '15' is printed just fine, but an array holding 'i' is 
        //  just passed instead if 'i' itself
        this.methods[i].call(arguments_array);   
        // With this one, 'i' is passed as a function argument, but now the
        //  'this' reference to main is lost when calling the function 
        this.methods[i].apply(--need a reference to 'main' here--, arguments_array); 
    }
}
4

6 回答 6

9

什么?Apply 也通过了范围...

method.apply(this, [args]);

编辑:

在您的代码中,您在包含范围内定义了主要对象,因此您可以简单地执行;

this.methods[i].call(main);

或者

this.methods[i].apply(main, [args]);
于 2012-05-22T18:52:06.437 回答
2

使用时applycall第一个参数是您要this设置的参数。

call接受参数列表:

.call(main, i, j)

apply接受一个数组或参数:

.apply(main, [i, j])

所以,在这一行:

this.methods[i].call([i]); 

[i]这作为thisinside传递this.methods[i]

你可能想做:

this.methods[i].call(main, i);

这将调用this.methods[i]、设置thismain并传递i给它。

或者:

this.methods[i].call(main, arguments_array);

这将调用this.methods[i]、设置thismain并将 的元素arguments_array作为参数传递。

于 2012-05-22T18:54:12.267 回答
1

只需将对父对象的引用附加到函数对象:

main.some_function = function () {
    //...
}
main.some_function.parent = main;

// now some_function.parent holds a ref to main

或者,如果您愿意,可以关闭救援:包括对main您定义时的引用main.some_function

// self-execution function the returns a function with `that` (i.e., main) in scope
main.some_function = (function(that) {
    return function() {
        alert(that.the_number);
    }
})(main);
于 2012-05-22T19:10:06.117 回答
1

当我阅读前面答案中的评论时,我认为您需要某种绑定。您可以使用 jQuery 或类似的库,也可以自己实现:

var bound_some_function = function(){
    return main.some_function.apply(main, arguments);
}
// ...
hub.methods.push(bound_some_function);
// ...
this.methods[i].apply(null /*whathever, has no effect*/, arguments_array);

绑定函数的通用定义如下所示:

function bind(fun, scope){
    return function(){
        return fun.apply(scope, arguments);
    };
}

有关绑定的进一步阅读,请谷歌“Javascript 函数绑定”。有很多关于它的文章。

于 2012-05-22T19:16:32.233 回答
0

我之前有不正确的信息(在我删除的答案中),因为应用不会对绑定函数产生影响(如上面的答案);

只需添加一个简单的代码即可证明:

/*Lets suppose we have a class "MessageReader" that has a method printMessage
 * that for some reason needs to receive the message to be printed from a function,
 * not directly a value.*/
function MessageReader() {
    this.value = "This is NOT a message and is not supposed to be readed!";
}
/*getMessage needs to return a string that will be printed!*/
MessageReader.prototype.printMessage = function(getMessage) {
    console.log(getMessage.apply(this, []));
}

function Message(value) {
    this.value = value;
}
Message.prototype.getMessage = function() {
    return this.value;
}


var myMessageReader = new MessageReader();
var myMessage1 = new Message("This is a simple message");
var myMessage2 = new Message("This is another simple message");

/*This will print the wrong message:*/
myMessageReader.printMessage(myMessage1.getMessage);

/*But this works!*/
myMessageReader.printMessage(myMessage2.getMessage.bind(myMessage2));
于 2015-08-26T14:50:56.567 回答
0

.call()和方法在.apply()与 的处理方面没有区别this。来自 MDN 文档:

虽然此函数的语法与 apply() 的语法几乎相同,但根本区别在于 call() 接受参数列表,而 apply() 接受单个参数数组。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

不幸的是,使用 call / bind / apply 调用函数并维护它的原始this引用是不可能的,但是,这看起来很丑陋,有一种解决方法可以通过

function applyOriginalThis(fn, parametersArray)
{
    var p = parametersArray;

    switch (p.length)
    {
        case 0: fn(); break;
        case 1: fn(p[0]); break;
        case 2: fn(p[0], p[1]); break;
        case 3: fn(p[0], p[1], p[2]); break;
        case 4: fn(p[0], p[1], p[2], p[3]); break;
        case 5: fn(p[0], p[1], p[2], p[3], p[4]); break;
        case 6: fn(p[0], p[1], p[2], p[3], p[4], p[5]); break;
        case 7: fn(p[0], p[1], p[2], p[3], p[4], p[5], p[6]); break;
        case 8: fn(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); break;
        case 9: fn(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); break;
        default: throw "Too many parameters.";
    }
}

当然,这仅适用于您希望支持的参数数量。从好的方面来说,采用过多参数的函数是代码异味。另外,如果我没记错的话,AngularJS 1.x 中使用了这种类型的代码模式作为性能优化.apply()(现在找不到参考)。

于 2016-07-20T16:22:18.570 回答