Passing function as parameter on javascript - javascript

I recently started to learn javascript and saw this code. It was very different than C++ so please tell me what it means.
function getWeather(lat, lng){
fetch(
`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lng}&appid=${API_KEY}&units=metric`
)
.then(function(response){
return response.json();
})
.then(function(json){
const temp = json.main.temp;
const place = json.name;
weather.innerText = `${temp} # ${place}`;
});
}
so basically it gets api and fetch it. After that it continues with .then(function(response)
I have no idea where that response come from. I can guess that response part is something returned from fetch function but still I don't understand how that happens.
const parsedToDos = JSON.parse(loadedToDos);
//for each takes function and execute function on each items.
parsedToDos.forEach(function(toDo){
paintToDo(toDo.text);
});
similar to the previous example, it takes toDo as parameter and it is not even global or local variable in this code but still used. Can anyone tell me the logic behind this?

Which arguments a function receivedsis determined by the caller of the function. If you have the following function
function add(a, b) {
return a + b;
}
Then "you" can call it as
add(1, 3)
Sometimes it is not directly "you" who calls the function, but another function. E.g.
function add1(a) {
return add(a, 1);
}
here add is called by add1. add1 will always pass 1 as second argument.
In addition to that, sometimes functions accept other functions as arguments and call them at some point. Again an example:
function doSomething(callback) {
return callback(40, 2);
}
Here doSomething accepts a function as argument and will always pass 40 and 2 as arguments. If I pass a function that expects to be passed two numbers it will work as expected:
// Works
doSomething(function (a, b) { return a + b; });
If I don't not, then I get some unexpected behavior or an error:
// Error :(
doSomething(function (a, b) { a.foo.bar = b });
But if that happens then I haven't read the documentation properly, because functions which accept callbacks usually explain which arguments are passed to the callback. For example for forEach (emphasis mine):
forEach() calls a provided callback function once for each element in an array in ascending order. It is not invoked for index properties that have been deleted or are uninitialized (i.e. on sparse arrays, see example below).
callback is invoked with three arguments:
the value of the element
the index of the element
the Array object being traversed
Similar for .then.
In summary, arguments are provided by the caller. If you pass a callback to a function then that function will most likely be caller and pass arguments. Read the documentation or look at the source code to find out which arguments these are.

Related

Javascript - why my function is recursive?

I don't know where am I going wrong. Why is my function resursive? I expect a result of sum = 3;
function init() {
let object = {
firsNum: 1,
sum: 0,
};
add(1, object.firsNum, fun);
console.log(object.sum);
}
function fun(a) {
a.firstNum++;
a.sum += a.firsNum;
fun(a);
}
function add(a, b, callback) {
return callback(a + b);
}
init();
I think you don't fully understand every programming concept you've used in your code.
Apart from that your original code contains a typo: a.firstNum++; where it should read firsNum like in all other places.
Let's break down your code into separate concerns.
Recursion
Recursion means that a function calls itself. Recursion is one way to compute, iterate or traverse data. The other one is iteration using loops like while or for.
Let's look at this simplified example:
function recurse(counter) {
console.log(counter);
recurse(counter + 1);
}
recurse(0);
This will call recurse() indefinitely since there's nothing that stops it to call itself. Recursion as well as iteration require some stop condition to break the recursion.
If you'd like to stop at a certain value you'd have to call the function only until that condition is met:
function recurse(counter) {
console.log(counter);
if (counter < 42) {
recurse(counter + 1);
}
}
recurse(0);
This will only recurse and increment until 42 is reached.
You could even pass the stop condition with the function itself:
function recurse(counter, maxValue) {
console.log(counter);
if (counter < maxValue) {
recurse(counter + 1, maxValue); // maxValue gets passed along
}
}
recurse(0, 42);
To make your recursive function stop at a certain value you've to add such a condition:
function fun(a) {
a.firstNum++;
a.sum += a.firstNum;
if (a.sum < a.maxNum) {
fun(a);
}
}
You'd then have to ensure your object specifies the condition:
let object = {
maxNum: 3, // stop condition added
firsNum: 1,
sum: 0,
};
Though this on its own won't solve the problem. Read on.
Callbacks
Callbacks are functions passed as function parameters, object properties or array elements to other functions.
Callbacks allow you to decide what function to call during the runtime without having to implement branching behavior using if or switch statements and requiring certain function names at time of writing.
function someCallback() { /* ... */ }
function callTheCallback(callback) {
callback(); // Execute the parameter as a function
}
callTheCallback(someCallback);
let someCallbackReference = someCallback;
// Call the same function but indirectly via a variable
callTheCallback(someCallbackReference);
In the above example someCallback could indirectly origin from an other object.
You could even directly specify a function expression as a callback:
callTheCallback(function() {
// ...
});
With callbacks it's crucial to grasp the difference between passing a function call result and the function itself:
callTheCallback(someCallback); // Callback function passed
callTheCallback(someCallback()); // Result of a function call passed
Note that it could be perfectly valid to pass the result of a function call if the return value itself is a function. This is also not uncommon in programming.
Callbacks are often used for inversion of control (IoC) in general and asynchronous programming specifically.
Examples for callback functions:
Filtering arrays: Array.prototype.filter() (IoC synchronous)
Events: window.onload (asynchronous)
In your case fun() is passed as third parameter callback to add().
Now that you know how to use recursion and callbacks there are still semantic errors in your code...
Semantical errors
In contrast to syntactical errors (wrong code grammar) semantical errors are errors with the meaning of the code.
Interface of add()
Analyze your function API:
function add(a, b, callback) {
return /* some value - callback() call removed for simplicity */;
}
add() accepts three parameters and returns a value. The third parameter is named "callback" and thus shall be a function.
Your call of add() looks sensible although you don't use the returned value (which is not necessarily an error).
add(1, object.firsNum, fun);
Implementation of add()
Since the parameters of add() are not documented, it is ambiguous what your intention was.
The name along with the fact that there are two parameters, a and b, leads one to the assumption that the two values shall be added. And you very well do this:
return callback(a + b);
But! what you've passed as callback function - which is fun() - expects different parameters, not a number. fun() expects an object.
Properly documented it would look like this:
/**
* Recursively sum values until a maximum value is reached.
*
* #param {Object} a object containing the sum, the increment and the max value
* #param {Number} a.firsNum start value
* #param {Number} a.maxNum the maxium value up to which to add "firsNum" to "sum"
* #param {Number} a.sum the summed value, shall be 0 initially
* #returns {undefined} nothing is returned
*/
function fun(a) {
// ...
}
The above comment style is called JsDoc: https://jsdoc.app
Note to use proper names for functions, variables and parameters. Proper names help documenting the code whithout writing explicit documentation comments.
Fixing the error
With the limited knowledge on what your actually wanted to achive one can only speculate. I assume add(), although it's name and parameters looks sensible, is implemented wrong. Your issue could be solved by calling the callback (fun()) correctly:
function add(obj, callback) {
return callback(obj);
}
And:
add(object, fun);
Since we require the object add() now expects it as a single parameter.
Well that does not make a lot sense anymore now. fun() could be called directly from within init() and not via a callback reference and add() has as a confusing name.
I won't further speculate what your intention was and how to implement alternative solutions to the problem.
What you've learned
About Mozilla developer network
How recursion works
Functions can be passed as parameters and be assigned to variables or properties
Reading code
The benefits of documenting code and properly naming functions and variables
Just commented out the recursive code
function init() {
let object = {
firsNum: 1,
sum: 0,
};
add(1, object.firsNum, fun);
console.log(object.sum);
}
function fun(a) {
a.firstNum++;
a.sum += a.firsNum;
//fun(a);
}
function add(a, b, callback) {
return callback(a + b);
}
init();
Your code
function init() {
let object = {
firsNum: 1,
sum: 0,
};
add(1, object.firsNum, fun);
console.log(object.sum);
}
function fun(a) {
a.firstNum++;
a.sum += a.firsNum;
fun(a);
}
function add(a, b, callback) {
return callback(a + b);
}
init();
init
Defines an object with attributes of firsNum and sum, then calls add, passing 1, 1 and fun.
fun
Receives a parameter. Increments firstNum of the parameter and adds it to sum. Then it calls itself.
add
Gets three parameters: a, b and callback. Calls callback and passes a + b to it.
The flow
init is called, then it calls add and passes 1 to a, 1 to b and fun to callback. As a result, add calls callback, which is fun and since both a and b are 1, a + b = 2 is passed to fun. fun increments a.firstNum, which seems to be a bug, since a is a number, then some other strange value is added to sum. Just remove fun(a); inside fun to get rid of recursion, but that's not the only bug in your code. Also, make sure that there is a return statement inside fun.

How to check if a callback function passed as a parameter in Javascript has arguments or not?

I have to do this
doSomething
.then(success)
.catch(failure);
And success and failure are callback functions which will get their value at run time (I am trying to write a module).
So I have to know that the function failure which is sent as a callback should have a parameter.
Because the callback is supposed to be like
function(error) {
// An error happened
}
Is there anyway in JS to do this? Or any library that makes this kind of checking possible?
EDIT
Just to clarify. I want a user(not end user), who uses my module, to send in a function as a parameter which will become a callback and I want to check if the function sent, which will be called back, has enough parameters required. I understand it is up to one who uses it to take care of that. But why not try?
With a promise callback (your then and catch handlers), they will only be passed a single argument when called, and will always be passed that argument. (If the promise is obeying native semantics; jQuery's Deferred doesn't always.)
In the general case:
Usually you don't care, because in JavaScript, you can call a function with either fewer or more arguments than it has formal parameters. That is, this is just fine:
function foo(a) {
console.log("a = " + a);
}
foo(); // Fewer arguments than params
foo(1, 2, 3); // More arguments than params
Another reason you don't care is that the function can use arguments even when it doesn't declare formal parameters for them, by using a rest parameter or the arguments pseudo-array (you'll see why this snippet logs foo.length and bar.length in a moment):
function foo() {
console.log("First argument: " + arguments[0]);
}
function bar(...rest) {
console.log("First argument: " + rest[0]);
}
foo(1); // First argument: 1
bar("a"); // First argument: a
console.log("foo.length = " + foo.length); // foo.length = 0
console.log("bar.length = " + bar.length); // bar.length = 0
So just define what your API will call the callbacks with, and do that, and it's up to the users of the API to ensure that they use what you call them with correctly.
On those occasions it matters, you can use the function's length property to know how many formal parameters it declares:
function foo(a) {
console.log("a = " + a);
}
console.log(foo.length); // 1
Note that that will tell you how many formal parameters are declared...
...not counting a rest parameter (...identifier)
...only up until the first parameter with a default value (even if there are ones without a default after it)
So for instance:
function foo(a, b = false, c) { // b has a default value
console.log("a = " + a);
}
console.log(foo.length); // 1, even though `c` has no default value,
// because `c` is after `b`
and
function foo(a, ...rest) {
console.log("a = " + a);
}
console.log(foo.length); // 1, because the rest parameter doesn't count
It's probably an anti pattern for the caller to enforce a certain number of arguments of a callback, but if you must you can check the length of the function:
function test() {}
function test1(a) {}
function test2(a, b) {}
console.log(test.length, test1.length, test2.length)
I need to know whether the function which is sent as a callback should have a parameter
You need to read the documentation of the function that you are sending the callback to. It will mention how (when, how often, and with what arguments) the callback is called.
For your failure that means reading the docs for catch (assuming doSomething is a native promise). It says that the callback will be called with one argument, the rejection reason, when the promise is rejected.
Or any library that makes this kind of checking possible?
You can use a typechecker that will warn you or throw an error when the function you are passing does not have the expected number of parameters.

Are function-arguments not necessarily objects?

I'm learning functional programming and node.js, and I came across this odd problem when using Function.prototype.apply and .bind.
function Spy(target, method) {
var obj = {count: 0};
var original = target[method]
target[method] = function (){//no specified arguments
obj.count++
original.apply(this, arguments)//only arguments property passed
}
return obj;
}
module.exports = Spy
This code works, it successfully spies on target.method.
//same code here
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
//same code here
This code, however, does not. It gives an error message: TypeError: CreateListFromArrayLike called on non-object.
And then the biggest surprise is, this method works perfectly fine.
//same code here
target[method] = function (args){
obj.count++
original.bind(this, args)
}
//same code here
So why exactly do I get this error? Is it because function arguments are not necessarily objects? Or is it because apply has a stricter description than bind?
In this version:
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
Here you are not taking all the arguments but just one, named args. Since apply expects an array like object you cannot use args since it is only the first argument passed to the original target.
You can change it to:
target[method] = function (arg){ //only one argument specified
obj.count++
original.apply(this,[arg]) //one argument passed here
}
Now it works, but you can only spy on one argument functions. Using call would be better since you only have one extra argument:
target[method] = function (arg){ //only one argument specified
obj.count++
original.call(this,arg) //one argument passed here
}
Now bind is a totally different animal. It partial applies functions, thus return functions. Imagine you need to send a callback that takes no arguments but calls a function with some arguments you have when making it. You see code like:
var self = this;
return function() {
self.method(a, b);
}
Well. bind does this for you:
return this.method.bind(this, a, b);
When calling either of these returned functions the same happens. The method method is called with the arguments a and b. So calling bind on a function returns a partial applied version of that function and does not call it like call or apply does.
bind is called the same way as call is, even though they do very different things.
If you really wanted to use bind in this way. You could use the spread operator (ES2015) to expand the arguments 'array' to individual arguments:
original.bind(null, ...args);
That will bind the original function with the array values as individual arguments.

Eloquent Javascript: Higher Order Functions

I'm going through Eloquent Javascript: Higher Order Functions example below and already read questions and answers here and here. But I'm still very confused.
function noisy(f) {
return function(arg) {
console.log("calling with", arg);
var val = f(arg);
console.log("called with", arg, "- got", val);
return val;
};
}
noisy(Boolean)(0);
// → calling with 0
// → called with 0 - got false
How can (0) be passed into noisy(f) since noisy() only takes one parameter and that is (Boolean)? I can see the inner function f(arg) is basically Boolean(0), but I don't understand how two parameters can get passed into a function that only allow one parameter. Would "noisy(Boolean)(0)(1)(2)(3);" be a valid function call? If so, how would you differentiate each value after Boolean within the noisy function? Which value will be referenced by "arg"?
The book noted the example function is modifying another function. Which function is being modified? I'm not understanding what the author meant by "modified".
but I don't understand how two parameters can get passed into a
function that only allow one parameter
noisy returns a function, Boolean is passed to noisy, 0 is passed to anonymous function returned from noisy, where f is Boolean, val becomes Boolean(0).
For example
function fn1(arg1) {
return function fn2(arg2) {
console.log(arg1, arg2)
}
}
// Call fn1, inside fn1 fn2 is called with `"b"` as parameter.
fn1("a")("b") // `a b`, `fn2`
This is the concept of currying in JavaScript where you can curry functions to return partially applied functions or pass in other functions
How can (0) be passed into noisy(f) since noisy() only takes one parameter and that is (Boolean)?
The answer to this is the curried function noisy() which expects a function f as parameter and returns another function. The returned function has a closure over noisy and as a result it can identify that Boolean was passed as parameter to noisy even after it was returned. That's why calling noisy(Boolean)(0) basically substitutes f=Boolean, arg=0
Refer this for more on currying: http://javascript.crockford.com/www_svendtofte_com/code/curried_javascript/ and closures: https://developer.mozilla.org/en/docs/Web/JavaScript/Closures

Javascript call() function - extra parameters

I'm reading Javascript: The Definitive Guide 6th Edition. It teaches ECMAscript 5. Anyway, it doesn't explain certain things thoroughly, like the call() function for example. This is about the extent of the book's definition:
Any arguments to call() after the first invocation context argument are the values that are passed to the function that is invoked. For example, to pass two numbers to the function f() and invoke it as if it were a method of the object o, you could use code like this:
f.call(o, 1, 2);
In the next section the author builds a map function. I've been studying Ruby so I know how map works. My question is about the implementation using the call() function. It looks like this:
var map = function(a,f, o) {
var results = [];
for(var i = 0, len = a.length; i < len; i++) {
if (i in a)
results[i] = f.call(o || null, a[i], i, a);
}
return results;
};
It then defines a square function and puts map to use:
function square(x){
return x*x;
}
var array = [1,2,3,4,5];
var results = map(array, square);
What is the purpose of the i, and a parameters in the call() function? If I remove them I get the same results.
Array.prototype.map is defined to pass the index and the array to the callback, just in case you need them. For example, instead of square(x), you could use Math.pow(base, exponent):
var results = [1, 2, 3, 4, 5].map(Math.pow);
console.log(results); // [1, 2, 9, 64, 625]
This map behaves in the same way. You don’t have to use the arguments if you don’t need them in a particular case.
Function.call allows you to call a function as though it were a method attached to an object.
What this means is you can have a function that is defined somewhere unrelated to an object, and then you can call that function as though it was a part of that object. This is a long way of saying that when you use Function.call, you are telling the JS engine to use the first parameter whenever you use 'this' inside the function.
So:
function set_field_value(name, value) {
// do stuff
this[name] = value;
}
makes no sense by itself, because the special variable 'this' is not set to anything (meaningful)
But, if you use call, you can set it to whatever you want:
// if my_object = some object:
set_field_value.call(my_object, 'firstname', 'bob');
console.log(my_object.firstname); // prints 'bob'
The only important argument to call is the first one, (in the above case, my_object) because the first argument becomes 'this' inside the function. The rest of the arguments are passed 'as is' to the function.
So - in your example, the i and a arguments are there to make the map function look like other map functions, which provide the array (a) and index (i) that are being worked on.
Hope that helps,
Jay
PS - I strongly recommend the book 'Javascript: the good parts' - it makes a lot more sense than the definitive guide.
f.call in this example equals to square.call, and square requires only one parameter(x), so i and a are totally redundant here (and not used). Only a[i] is used by the function.
However, since you can pass in any function you want as the second parameter of the map function, chances are there will be another function instead of square coming up in the book, and that function would require those additional two parameters as well. Or you can make one example yourself to try it.
function threeParams(a, b, c) {
return [a, b, c]; // simply puts the three parameters in an array and returns it
}
var array = [1,2,3,4,5];
var results = map(array, threeParams);
Your main confusion is not really about the call method. It's more about how javascript treats function arguments.
Forget about call for a moment and let's look at a regular function to minimize the number of things under consideration.
In javascript, functions are allowed to be called with more arguments than is specified. This is not considered an error. The arguments may be accessed via the arguments object:
function foo (arg1) {
alert('second argument is: ' + arguments[1]);
}
foo('hello','world'); // this is not an error
Javascript also allows functions to be called with fewer arguments than specified. Again, this is not considered an error. The unpassed arguments are simply given the value undefined:
function foo (arg1,arg2, arg3) {
alert('third argument is: ' + arg3);
}
foo('hello'); // this is not an error
That's all there is to it. When the function passed to map() is defined to accept one argument but map() calls it with three the remaining two arguments are essentially ignored.

Categories

Resources