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 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);
}
'this' is a reference to your function's context. That's really the point.
If you mean to call it in the context of a different object like this:
otherObj.otherFn(args)
then simply substitute that object in for the context:
otherObj.otherFn.apply(otherObj, args);
That should be it.
If you bind the function to an object and you use everywhere the bound function, you can call apply with null, but still get the correct context
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"
One way that you can work around the change of context that can happen in JavaScript when functions are called, is to use methods that are part of the object's constructor if you need them to be able to operate in a context where this is not going to mean the parent object, by effectively creating a local private variable to store the original this identifier.
I concede that - like most discussions of scope in JavaScript - this is not entirely clear, so here is an example of how I have done this:
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
}
These days you can use rest parameters:
function fn(...args) {
otherFn(...args);
}
The only downside is, if you want to use some specific params in fn, you have to extract it from args:
function fn(...args) {
let importantParam = args[2]; //third param
// ...
otherFn(...args);
}
Here's an example to try (ES next version to keep it short):
// a one-line "sum any number of arguments" function
const sum = (...args) => args.reduce((sum, value) => sum + value);
// a "proxy" function to test:
var pass = (...args) => sum(...args);
console.log(pass(1, 2, 15));
I'm not going to accept this as an answer, as I'm still hoping for something more suitable. But here's the approach I'm using right now based upon the feedback on this question so far.
For any class that will be calling Client.prototype.addListener or Client.prototype.removeListener, I did added the following code to their constructor:
class ExampleClass
constructor: ->
# ...
for name, fn of this
this[name] = fn.bind(this) if typeof(fn) == 'function'
message: (recipient, body) ->
# ...
broadcast: (body) ->
# ...
In the above example, message and broadcast will always be bound to the new ExampleClass prototype object when it's instantiated, allowing the addListener code in my original question to work.
I'm sure some of you are wondering why I didn't just do something like the following:
example = new ExampleClass
client.addListener('message', example.bind(example))
# ...
client.removeListener('message', example.bind(example))
The problem is that every time .bind( ) is called, it's a new object. So that means that the following is true:
example.bind(example) != example.bind(example)
As such, the removeListener would never work successfully, thus my binding the method once when the object is instantiated.
Since you seem to want to be using the bind function as it is defined in Javascript 1.8.5, and be able to retrieve the original this object you pass the bind function, I recommend redefining the Function.prototype.bind function:
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;
};
Now you can retrieve the original context that you called the bind function with:
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"};
I was just reminded of this question after a long time. Looking back now, I think what I was really trying to accomplish here was something similar to how the React library works with its automatic binding.
Essentially, each function is a wrapped bound function being called:
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
Just push properties directly to the function's object and call it with it's own "context".
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);
}
Related
function Base(x) {
this.x = x;
this.sub = new (function Sub() {
this.method = function() {
return function() {**this.super.x**}; // return an anonymous function that uses the outer x, but for reasons the anonymous function can't take parameters
}
});
}
var base = new Base(5);
console.log(base.sub.method()())
basically, in this example I'm trying to create an object Base who has a sub object sub, which has a 'method' that uses an anonymous function. The anonymous function needs the x but cannot take in any parameters..
Is there any way to access the this.x of Base?
At the beginning I tried the following.
This works well when Base is not inherited by other objects.
But in the following
"use strict";
function Base(x) {
var that = this;
this.sub = new (function Sub() {
this.method = function() {
return function() {
console.log(that); // displays Base *note[1]
return that.x;
}; // return an anonymous function that uses the outer x, but for reasons the anonymous function can't take parameters
}
});
}
function Other() {
Base.call(this); // why do I need to call Base.call(this) ?
this.setX = function(x) {this.x = x;}
}
Other.prototype = new Base();
var other = new Other();
other.setX(5);
console.log(other.sub.method()()); // otherwise undefined?
Base is extended by Other, after struggles I figured out I need to call Base.call(this); in order to make it works. Otherwise console.log(other.sub.method()()); will be undefined.
If I put a console.log(that) at *note[1], it will actually says the that is a Base object even though I construct it using var other = new Other(); I guess the problem would be solved if that is an Other object? What am I misunderstanding here?
My question is why do I need to call Base.call(this)? Why does it works after calling it? Is there any better solution to this situation?
If I understand you correctly you'd like Base to have a Sub and Sub is aware of the Base instance?
At the moment you're declaring the Sub type over and over again every time you create a Base, not the most effective way of doing it. Maybe try something like this:
function Sub(baseInstance){
this.baseInstance = baseInstance;
}
Sub.prototype.displayBaseX=function(){
console.log('base.x in sub:', this.baseInstance.x);
};
function Base(){
this.x = 22;
this.sub = new Sub(this);
}
You should not create an instance of Parent to set prototype of Child, use Object.create instead.
As for Parent.call(this, arguments) that is to re use the constructor function of Parent in Child when inheriting. More info about that can be found here: https://stackoverflow.com/a/16063711/1641941
The reason why your code doesn't work without Base.call(this); is because Base constructor defines and creates it's Sub type and instance. Not re using the Base constructor code when creating a Other type would cause the Other type not to have a Sub instance.
Constructor code deals with instance specific members and prototype can hold members that are shared by all instances. So usually behavior (functions) and a value for immutable default data members (numbers, string, boolean) can be found on the prototype.
I'm not exactly sure what you're end goal is here. But to solve your scope issue, this seems to work.
"use strict";
function Base(x) {
this.sub = new (function Sub(_this) {
this.method = (function(_this) {
return function() {
return _this.x;
};
})(_this);
})(this);
}
function Other() {
Base.call(this); // why do I need to call Base.call(this) ?
this.setX = function(x) {this.x = x;}
}
Other.prototype = new Base();
var other = new Other();
other.setX(5);
console.log(other.sub.method()); // otherwise undefined?
And a fiddle.
I'm stuck in some issue, a had a function like this:
var sayHi = function(string){
console.log(string + '' + this.name);
};
then i need to do :
sayHi = giveContext(sayHi,{"name":"moe"});
and then I do :
function giveContext(func,obj){
var fn = func;
fn.prototype.name = obj.name;
var myFn = new fn;
return myFn;
}
and the expected behavior would it be :
sayHi('Hello') // ==> "Hello moe"
the thing is that the "new" keyword in givecontext returns an object instead of a function.
and I'm only getting a
undefined moe
Uncaught TypeError: object is not a function
I'm missing something ?
When you are using new fn it will call the function fn as the constructor of an object, and the result is the object that was created.
Basically this:
var myFn = new fn;
works as:
var myFn = {}; // create an object
fn.call(myFn); // call the constructor with the object as context
(There are more things going on of course, but that is the important stuff for now.)
So, the function giveContext doesn't give a context to a function and return it, instead it will call the function as a constructor of an object and return the object. The code inside the function will be called already (that's why there is a console output at all), and when you are trying to use the return vale from giveContext as a function you will get an error as it's not a function at all.
There is already a built in method bind that sets the context for a function:
sayHi = sayHi.bind({"name":"moe"});
(Note the support information for the method though, it's not supported in iE 8 for example.)
You can also do the same without the bind method by creating a function that calls the function:
function giveContext(f, obj) {
return function(){
return f.apply(obj, arguments);
};
}
Yes, JavaScript is object-oriented, so when you say new fn, it creates a new object. In your setup, your function has a name property, so you should be using "Hello " + sayHi.name to get the results you expect.
You're going to want to read though this to get a good overview: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
I would like to do the following.I have a code like this:
var obj = {
method : function() {}
};
var func = function() {
return method(); //method is undefined here
};
func(); // What to do here?
Is it possible to call func in a way that it will see the method inside from obj as it was given for example as a parameter. I want to use obj.method inside func, without writing 'obj.' before and without modifying func itself. Is there any hack possible to achieve this?
In other words, is it possible to force obj as a closure into func?
I tried:
with(obj) {
func();
}
But it doesn't work. Anyone, any ideas? Or is it the only option to get the body of the function as string, put 'with(obj)' inside it and then create a new function out of it?
Clarification:
Because this code will be in a helper class 'eval' is OK. Which I don't want is the modification of the function through .toString(), because browsers implement it differently.
This is a solution, using eval (MDN):
var obj = {
method : function() {console.log("it workes!");}
};
var func = function() {
return method(); //method is undefined here
};
var newfunc = (function (obj, func) {
var method = obj.method;
eval("var f = " + func.toString());
return f;
}(obj, func));
newfunc(); //it workes
Basically you're just creating a new scope with a local variable called method and re-evaluating the function body in this scope. So you're basically creating a new function with the same body. I don't really like this approach and I wouldn't recommend it, but considering your constraints, it might be the only solution.
And yes, it still requires you to write obj.method, but not inside of func. So I figured, it should be ok.
EDIT
So here is a version, in which you don't have to specify the property name manually:
var newfunc = (function (__obj__, __func__) {
for (var __key__ in __obj__) {
if (__obj__.hasOwnProperty(__key__)) {
eval("var " + __key__ + " = " + __obj__[__key__]);
}
}
eval("var __f__ = " + func.toString());
return __f__;
}(obj, func));
This also done by using eval().
Note that I changed all remaining local variables to a names containing underscores, to minimize the probability of name collisions with properties inside obj.
Note also that not all valid property names are valid variable names. You could have an object like this:
var obj = {
"my func": function () {}
}
But if you would use this object you would generate a syntax error with the above method, because it would try to evaluate:
var my func = ...
As apsillers said in the comment section, it gets even worse if you don't have control over the properties of obj. In this case you shouldn't use eval at all, because you would make cross-site scripting attacks very easy (example from apsillers):
var obj = {
"a; alert('xss'); var b": function () {}
}
would evaluate to 3 different statements:
var a;
alert('xss');
var b = function () {};
This is not possible unless you define method separately:
var obj = {
method : function() {}
},
method = obj.method;
// rest of code
This is because the method reference inside func() assumes the window. namespace; thus, without modifying func() itself, it can't be done sanely.
More clarified version based on basilikum's answer, and I've found a simplification with 'with':
var obj = {
method : function() { return "it workes!"; }
};
var func = function() {
return method(); //method is undefined here
};
(function (obj, func) {
with(obj) {
eval("var __res__ = (" + func.toString() + ")()");
}
return __res__;
}(obj, func));
>> "It workes!"
I don't think there's such thing because I guess I would have found it by now but the only way to be certain is to be given a straight answer, therefore I must ask before I give up. I need a method that's going to be called every time I try to access an object's method. Is there such a thing in JavaScript?
Basically I need to run a couple of lines before and after each of my object's methods. Hardcoding them is really not an option. The other thing I thought of was to have a main method like
Mainmethod(ActualMethod, Parameters)
But this really doesn't look good to me, I'd really like not having to do this.
If this is just for a particular object or a particular type of object, you can dynamically replace all the methods with your own stub that does your pre-work, calls the original method and then does your post-work.
Something like this will work:
function overideMethods(obj) {
// can pass either an instantiated object (hooks existing methods on the object)
// or a function constructor (hooks methods in the prototype)
if (typeof obj === "function") {
obj = obj.prototype;
}
for (var prop in obj) {
if (typeof obj[prop] === "function") {
(function(origMethod) {
obj[prop] = function() {
var retVal, args;
// do your pre-work here
// make copy of args passed to this method
args = Array.prototype.slice.call(arguments, 0);
// call original method with proper args
retVal = origMethod.apply(this, args);
// do your post-work here
return retVal;
};
})(obj[prop]);
}
}
}
Working demo: http://jsfiddle.net/jfriend00/7LzQj/
You can now pass this function either an object or a constructor function. If you pass it an object, it will hook the existing enumerable methods on that object. If you pass it a constructor function, it will hook the methods on the constructor's prototype. This allows you to set up the hooks for all objects made from an entire constructor (in advance) or just hook an individual object.
If your object adds methods dynamically, either in the constructor or later in the life of the object and you want those methods hooked, then you will need to call overideMethods() on the object AFTER those methods are added - you will not be able to just call it on the constructor.
Here's a possible solution with function decorators. If you have something like Underscore at hand you can trim the code a bit, but I'm assuming you don't.
A decorator is a higher-order function that returns a modified version of another function. Decorators are a safer approach to monkey patching in some situations but it all depends on your needs.
Here's a demo: http://jsbin.com/ApaLAVab/1/edit
function compose(f, g) {
return function() {
return f(g.apply(this, arguments));
};
}
function before(fn) {
return function() {
console.log('before'); // code before method
fn.apply(this, arguments);
};
}
function after(fn) {
return function() {
fn.apply(this, arguments);
console.log('after'); // code after method
};
}
var run = compose(before, after);
function A() {}
A.prototype = {
say: run(function(name) { // decorate method
console.log('Hello '+ name);
})
};
var a = new A();
a.say('Peter');
//^ before
// Hello Peter
// after
You can also add it to the constructor, so you don't have to run it manually:
function SimpleClass(){
this.overideMethods();
}
SimpleClass.prototype.overideMethods = function() {
var obj = this;
for (var prop in obj) {
if (typeof obj[prop] === "function") {
console.log(prop);
(function(origMethod) {
obj[prop] = function() {
var retVal, args;
// do your pre-work here
alert("before function call");
// make copy of args passed to this method
args = Array.prototype.slice.call(arguments, 0);
// call original method with proper args
retVal = origMethod.apply(this, args);
// do your post-work here
alert("after function call");
return retVal;
};
})(obj[prop]);
}
}
}
SimpleClass.prototype.testFn = function(){
alert("In the function.");
};
var testObj = new SimpleClass();
testObj.testFn();
Working example: http://jsfiddle.net/awesomepeter/wvxAd/1/
Credit to jfriend00 though, i wanted to do the same thing as him just came a little bit too late ;) So i just copypasted his answer and improved.
Basically I want to do this:
someFunction() // do something
someFunction.somePropertyFunction()
someFunction() // Now someFunction is modified; it should now exhibit a different behaviour
Is this possible?
EDIT:
I'm not looking for what #Kolink was suggesting. Basically I want to augment a function's functionality by calling one of it's property function.
Specifically, I need to: 1. have access to the original function inside my property function (which is entirely doable using this), and 2. bind a new function to the original function's name (which I'm not sure if it's possible).
Just to be clear, I don't have access to the internal definition of the function that I want to augment. I want to attach a function to Function.prototype (so that it will be available as a property of the function that I want to augment), and then I will call func.augmentThis(), and then func should be augmented. But I'm not sure how, hence the question :P
Easily. Here's an example:
var derp = 123;
someFunction = function() {alert(derp);};
someFunction.somePropertyFunction = function() {derp = 456;};
someFunction(); // alerts 123
someFunction.somePropertyFunction();
someFunction(); // alerts 456
Okay, that's an oversimplified example, but yeah, it's entirely possible.
If your question is whether a function attached as a property to another function has a way to access the function to which it is attached, the answer is no. After all, the same function could be attached to any number of functions of objects.
So one alternative is to explicitly refer to the "mother" function within the function that is attached to it and intended to change its behavior:
function f (n) { alert (n + f.offset); }
f.offset = 0;
f.change_offset = function (i) { f.offset = i; };
f (1); //1
f.change_offset (100);
f (1); //101
Here, f is hard-wired into the definition of change_offset. If this bothers you, or you want something slightly more general, write a little routine to set a function as a property on another function, while binding its this to the function being attached to:
function set_func_as_func_prop ( propname, func_to_set, func_to_set_on ) {
func_to_set_on[propname] = func_to_set.bind(func_to_set_on);
}
Now you can write the function more generally
function change_offset (i) {
this.offset = i;
}
and set it on f or any other function.
set_func_as_func_prop ("change_offset", change_offset, f);
set_func_as_func_prop ("change_offset", change_offset, g);
Sort of:
function someFunction() {
return realFunction.apply(this, arguments);
}
function someFunctionA(name) {
return 'Hello, ' + name + '!';
}
function someFunctionB(name) {
return 'Goodbye, ' + name + '...';
}
var realFunction = someFunctionA;
someFunction.somePropertyFunction = function () {
realFunction = someFunctionB;
};
Sure it's possible. It's not recommended, but it's possible. For example:
function a() {
alert("a");
}
function b() {
alert("b");
}
function c() {
return c.f.apply(this, arguments);
}
c.f = a;
c.toggle = function () {
c.f = c.f === a ? b : a;
};
Now let's test it:
c(); // alerts "a"
c.toggle();
c(); // alerts "b"
See the demo: http://jsfiddle.net/LwKM3/
I want to attach a function to Function.prototype. Then I need to bind a new function to the original function's name (which I'm not sure if it's possible).
That indeed is impossible, you don't know what refers to the function. And you cannot change the internal representation of a function, which is immutable.
The only thing you can do is to create a new function and return that, to let the caller of your method use it somehow - specifically assigning it to the original variable:
somefunction = somefunction.augmentSomehow();
Your method for that will look like this:
Function.prototype.augmentSomehow = function() {
var origFn = this;
return function() {
// in here, do something special
// which might include invoking origFn() in a different way
};
};
Not sure if this helps, but I would implement described problem in following way:
// defined by somebody else - unknown to developer
var someFunction = function() {
alert("this is initial behavior");
}
someFunction(); // returns "this is initial behavior"
// defines parent object on which someFunction() is called
var parentObject = this; // returns window object (as called direclty in the
// browser)
// if you are calling someFunction from some object (object.someFunction())
// it would be:
// var parentObject = object;
// augumentThis definition
someFunction.augumentThis = function() {
var newFunction = function() {
alert("this is changed behavior");
};
parentObject.someFunction.somePropertyFunction = function() {
parentObject.someFunction = newFunction;
parentObject.someFunction();
};
};
someFunction.augumentThis(); // change function behavior
someFunction(); // "this is initial behavior"
someFunction.somePropertyFunction(); // "this is changed behavior"
someFunction(); // "this is changed behavior"