有没有可能调用function.apply而不改变上下文?

在一些Javascript代码(特别是node.js)中,我需要在不改变上下文的情况下调用具有未知参数集的函数。 例如:

function fn() { var args = Array.prototype.slice.call(arguments); otherFn.apply(this, args); } 

在上面的问题是,当我打电话apply ,我通过传递this作为第一个参数改变上下文。 我想传递args的函数被调用, 而不改变被调用的函数的上下文。 我基本上想这样做:

 function fn() { var args = Array.prototype.slice.call(arguments); otherFn.apply(<otherFn's original context>, args); } 

编辑:添加更多关于我的具体问题的细节。 我正在创build一个Client类,其中包含有关连接的其他信息的套接字(socket.io)对象。 我通过客户端对象本身暴露套接字的事件侦听器。

 class Client constructor: (socket) -> @socket = socket @avatar = socket.handshake.avatar @listeners = {} addListener: (name, handler) -> @listeners[name] ||= {} @listeners[name][handler.clientListenerId] = wrapper = => # append client object as the first argument before passing to handler args = Array.prototype.slice.call(arguments) args.unshift(this) handler.apply(this, args) # <---- HANDLER'S CONTEXT IS CHANGING HERE :( @socket.addListener(name, wrapper) removeListener: (name, handler) -> try obj = @listeners[name] @socket.removeListener(obj[handler.clientListenerId]) delete obj[handler.clientListenerId] 

请注意, clientListenerId是一个自定义的唯一标识符属性,它与在此处find的答案基本相同。

this对你的函数的上下文的引用。 这确实是重点。

如果你的意思是在这样一个不同的对象的上下文中调用它:

 otherObj.otherFn(args) 

然后简单地将该对象replace为上下文:

 otherObj.otherFn.apply(otherObj, args); 

应该是这样的。

如果我正确理解你的话

  changes context | n | y | accepts array n | func() | func.call() | of arguments y | ???????? | func.apply() | 

PHP有一个函数call_user_func_array 。 不幸的是,在这方面JavaScript是缺乏的。 看起来你使用eval()来模拟这个行为。

 Function.prototype.invoke = function(args) { var i, code = 'this('; for (i=0; i<args.length; i++) { if (i) { code += ',' } code += 'args[' + i + ']'; } eval(code + ');'); } 

是的我知道。 没有人喜欢eval() 。 这是缓慢和危险的。 但是,在这种情况下,您可能不必担心跨站脚本,至less所有variables都包含在函数中。 真的,JavaScript不具备本地function,但我想这是我们有eval

certificate它的工作原理:

 function showArgs() { for (x in arguments) {console.log(arguments[x]);} } showArgs.invoke(['foo',/bar/g]); showArgs.invoke([window,[1,2,3]]); 

Firefox控制台输出:

 -- [12:31:05.778] "foo" [12:31:05.778] [object RegExp] [12:31:05.778] [object Window] [12:31:05.778] [object Array] 

简单地说,就是把这个分配给你想要的,这就是otherFn

 function fn() { var args = Array.prototype.slice.call(arguments); otherFn.apply(otherFn, args); } 

如果将函数绑定到一个对象,并且在任何地方使用了绑定函数,则可以使用null调用apply,但仍然可以获得正确的上下文

 var Person = function(name){ this.name = name; } Person.prototype.printName = function(){ console.log("Name: " + this.name); } var bob = new Person("Bob"); bob.printName.apply(null); //window.name bob.printName.bind(bob).apply(null); //"Bob" 

在调用函数时,可以解决JavaScript中可能发生的上下文更改的一种方法是,如果您需要这些方法是对象构造函数的一部分,那么可以在不打算意思是父对象,通过有效地创build一个本地私有variables来存储原始的this标识符。

我承认,就像大多数关于JavaScript范围的讨论一样,这个问题还不是很清楚,所以这里是我如何做到的一个例子:

 function CounterType() { var counter=1; var self=this; // 'self' will now be visible to all var incrementCount = function() { // it doesn't matter that 'this' has changed because 'self' now points to CounterType() self.counter++; }; } function SecondaryType() { var myCounter = new CounterType(); console.log("First Counter : "+myCounter.counter); // 0 myCounter.incrementCount.apply(this); console.log("Second Counter: "+myCounter.counter); // 1 } 

我不打算接受这个答案,因为我仍然希望有更适合的东西。 但是,到目前为止,基于对这个问题的反馈,我现在正在使用这个方法。

对于任何将调用Client.prototype.addListenerClient.prototype.removeListener ,我都将下面的代码添加到它们的构造函数中:

 class ExampleClass constructor: -> # ... for name, fn of this this[name] = fn.bind(this) if typeof(fn) == 'function' message: (recipient, body) -> # ... broadcast: (body) -> # ... 

在上面的示例中, messagebroadcast将在实例化时始终绑定到新的ExampleClass原型对象,从而允许我的原始问题中的addListener代码正常工作。

我相信你们中有些人想知道为什么我不只是做下面的事情:

 example = new ExampleClass client.addListener('message', example.bind(example)) # ... client.removeListener('message', example.bind(example)) 

问题是每次调用.bind( )都是一个新的对象。 所以这意味着以下是正确的:

 example.bind(example) != example.bind(example) 

因此, removeListener将永远不会成功工作,因此,在实例化对象时,我将该方法绑定一次。

由于您似乎想要使用Javascript 1.8.5中定义的bind函数,并且能够检索传递绑定函数的原始对象,所以我build议重新定义Function.prototype.bind函数:

 Function.prototype.bind = function (oThis) { if (typeof this !== "function") { throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () {}, fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); /** here's the additional code **/ fBound.getContext = function() { return oThis; }; /**/ return fBound; }; 

现在,您可以使用以下方法检索您称为bind函数的原始上下文:

 function A() { return this.foo+' '+this.bar; } var HelloWorld = A.bind({ foo: 'hello', bar: 'world', }); HelloWorld(); // returns "hello world"; HelloWorld.getContext(); // returns {foo:"hello", bar:"world"}; 

经过很长时间,我才想起这个问题。 现在回想起来,我认为我真正想要完成的事情与React库如何与自动绑定工作类似。

实质上,每个函数都是一个被调用的绑定函数:

 function SomeClass() { }; SomeClass.prototype.whoami = function () { return this; }; SomeClass.createInstance = function () { var obj = new SomeClass(); for (var fn in obj) { if (typeof obj[fn] == 'function') { var original = obj[fn]; obj[fn] = function () { return original.apply(obj, arguments); }; } } return obj; }; var instance = SomeClass.createInstance(); instance.whoami() == instance; // true instance.whoami.apply(null) == instance; // true 

只需将属性直接推送到函数的对象,并用它自己的“上下文”来调用它。

 function otherFn() { console.log(this.foo+' '+this.bar); // prints: "hello world" when called from rootFn() } otherFn.foo = 'hello'; otherFn.bar = 'world'; function rootFn() { // by the way, unless you are removing or adding elements to 'arguments', // just pass the arguments object directly instead of casting it to Array otherFn.apply(otherFn, arguments); }