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
Related
function sum(a) {
let currentSum = a;
function f(b) {
currentSum += b;
return f;
}
f.toString = function() {
return currentSum;
};
console.log(f);
return f;
}
alert( sum(1)(2) ); // 3
alert( sum(5)(-1)(2) ); // 6
please help me to understand the difference between - return f and f(). what happen with function
code when activate return f? how it work? why console.log(f) return a number? i know f() return
result, but return f?
i dont understand.
In Javascript functions are first class objects. You can treat a function like any other variable or object, and pass them to functions, assign to other variables, and (as in this case) return them from functions.
A perhaps simpler example to show it might be something like
function foo() {
console.log("foo called");
}
bar = foo; // Assign the function foo to the variable bar
// Note that this doesn't actually call foo
bar(); // Now we call the foo function
My own example here is quite useless and only to show the principle. For a more useful example it's common for functions to return references to other functions, like in the example inside the question.
It happens to be that when you try to console.log any value it invokes "toString" method.
In your instance you override toString method instead of default implementation it returns a number
The function without the () is a pointer to the function. I use it with setTimeout all the time.
function doSomething() {
console.log('something');
}
setTimeout(doSomething, 5000);
Each time you invoke sum function, you are always returning reference of function f.
So sum(1) will return the reference of f, while sum(1).toString() will return the 1
sum(1)(2) will return reference of f, while sum(1)(2).toString() will return 3
It is not recursion because you returning just the reference. So until you invoke it, the function is not called
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);
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.
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)
}
I just experienced a behavior in JS which I couldn't understand: I wanted to create a method which calls String.prototype.replace with some args given, therefore I came up with that:
String.prototype.replace.bind("foo", /bar/g, function(){}).call
I guessed that I would get a function where I would just have to throw a string into to get my replace. Instead, I always get the initial this-value (in this case foo) returned.
Now my questions are:
Why is JS behaving like that? What does bind really return and how can I get the this-parameter of .call()?
Is there another way of doing what I want to do without creating a wrapper function?
Why it's happening
You pass some this value from call to the function returned by bind. bind, however, ignores that value and calls the original function with the bound this (e.g. foo). In fact, bind is meant to bind the this value. The arguments you can bind is rather something additional.
Solving it
Without a wrapper function I don't think you can do what you want. However, with a wrapper function you could do what you're after:
Function.prototype.bindArgs = function() {
var args = arguments,
func = this;
return function(context) {
return func.apply(context, args);
}
};
E.g.
var func = function(a, b, c) {
console.log(this, a, b, c);
};
var bound = func.bindArgs(1, 2, 3);
bound([1]); // [1] 1 2 3
bound({a: 1}); // {a: 1} 1 2 3
Function.bind returns a new function that, when called, will always call the original function with the bound context.
One could implement Function.bind like this:
Function.prototype.bind = function(context) {
var origFunction = this;
return function() {
return origFunction.apply(context, arguments);
};
};
You can try this here: http://jsfiddle.net/HeRU6/
So when you do somefunction.bind("foo"), it returns a new function. Calling this new function will always call somefunction with "foo" as context.
You can write a function that would bind only the arguments, and not the context:
Function.prototype.curry = function() {
var origFunction = this, args = Array.prototype.slice.call(arguments);
return function() {
console.log(args, arguments);
return origFunction.apply(this, Array.prototype.concat.apply(args, arguments));
};
};
a = function() { console.log(this, arguments); };
b = a.curry(1, 2);
b(); // Window [1, 2]
b(3); // Window [1, 2, 3]
b.call("foo", 4); // "foo" [1, 2, 4]
Why is JS behaving like that?
Because that's the way it's defined in the standard.
What does bind really return and how can I get the this-parameter of .call()?
g = f.bind(foo, bar, ...) is exactly the same as function g() { return f.call(foo, bar, ...); }
Since there is no this in g, you cannot get it back from the call.
Is there another way of doing what I want to do without creating a wrapper function?
Probably not.