I'm trying to figure out how I can reference an argument on a wrapper function stored in a variable that calls an anonymous function. Such as in the example below. The problem I run into is I'm used to accessing parameters via the arguments variable, but that sees only the parameter myFunc. I know it's supposed to be possible, but I can't figure out how.
var myFunc = function(arg1, arg2){//do stuff}
var myFuncWrapped = wrapper(myFunc);
myFuncWrapped('arg value1', 'arg value2');
function wrapper(func){
//how can I reference 'arg value1' from here since arguments == myFunc?
}
As the comment suggested, wrapper should be returning a function so you can capture the arguments via closure when myFuncWrapped gets called.
var myFunc = function(arg1, arg2) {
console.log(arg1); // (For testing only) Should be"arg value1"
};
var myFuncWrapped = wrapper(myFunc);
myFuncWrapped('arg value1', 'arg value2');
function wrapper(func) {
/* The anonymous function below is actually the one
* being called when you invoke "myFuncWrapped" so it has the arguments you need.
*/
return function() {
console.log(arguments[0]); // (For testing only) Should be"arg value1"
func.apply(this, arguments);
}
}
Related
I have target object
function Foo() {
this.someVar = 'some var';
};
Foo.prototype.callback() {
console.log(this);
};
And object, that will call this callback
function Bar(callback) {
this.callback = callback;
};
Bar.prototype.onSomeAction = function() {
this.callback();
};
And initial code
foo = new Foo();
bar = new Bar();
bar.callback = foo.callback;
bar.onSomeAction();
Result: i have logged to console Bar()'s context instead of Foo().
How can i get context of Foo() in the Foo() callback?
PS: I tried closures
Foo.prototype.callback() {
var foo = this;
return function(foo) {
console.log(foo);
};
};
but it does nothing. I have not fully understanding of the closures :(
The reason your original code didn't work is that the value of this inside of a method call is the value of the object it's being called on. That means when you say:
bar.callback = foo.callback;
And then you call:
bar.callback();
The code defined here:
Foo.prototype.callback = function () {
console.log(this);
};
gets called with this being a reference to bar because bar is to the left of the . on the method call. So whenever you assign a function as an object property, calling it on that object will call it with the object as this.
You could also have written:
function callback() {
console.log(this);
}
bar.callback = callback;
bar.callback();
And you would find that this still references bar.
In fact, if you call the plain function callback(); as defined above, you'll find that this is a reference to the global object, usually window in web browsers. That's because all global variables and functions are properties of window, so callback(); is implicitly window.callback();
The fact that the value of this depends on what object is calling a function can be a problem when passing callbacks around, since sometimes you want this to reference the original object the function was a property of. The bind method was design to solve this problem, and Yuri Sulyma gave the right answer:
bar.callback = foo.callback.bind(foo);
However, the way you would do this using closures is to capture an instance of Foo within an anonymous function that calls the correct method on the correct object:
foo = new Foo();
bar = new Bar();
bar.callback = function () {
foo.callback();
};
bar.onSomeAction();
Which is essentially what bind does. In fact, we call write our own naive version of bind using a closure:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.call(obj);
};
};
call let's you call a function with the value of this explicitly defined. This allows you to "set the context" the function is called in so that it's the same as calling obj.fn() when you call bar.callback(). Since when we call foo.callback.bind(foo);, obj is foo and fn is foo.callback, the result is that calling bar.callback() becomes the same as calling foo.callback().
That's where Dalorzo's answer comes from. He uses call to explicitly set the context.
There's also another function for setting the context called apply that also takes an array representing the arguments for the function as its second argument. This allows us to write a more complete version of bind by taking advantage of the special arguments variable:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.apply(obj, arguments);
};
};
bar.callback = foo.callback.bind(foo);
You can polyfill Function.prototype.bind() if necessary: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
Try using these changes:
Use call to set context:
bar.onSomeAction.call(foo);
And I think your callback function needs to change to:
Foo.prototype.callback=function() {
console.log(this);
};
i came across a situation where I need to call another function with .call() or .apply() like this:
function b() {
alert(arg);
}
Then
function a(arg) {
b.call();
}
a(123);
Function b is called, but doesnt' have access to arg. That's ok, I can pass scope.. yes?
function a(arg) {
b.call(this);
}
a(123);
Still no - I can't access arg from function b. How can I do it?
UPDATE:
I do not want to modify b function :-)
You still need to pass the arguments via call (individually) or apply (as an array):
function a(arg1, arg2, arg3) {
b.call(this, arg1, arg2, arg3);
// or
b.apply(this, arguments)
// or
b.apply(this, [arg1, arg2, arg3]);
}
Of course, nothing about your situation suggests actually using call or apply: Just invoke the function yourself.
function a(arg) {
b(arg);
}
It’s not possible to “pass scope” or something like that. The scope of a function is determined when the function is created, so it can only access arg if it exists where b is defined.
If arg was part of this, then you could do this using call, i.e. make the this in b the same this as it is in a (of course this will modify whatever this actually refers to, which can have side effects you might not want to happen).
function a (arg) {
this.arg = arg;
b.call(this);
}
function b () {
console.log(this.arg);
}
The other way would be to just pass the argument to b as an actual function argument. You can access all arguments of a function using arguments:
function b () {
console.log(arguments);
}
Try this one:
function a(arg) {
b.apply(this, [arg]);
// or
// b.call(this, arg);
}
function b() {
alert(arguments);
}
You failed to pass the arguments when you called b.
Function::call allows you to pass a fixed number of arguments:
function a(arg1,arg2) {
return b.call(this,arg1,arg2);
}
Function::apply allows you to pass any number of arguments, as an array:
function a(arg1,arg2) {
return b.apply(this,[arg1,arg2]);
}
// or
function a(arg1,arg2) {
return b.apply(this,arguments); // The magical 'arguments' variable
}
this is the context object, and is not the same thing as scope.
I'll guess the problem lies in b(). There's no 'arg' argument defined. Try:
function b(arg) { alert(arg); }
and
function a(arg) {
b.call(this,arg);
}
a(123);
now it runs
(Update: the call needs the arguments ( context functionarg1, functionarg2...) )
Assuming that, for some reasons you can't modify the body of b , you can try something like this:
function a(arg) {
eval("("+ b.toString() +")()");
}
DEMO
I would like to make a generic function wrapper that (for example) prints the called function and its arguments.
Doing so is easy through the arguments quasi-array and simple calls. For example:
function wrap(target, method) {
return function() {
console.log(Array.prototype.slice.call(arguments).join(', '));
return method.apply(target, arguments);
}
}
However, this way of doing of course completely loses the arity of the called function (if you didn't know, one can obtain the arity (number of arguments) of a JavaScript function through its length property).
Is there any way to dynamically create a wrapper function that would copy the arguments of the wrapped function to itself?
I've thought about creating a new Function object, but I don't see any way to statically extract the arguments list, since the arguments property is deprecated.
Here's a solution using Function:
// could also generate arg0, arg1, arg2, ... or use the same name for each arg
var argNames = 'abcdefghijklmnopqrstuvwxyz';
var makeArgs = function(n) { return [].slice.call(argNames, 0, n).join(','); };
function wrap(target, method) {
// We can't have a closure, so we shove all our data in one object
var data = {
method: method,
target: target
}
// Build our function with the generated arg list, using `this.`
// to access "closures"
f = new Function(makeArgs(method.length),
"console.log(Array.prototype.slice.call(arguments).join(', '));" +
"return this.method.apply(this.target, arguments);"
);
// and bind `this` to refer to `data` within the function
return f.bind(data);
}
EDIT:
Here's a more abstract solution, which fixes the closure problem:
function giveArity(f, n) {
return new Function(makeArgs(n),
"return this.apply(null, arguments);"
).bind(f);
}
And a better one, that preserves context when invoked:
function giveArity(f, n) {
return eval('(function('+makeArgs(n)+') { return f.apply(this, arguments); })')
}
Used as:
function wrap(target, method) {
return giveArity(function() {
console.log(Array.prototype.slice.call(arguments).join(', '));
return method.apply(target, arguments);
}, method.length)
}
Why is this valid:
function func(a,b,c) {
console.log(this, a,b,c);
return '';
}
'testing'.replace(/e/, func);
but this isn't:
function func(a,b,c) {
console.log(this, a,b,c);
return '';
}
'testing'.replace(/e/, func.call);
if func is a function reference, and call is a function reference shouldn't they both work?
Here's a fiddle of this
Because when you pass the call function, you take it out of the context of func, so inside call the this keyword will refer to window instead of func.
window is not a function, but call expects this to be a function, so it breaks.
For comparison.
var AnObject = {
call: function () { console.log("this.location is: ", this.location); },
location: "This string is the location property of AnObject"
};
AnObject.call();
setTimeout(AnObject.call, 500);
Because .call() itself is a method, but not one that is useful to replace().
In other words, while your intention is to pass func, you're actually passing a completely different function (call) that serves a purpose not useful as an argument to .replace().
I have a function I can't modify:
function addToMe() { doStuff(); }
Can I add to this function? Obviously this syntax is terribly wrong but it's the general idea...
function addToMe() { addToMe() + doOtherStuff(); }
You could store a reference to the original function, and then override it, with a function that calls back the original one, and adds the functionality you desire:
var originalFn = addToMe;
addToMe = function () {
originalFn(); // call the original function
// other stuff
};
You can do this because JavaScript functions are first-class objects.
Edit: If your function receives arguments, you should use apply to pass them to the original function:
addToMe = function () {
originalFn.apply(this, arguments); // preserve the arguments
// other stuff
};
You could also use an auto-executing function expression with an argument to store the reference of the original function, I think it is a little bit cleaner:
addToMe = (function (originalFn) {
return function () {
originalFn.apply(originalFn, arguments); // call the original function
// other stuff
};
})(addToMe); // pass the reference of the original function