I have the function
function bind(method, context) {
var args = Array.prototype.slice.call(arguments, 2);
return function() {
var a = args.concat(Array.prototype.slice.call(arguments, 0));
return method.apply(context, a);
}
}
The question was: how this function working and for what it can be used.
I understand that function bind (and return function too) convert array-like arguments into a real array. Method and context is not converted into array (because of 2 index). I can pass extra args in bind function and args into returned function and call method with context as 'this'.
My question is - how it can be used, in what cases. Is method and context - function or objects, or function and object?
This function is usual to produce an event handler function.
When you subscribe to your function to an event, for example, and the function is called the this depends on which call your handler.
If you pass a method of your object instead a function, you want to access your object by this.
The function first 2 arguments are the method (the function you want to bind), the context (the object you want to access on this), and you could add some other fixed arguments, that will be pass to your handler function each time is called.
Then the function return a new function, that you use to subscribe the event listener you need, and this function get all the argument passed by this event and add to the arguments array.
And finally do the magic with apply that allow you to call a function changing the context and passing an arbitrary array as the arguments of the function.
You could use this kind of function not only in event subscribtion, but even in call like array forEach, map and so on.
Related
How come we do not have to pass an argument to function b
in the code below? Is it just because we are using map method of type Array? Or is there anywhere else that we can use a function just like this in
JavaScript?
Can someone give a very clean and through explanation?
Code:
/* we have an array a*/
const a = ['a', 'b', 'c'];
/*we define a function called b to process a single element*/
const b = function(x){do something here};
/*I noticed that if we want to use function b to take care with the
elements in array a. we just need to do the following.*/
a.map(b);
Functions are first class citizens in Javascript, which is just a fancy way of saying they can be passed around as variables and arguments.
What you are doing when you call
a.map(b);
Is essentially calling
[
b('a'),
b('b'),
b('c')
]
The array function map just calls the given function (in your case b), with each argument in the array, and puts the output in a new array. So there are arguments being passed to b, it's just that map is doing it behind the scenes for you.
As for your other questions, there are plenty of cases where you'll pass a function as an argument without calling it first. Another common function is the Array object's reduce.
const out = a.reduce(function (accumulator, val) {
return accumulator + ' - ' + val;
}
// out: 'a - b - c'
Also a lot of functions take callbacks, that are called when some kind of asynchronous task is completed. For instance. setTimeout, will call a given function after the elapsed time.
setTimeout(function (){
console.log("Hello World!");
}, 1000
);
// Will print "Hello World!" to console after waiting 1 second (1000 milliseconds).
And you can easily write your function to take another function as an argument too! Just call the function you've passed in as you would any other function.
// A really basic example
// More or less the same as [0, 1, 2].map(...)
function callThreeTimes(f) {
return [
f(0),
f(1),
f(2)
]
}
// My function here returns the square of a given value
function square(val) { return val * val }
const out = callThreeTimes(square);
// out: [0, 1, 4]
You don't pass arguments to b because you're not calling it. You're passing the function itself as a value.
The use of map here is irrelevant; you can see what's happening directly:
const a = function(x) { alert(`called with ${x}`); };
// The function is NOT called here; it's just being assigned,
// like any other kind of value. This causes "b" to become
// another name for "a".
// This is NOT the same as a(), which would call the function
// with undefined as the argument.
const b = a;
// Now we call it, and the alert happens here
b(5);
Passing a function to another function works the same way, since it's just another form of assignment.
This is useful because you can tell other code how to do something even if you yourself don't know what the arguments are. In the particular case of map, it loops over the array for you and calls the function once for each element. You don't want to be calling the function you pass to map, because the entire purpose of map is to call the function for you.
map accepts function as a parameter and executes provided function for every element of an array.
Here you are passing function b as a parameter to map, hence map executes function b for every elements of array a.
So you do not need to pass arguments to function b here, map will take care of this.
You probably heard that functions are first class citizens in javascript.
If you look at the docs from MDN map you will notice that the map function accepts a callback with up to 3 arguments first one being currentValue
So let's break it down. A very explicit example of doing a map over the array above would be this one
a.map(function(currentValue, index, array){
// here you can access the 3 parameters from the function declaration
});
This function is called on each iteration of the array. Since functions are very flexible in javascript, you could only declare 1 parameter or even none if you want to.
a.map(function(currentValue){
// we need only the current value
});
Every function in JavaScript is a Function object. Source here
This means that every function is just a reference in the memory, meaning it can be specified either directly as an anonymous function (which is our case above), or declared before like this
function b(currentValue){
// this will be called on each item in the array
};
a.map(b)
This piece of code iterates over each element in the array and calls the reference we passed it (function b). It actually calls it with all the 3 parameters from the documentation.
[
b('a',0,a),
b('b',1,a),
b('c',1,a)
]
But since our function b only declared one, we can access the value only.
The other arguments are stored in the so-called Arguments object
Take from here Every function in JavaScript is a Function object which makes every function a reference to a certain memory location which in the end leaves us with a lot of flexibility of passing the function as a parameter however we want to (explicit via an anonymous function, or implicit via a function declaration (reference) )
how come we do not have to pass argument to function b here?
Simply because as per spec, map calls the b with 3 implicitly.
callbackfn is called with three arguments: the value of the element,
the index of the element, and the object being traversed
For each element in the array, callback function is invoked with these three arguments
value of the element (a, b and c in your case)
index of the element
b itself (object being traversed).
Why there are no parenthesis?
When you are passing a function as an argument to the sort method it doesnt have parentheses after the function name. This is because the function is not supposed to be called right then and there but rather the map method to have a reference to this function so that it can call it as needed while it's trying to map the array.
Why it does not take any arguments?
Now we know that map will be calling this callback function accordingly, so when map calls it it implicitly passes the arguments to it while calling it.
For example if this would be callback of sort then the argument passed will be current element and next element. If this is a callback for map then the arguments will be current value, index, array.
In JavaScript, functions are just another type of object.
Calling a function without arguments is done as follows. This will always execute the function and return the function's return value.
var returnValue = b();
Removing the parenthesis will instead treat the function itself as a variable, that can be passed around in other variables, arguments etc.
var myFunction = b;
At any point such adding parenthesis to the "function variable" will execute the function it refers to and return the return value.
var returnValue = myFunction();
var sameReturnValue = b();
So map() accepts one argument, which is of type function (no parenthesis). It will then call this function (parenthesis) for each element in the array.
Bellow you will find how to use the map function:
first Methode
incrementByOne = function (element) {
return element + 1;
}
myArray = [1,2,3,4];
myArray.map(incrementByOne); // returns [2,3,4,5]
Seconde methode
myArray = [0,1,2,3];
myArray.map(function (element) {
return element + 1;
}); // returns [1,2,3,4]
Third Methode
myArray = [1,2,3,4];
myArray.map(element => {
return element + 1;
});
isInSet requires two arguments, but binds passes only theSet as first argument. I am not able to figure out how bind method works in this case
function isInSet(set , person) {
return set.indexOf(person.name) > -1;//checks for existence of person.name in theSet
}
console.log(ancestry .filter(function(person) {
return isInSet( theSet , person) ;
}) ) ;
// ! [{ name : " Maria van B r u s s e l " , ...} ,
// { name : " Carel H a v e r b e k e " , ...}]
console.log(ancestry.filter(isInSet.bind(null, theSet))) ;//**how does this work?**
// !... same result
bind() creates a new function from the function that it is called on. It sets the this keyword in the newly created function to be the first argument that you pass it (if null is passed, then it does not overwrite the default this keyword). You can also pass in extra arguments to bind() and if you do, they will always be inserted into the new function. So for instance, let's say you have a sum function that takes 2 arguments.
function sum (a, b) {
return a + b;
}
Now we could create a new function from this using bind() and always pass in one argument.
var boundSum = sum.bind(null, 2);
This will always bind 2 as the first argument in the sum() function. Now anytime you call that boundSum() function, it will only take one argument, as the 2 is already bound.
boundSum(3); // <-- this would return 5
Your example is using a similar principle. Because you are calling isInSet.bind(null, theSet) it is always binding theSet to the first parameter passed in the isInSet function. However, it is still missing the second parameter. The reason why it works is because you are putting that inside of an ancestry.filter() function. filter() essentially loops over an array and passes each element to the function inside of it (check the docs). So therefore, each element in the ancestry array is getting passed to that bound function, which makes it the second parameter of isInSet().
Read the text from the book a few times and compare the unbound and the bound version.
Calling .bind() on a function, return a new function, with some of the arguments already filled in. The isInSet function, expects two parameters: the set to filter, and a person having a name.
The .filter() method of an array, expects one parameter, namely the function to which each element of the array has to be sent to.
So when you look to the unbound version, you see that the filter function used, just returns isInSet(theSet, person).
So to make the two compatible, we .bind() to create a new function with only one parameter left (namely 'person'), that is bound to theSet. So every time this new function returned by bind gets called, it will use theSet as it's first parameter and will expect only one parameter, 'person'.
Here, person, the second parameter of the original isInSet function we didn't bind, is used as the first and only parameter of the bound function.
So we have exactly what we need after using the bind. A function that will take a 'person' as a parameters and will always look inside the same array (theSet).
If you read the docs for .bind(), you see that it accepts a variable list of arguments. The first argument is the 'this' argument. So if you have a function that uses 'this' inside it and you want to bind it to eg. an object, you can use the 1st parameter. Eg. myFunc.bind( objectToCallUpon, firstArg, secondArg );
Since we don't actually use the 'this' argument in the isInSet() function, we just pass 'null' as the 'this' value.
All the other parameters used are the arguments of isInSet you want to make 'fixed'. Since we want the first parameter to always be theSet, we bind that value.
If the isInSet function would accept two parameters, you would use:
isInSet.bind( null, theSet, secondParameter );
Can someone please explain why we can simply pass a method name to a higher order function and everything works just fine. I know in something like Java I have to call the method words on each element individually. I was told that in Javascript if method signature matches we can simply pass in the name of the function with () and it will work. It is great but I want to know whats going on in the background. Why are we able to do this in javascript ?
function words(str) {
return str.split(" ");
}
var sentences = function(newArr){
return newArr.map(words);
}
In many languages you can pass a reference to a function as an argument to a function. That then allows the host function to use that argument and call that function when appropriate. That's all that is going on in Javascript. When you pass the name of a function without the () after it, you're just passing a reference to the function. That enables the host function to use that function as an argument and call it some time later.
In your specific example, .map() expects you to pass in a function that it will call once for each item in an array. So, you pass the name of a function that will then get called multiple times, once for each item in the array. That function you pass has a bit of a contract that it has to meet. It will be passed three arguments (value, index, array) and it must return a value that will be used to construct a new array.
In Javascript, since there is no argument type checking by the language, it is the developer's responsibility to make sure the arguments of the function you are passing match what the caller of that function will actually pass to it and you have to consult documentation of the calling code itself to know what arguments will be passed to it. You can name the arguments anything you want (that is entirely internal to your function implementation), but the order and the quantity of the arguments is determined by the caller and you must declare your function to match what the caller will provide.
Once thing that confused many in Javascript.
If you pass just a function name, you are passing a reference to the function (something that the host function can call at some later time).
array.map(myFn) // passes a function reference
Or, use an inline function (same outcome):
array.map(function(value, index, arr) {
// code goes here
})
If you put parens at the end of the function name, then the function is executed immediately and the return value of that function execution is what is passed:
array.push(myFn()); // pushes the result of calling myFn()
You are calling the words function repeatedly. You're calling it for each iteration of the map function.
The map function takes a callback which it runs for every iteration. That callback is usually in the form of
function (elementOfNewArr, indexOfNewArr, newArr) { }
Because functions are objects, you can store them on a variable and use that new variable name to call that function, instead of its original one. That's mostly the use of functions as objects. You can toss them around.
let foo = function () { return 'jasper!'; }
let boo = foo;
let ron = boo; // ron() will now return 'jasper!'
So, what you've done is plop in your callback function, though it was defined elsewhere. Since callback functions, like all functions are objects, you can declare that callback function, "saving" it to whatever variable you want and use it in anywhere that you can use it normally.
This is super useful if you have to use the same function in more than one place.
What I believe you are misunderstanding is that functions themselves can be treated the same as other variables in javascript. Consider this example:
var newArr = [1,2,3,4];
newArr.map(function(item){
return item * item;
});
In the above example, a function is passed as an argument to the map() function. Notice that it is described anonymously (no function name given). You can accomplish the exact same thing like this:
var newArr = [1,2,3,4];
function squared(item){
return item * item;
}
newArr.map(squared);
These two examples achieve the same thing, except in the second example, rather than writing the function in place, we define it earlier in the code. If it helps, you can even create the function in the same way as you would any other regular variable:
var squared = function(item){
return item * item;
};
You can pass this function around the same way. If you want to know the difference between defining functions in these ways try var functionName = function() {} vs function functionName() {}
I have a simple function that takes one argument
fn = function(argument) {console.log(argument)}
In setInterval, I want to call the function and pass an external variable:
argument = 1
setInterval(<MY FUNCTION WITH ACCESS TO ARGUMENT>, 1000)
I realize that I could do it with a higher-order function, i.e.
fn = function(argument) {
function () {
console.log(argument)
}
}
argument = 1
setInterval(fn(argument), 1000)
And this does work, but I want to know if it can be done with curry.
I've tried:
fn = _.curry(fn)("foo")
// since the function takes only one argument,
// here it is invoked and no longer can be
// passed as a function to setInterval
fn = _.curry(fn, 2)("foo")
// setting the arity to 2 makes it so the function
// isn't invoked. But setInterval does not pass
// the additional argument and so it never ends
// up getting invoked.
I feel like there is something I'm missing with these curry examples. Am I, or will curry not help here?
Indeed lodash _.curry seems not suitable for your use-case.
But you can use the vanilla JavaScript bind for this:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
Syntax
fun.bind(thisArg[, arg1[, arg2[, ...]]])
In your case your code would look like this:
fn = function(argument) {console.log(argument)}
var argument = 1
setInterval(fn.bind(this, argument), 1000)
Lodash alternative
If you really want to do it the lodash way, the equivalant of fn.bind(thisArg, ...args) is _.bind(fn, thisArg, ...args). And if you are not interested in setting the this reference, then you can save one argument with _.partial(fn, ...args):
Creates a function that invokes func with partials prepended to the arguments it receives. This method is like _.bind except it does not alter the this binding.
Let's say I have a function:
function myFunction() {
...
}
I want to call it from event handler. Why this construction doesn't call my function?
$(window).resize(myFunction());
But this does the trick:
$(window).resize(function(){myFunction()});
What is the difference between these types of calling?
$(window).resize(myFunction());
immediately calls myFunction and passes the return value to resize. Adding parenthesis after a function name/reference calls the function.
On the other hand,
$(window).resize(function(){myFunction()});
passes an anonymous function to resize. myFunction is only called when the outer function is called.
Anonymous functions are nothing special. In this case it is just some kind of inline function definition. You can easily replace it with a function reference:
var handler = function(){myFunction()}; // take out the function definition
$(window).resize(handler); // and replace it with the new name
Now you can see that there are no parenthesis after the function name, which means, handler is not called, only the reference is passed.
I hope you can also see now that creating a new function in this example is not necessary. You can achieve the same by simply passing a reference to myFunction:
$(window).resize(myFunction);
But both ways have their use cases.
The first example $(window).resize(myFunction()); can still be useful if myFunction returns a function that should be used as event handler:
function myFunction() {
var handler = function() {};
return handler;
}
Maybe the handler retuned depends on a parameter passed to myFunction.
Passing an anonymous function is necessary if you want to call myFunction with some specific arguments:
function myFunction(a, b) {
alert(a + b);
}
$(window).resize(function(){
myFunction(40, 2);
});
Update:
#T.J. Crowder made an important comment about the event object. Every event handler gets the event object passed as first parameter. If you'd define your function as
function myFunction(event) {
...
}
to have (easier) access to it, using an anonymous function, you would have to pass it through:
$(window).resize(function(event){myFunction(event)});
if you want to use it.