Override the arity of a function - javascript

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)
}

Related

javascript: always pass the nth argument in a function as fixed value by default

the function takes 3 parameters like
function p(x,y,z){
console.log(arguments);
}
so when we call it like
p(12,21,32)
a fourth argument should pass as say 56
so effectively the call should be p(12,21,32,56)
How to do this?
Condition We cannot change the function definition. I need to partially bind the fourth argument as 56 something like
p=p.bind(this,'','','',56);
or use lodash
and then call p later like
p(12,21,32);
such that 56 should pass by default
You can use _.partialRight() to create a new function that appends arguments to the end of the original function:
function p(a, b, c)
{
alert([].join.call(arguments, ','));
}
p = _.partialRight(p, 56);
p(1,2,3); // 1,2,3,56
<script src="https://raw.githubusercontent.com/lodash/lodash/3.9.3/lodash.js"></script>
To exactly specify the position of the extra argument(s) you can use placeholders:
p = _.partialRight(p, _, _, _, _, _, _, 56); // add 56 as 7th arg
p(1,2,3); // 1,2,3,,,,56
p = (function() {
var old_p = p;
return function(a, b, c) {
return old_p(a, b, c, 56);
};
})();
We remember the old version of p under the name old_p so we can invoke it even after we've redefined p. We do this inside the IIFE so that old_p does not pollute the global scope. Then, we return a function (which is assigned to p) which returns the result of calling old_p with the extra argument.
We can make this more general, to create "bound" functions which add extra arguments to any function call. Below I use ES6 syntax, especially the spread ... operator. However, you can accomplish the same thing by manipulating the arguments object and using apply:
function bind_with_arguments_at_end(f, ...extra_args) {
return function(...args) {
return f(...args, ...extra_args);
}
}
Where the function involved is a method on an object, it makes sense to "pass through" this, so the new function can be called as this.bound_func and things continue to work. Do do this, we can use call:
function bind_with_arguments_at_end(f, ...extra_args) {
return function(...args) {
return f.call(this, ...args, ...extra_args);
^^^^^^^^^^^
}
}
You can create a new function which uses apply to redirect its arguments to the original one, but using Object.assign to overwrite some of them:
function fixArguments(f, args) {
return function() {
return f.apply(this, Object.assign([].slice.call(arguments), args));
};
}
p = fixArguments(p, {3: 56}); // Overwrite the 4th argument with 56
function fixArguments(f, args) {
return function() {
return f.apply(this, Object.assign([].slice.call(arguments), args));
};
}
function p(x,y,z){
console.log(arguments);
}
p = fixArguments(p, {3: 56});
p(12,21,32);
Make a copy of the original and override the name, and call the original with the new arguments.
function p(a,b,c,d) {
console.log(arguments);
}
(function (){
var org_p = p; //copy original
p = function() { //override p
var args = [].slice.call( arguments ); //turn arguments in to array
args.push(56); //add the 4th argument
return org_p.apply( this, args ); //call the original with the updated arguments.
}
}());
p(1,2,3);

What is fn.bind.apply(fn, arguments) doing?

I saw this shortcut given as an answer on a code Kata but I am having difficulty understanding exactly what the below example is doing.
function func(fn) {
return fn.bind.apply(fn, arguments);
}
So far my understanding is that bind creates a new function similar to doing the following:
function func(fn) {
return function () {
return fn.apply(fn, arguments);
};
}
Is this the case? Any clearer answers or breakdowns of what is going on would be great.
fn.bind
is just
Function.prototype.bind
So we're applying bind to fn, returning
fn.bind(arguments[0]/* doesn't matter, it's fn*/, arguments[1], arguments[2], etc.)
So the bound function is called with arguments being the arguments of func after fn.
Another way to write it would have been:
function func(fn) {
var args = [].slice.call(arguments, 1);
return function () {
var localArgs = [].slice.call(arguments);
return fn.apply(fn, args.concat(localArgs));
};
}
The fact that the context of the call is the initial function (arguments[0]) is most certainly only a side effect. The important thing is we wrap the arguments with the function, but make it possible to dynamically pass other arguments.
Example 1, wrapping all arguments :
function add(a,b){
return a+b
}
var f = func(add, 2 ,3); // f is a function which will always apply add to 2 and 3
console.log(f()) // logs 5
Exemple 2, currying:
function multiply(a,b){
return a*b
}
var multBy2 = func(multiply, 2);
console.log(multBy2(3)) // logs 6

Binary function equivalence - demethodize

I was watching a Pluralsight video from Douglas Crockford: http://pluralsight.com/training/Courses/TableOfContents/javascript-good-parts
In this video he goes through a number of interesting exercises to demonstrate some javascript principals.
When he gets to the 'demethodize' function you basically have the following code sample:
function add(x,y){
return x + y;
}
//add(1,2) => 3
function methodize(fn){
return function(x){
return fn(this, x);
};
};
Number.prototype.Add = methodize(add);
//(1).Add(2) => 3
function demethodize (fn){
return function(x,y){
return fn.call(x,y);
};
}
var newAdd = demethodize(Number.prototype.Add);
// newAdd(1,2) => 3
Note that this 'demethodize' function is for binary functions only.
My question is that, according to my understanding, the following should result in an equivalent 'demethodise' functions:
function demethodize (fn){
return fn.call;
}
or
var demethodize = Number.prototype.Add.call;
But these functions do not work (given the same binary function requirement)!
Why is this?
Please help me fill the gap in my understanding.
I had thought that if I had a binary function whose implementation contained another binary function call with the same arguments passed to the inner function, then using the inner function call directly would be equivalent.
The result of your demethodize function is the Function.prototype.call function - not bound to your fn, i.e. with no this value (which it had if it was called as fn.call(…)). You can however use the bind method to fix that:
function demethodize(fn) {
return Function.prototype.call.bind(fn);
}
// or long:
function demethodize(fn) {
return function(context/*, args... */) {
var args = Array.prototype.slice.call(arguments, 1);
return fn.apply(context, args);
// equivalent (for binary functions) to
return fn.call(context, args[0]);
// ^^^^^^ is a method invocation here
};
}
Because call doesn't know on which function it is supposed to be called. Have a look at the MDN documentation for how this works.
It's the same as
var foo = {bar: function() { console.log(this); }};
foo.bar(); // logs foo
var bar = foo.bar;
bar(); // logs window
You changed the way how .call is executed and with that you changed what this refers to.

JavaScript equivalent to php's __call

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.

Where do function arguments exist when using .call()/.apply()?

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

Categories

Resources