In some Javascript code (node.js specifically), I need to call a function with an unknown set of arguments without changing the context. For example:
function fn() { var args = Array.prototype.slice.call(arguments); otherFn.apply(this, args); }
The problem in the above is that when I call apply
, I'm change the context by passing this
as the first argument. I'd like to pass args
to the function being called without changing the context of the function being called. I essentially want to do this:
function fn() { var args = Array.prototype.slice.call(arguments); otherFn.apply(<otherFn's original context>, args); }
Edit: Adding more detail regarding my specific question. I am creating a Client class that contains a socket (socket.io) object among other info pertaining to a connection. I am exposing the socket's event listeners via the client object itself.
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]
Note that clientListenerId
is a custom unique identifier property that is essentially the same as the answer found here.
If we want to, we can dynamically change the execution context of any method by using either call() or apply(). Both of these functions can be used to bind the "this" keyword to an explicit context.
The Difference Between call() and apply() The difference is: The call() method takes arguments separately. The apply() method takes arguments as an array. The apply() method is very handy if you want to use an array instead of an argument list.
to answer your question concerning calling functions without their parameters. it's possible for functions where default values for all parameters are set e.g.: def foo(bar = "default string"): print(bar) print(foo()) # prints: default string print(foo("hello world!")) # prints: hello world!
var object = {}; function fn(){ return this; } assert( fn() == this, "The context is the global object." ); assert( fn. call(object) == object, "The context is changed to a specific object."
If I understand you correctly:
changes context | n | y | accepts array n | func() | func.call() | of arguments y | ???????? | func.apply() |
PHP has a function for this, call_user_func_array
. Unfortunately, JavaScript is lacking in this regard. It looks like you simulate this behavior using 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 + ');'); }
Yes, I know. Nobody likes eval()
. It's slow and dangerous. However, in this situation you probably don't have to worry about cross-site scripting, at least, as all variables are contained within the function. Really, it's too bad that JavaScript doesn't have a native function for this, but I suppose that it's for situations like this that we have eval
.
Proof that it works:
function showArgs() { for (x in arguments) {console.log(arguments[x]);} } showArgs.invoke(['foo',/bar/g]); showArgs.invoke([window,[1,2,3]]);
Firefox console output:
-- [12:31:05.778] "foo" [12:31:05.778] [object RegExp] [12:31:05.778] [object Window] [12:31:05.778] [object Array]
Simply put, just assign the this to what you want it to be, which is otherFn:
function fn() { var args = Array.prototype.slice.call(arguments); otherFn.apply(otherFn, args); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With