Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to call function.apply without changing the context?

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.

like image 229
Matt Huggins Avatar asked Sep 09 '12 02:09

Matt Huggins


People also ask

Is it possible to change functions context of execution?

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.

What is the difference between call () and apply ()?

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.

Can we call a function without passing arguments?

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!

How do you change the context of a function?

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."


2 Answers

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] 
like image 106
Brian McCutchon Avatar answered Sep 18 '22 10:09

Brian McCutchon


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); } 
like image 37
Dave Avatar answered Sep 18 '22 10:09

Dave