javascript mystery - how functions get access to outside variables [duplicate] - javascript

This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
I am kind of new to javascript and trying to understand some non trivial - at least so i hope :) things.
my question is general, but i have a specific example which can help me ask my question and help you understand what i mean.
the example:
function updateBookmark(bookmark){
var index = _.findIndex($scope.bookmarks, function(b){
return b.id == bookmark.id;
});
return index;
}
obviously the findIndex function is declared somewhere (in our case - lodash.js)
and it gets two parameters (at least two visible parameters: a data set, and a function)
first question:
in this example, what is b? how does b gets its value? i understand b is each of the data set's objects, but i mean - what is going behind the scenes here so b will be what it is??
second question:
the author chose to pass an anonymous function which equals b.id with bookmark.id,
i understand that he can use bookmark.id where he is using it, but how does findIndex has access to this bookmark?!
this function as i concluded earlier is declared somewhere else, does it get all the variables in the scope some how?
what is going on here?
Thanks in advance to responders and sorry for the messy question...
Jim.

If you rewrite some things, it becomes easier to understand.
Starting with the last portion:
// Q: "How does `findIndex`have access to `bookmark`"
_.findIndex(bookmarks, function (b) { });
// A: "It doesn't."
var bookmark = { id: 1 };
var bookmarks = [ /* ... */ ];
function compareWithBookmark( test ) {
return test.id === bookmark.id;
}
_.findIndex(bookmarks, compareWithBookmark);
As you can see, findIndex doesn't actually have any access to bookmark.
Rather, it has access to a function which it can pass a value to test, and that function will return whether that test passed or failed.
Under the covers of .findIndex or [].map or [].filter, they're all just taking a function, making a loop, passing each element into the function one at a time, and doing something with the return value.
function findIndex (array, test) {
var index = -1;
var i = 0;
var l = array.length;
var el;
var result;
for (; i < l; i += 1) {
el = array[i]; // <-- how `b` got its value
result = test(el, i, array); // <-- test(b)
if (!!result) {
index = i;
break;
}
}
return index;
}
The different functions would do different things with the results (map returns a new array which contains each result, filter returns an array where only !!result tests passed, et cetera), but all of them do this inner-looping.
This is also a pretty gross simplification of the looping structure and considerations, but it's exactly what's driving your expected behaviour.
Edit
Here is a full usage of the function I just defined, plus the array, plus the object I'm checking.
var bookmarks = [
{ id: 2 },
{ id: 3 },
{ id: 6 },
{ id: 14 }
];
var bookmark = { id: 3 };
function compareBookmarkIdTest (el) {
return el.id === bookmark.id;
}
var index = findIndex(bookmarks, compareBookmarkIdTest);
index; // 1
Hope that helps.

Related

How these inner function parameters are getting a value? [duplicate]

This question already has answers here:
Where do the parameters in a javascript callback function come from?
(3 answers)
Closed 1 year ago.
I saw that function and tried to understand it, but I don't get what are the (a, i) arguments of that function. How they're getting a value assigned to them?
function ftRotations() {
let str = 'abc';
let arr = ['a', 'b', 'c'];
return arr.map(function (a,i) {
return str.slice(i)+str.slice(0,i)
});
}
The map method provides arguments when calling the callback, which are received by the callback in the a and i parameters it declares. (The callback doesn't use a for anything, but uses i. The callback also uses str, which it closes over because the function was created in a context where str exists.)
It may help to see a rough idea of what map does internally:
// Note: VERY ROUGH approximation, leaving out all kinds of details (like `thisArg`)
map(callback) {
// Note: `this` is the array you called `map` on
const result = [];
for (let index = 0; index< this.length; ++index) {
result[index] = callback(this[index], index, this);
// In the callback, ^^^^^^^^^^^ ^^^^^
// becomes `a` −−−−−−−−−−−−−−/ \−−−−−−−becomes `i`
}
return result;
}

What is "el", what relation do these codes have with each other, and how does the fat arrow work?

I need to understand what the code means, or a better break down of how/why it works.
This function should find an object in an array called items by it's id number, and return the objects properties of which there are 9 for each object.
I've googled how the fat arrow works, but the examples given don't match this code very well, or I'm still to ignorant to see the relation. I also don't COMPLETELY understand how el is being used. I assume it's a place holder for "item" or a Boolean. I feel it's on the tip of my tongue.
pantry{
//.. other code of an empty array, and a function that fills the array at run time
getItem: function(id) {
return this.items.find(el => {
return el.id == id
});
}
}
My tutor showed me that this code is equivalent to
for(var i = 0; i < items.length; i++){
let item = items[i];
if(item.id == id){
return item;
}
This code works as is, but I'm just barely not understanding what el does, I've changed it to ed, and do and se, and it still works, but I'm trying to understand deeply.
I'm very new to JavaScript. I've been reading "You Don't Know JavaScript" for a month now, and practicing for 3 months. I don't know if I'm learning in the best way, but I put work into learning every single day --- at any rate I digress.
Thank you for any amount of your time.
el is just a variable. When you pass it into a function like find, it represents the element of the array that you are looking at during that iteration.
The arrow is just another way of defining a function. Broadly speaking, these two functions are equivilant:
(el) => {
return el.id == id
});
function(el) {
return el.id == id
}
You can read more about them and the differences between arrow and normal functions here.
for beginner simple function and arrow function are same it's just the short way of doing stuff i. e
function(param) {} = (param) => {} = param => {}
what array find function (in your case array is items) does is that it pass every item to the function and return only those item which pass the condition or return true i.e its id is equal to the given id e.g
arr = [{id: 2, name: 'temp'}, {id: 1, name: 'other'}]
on first iteration el is equal to {id: 2, name: 'temp'} on second iteration its equal to {id: 1, name: 'other'} now if the given id is 2 it will return {id: 2, name: 'temp'} because it pass the condition or it return true now point to note is the el is just a name of a variable you can replace it with any name like you can replace id with other variable like tempId in getItem: function(id) but you have to give it only one parameter because the find function expect only one argument

Memoization javascript

I'm studying JavaScript from the book JavaScript: The Good Parts, in the memoization section there is an example about using memoize technique to do Fibonacci problem
We create a general function call memoizer, it takes an memo array and the fundamental function, returns a shell function that manages the memo and calls fundamental function
var memoizer = function(memo, fundamental) {
var shell = function(n) {
var result = memo[n];
if (typeof result !== 'number') {
result = fundamental(shell, n);
memo[n] = result;
}
return result;
};
return shell;
};
And then create fibonacci like this:
var fibonacci = memoizer([0, 1], function(shell, n) {
return shell(n-1) + shell(n-2);
});
If I run fibonacci(10), the result will be displayed exactly.
But the thing that makes me confused is the n parameter shell function in memoizer function. I know that it is the value that we want to compute. But where is it come from? How can I call fibonacci(10), for example, can pass the value 10 to n? And what is exactly the var fibonacci? Is it a function or points to a function object as memoizer?
Thanks for any help!
So, to understand this fully, you need to understand things below as fundamental pieces.
Closure How do JavaScript closures work?
Scope What is the scope of variables in JavaScript?
First class object What is meant by 'first class object'?
So, now look at the code.
var memoizer is assigned as a function that returns an another function inside.
var memoizer = function(memo, fundamental) {
var shell = function(n) {
... do some works ...
};
return shell;
};
Can you see it? var shell is returned at the end of the line in memoizer
And whatever the inner logic is in memoizer, the result of it is assigned to var fibonacci
var fibonacci = memoizer([0, 1], function(shell, n) {
return shell(n-1) + shell(n-2);
});
So, it means fibonacci equals the result of memoizer (Since we excuted it), and the result of memoizer equals shell function. If you read those links given to you by me well, you can understand what is going on behind the scenes.
By the way, the code you've given isn't the best way. Because anyway closure makes an activated object alive that isn't necessary.
This code below is an another way of making fibonacci, this isn't the absolute best way, however, I recommend you to compare this code with your code, and figure the differences out.
var result = [];
result[0] = 1;
result[1] = 1;
function fibonacci(n){
var i;
for(i = 2; i < n; i++){
if(!result[i]){
result[i] = result[i-1] + result[i-2];
}
}
return result[i-1];
}
console.log(fibonacci(10));

Confused about the `return` statement in javascript. Explanation needed

I posted a question not too long ago this morning regarding a kata that I was trying to solve. In that question, (found here if interested Kata Question) I needed to add a return statement to my function so that I would avoid the following error Value is not what was expected.
Now I have my second iteration of my kata solution to try out and here it is:
function isMerge(s, part1, part2) {
var pointer = 0
splitString = s.split('');
splitString.forEach(function(character) {
if (part1.includes(character) || part2.includes(character)) {
pointer++;
return true;
} else {
return false;
}
});
}
isMerge('codewars','cdw','oears')
I am still getting Value is not what was expected errors when I try to execute the code and this time I'm confused as to why in particular this happens.
For starters, taken from the MDN guide
The return statement ends function execution and specifies a value to be returned to the function caller.
expression
The expression to return. If omitted, undefined is returned instead.
Look at my if/else logic I am specifying a return true and return false condition in my forEach loop to see if all the chars from part1 and part2 are in the string. I am returning something so why is it that I have a Value is not what was expected?.
Second of all, by definition of the return statement, the function is supposed to stop when it reaches that keyword. However, when I place a console.log(character) in the logic, I can see on my console that all of the characters are being outputted so the function is not breaking at all when return true is executed. Why is that?
Third, I am confused as to when to use the return keyword in general. Consider these examples from the MDN docs for ForEach.
Example 1:
function logArrayElements(element, index, array) {
console.log('a[' + index + '] = ' + element);
}
// Notice that index 2 is skipped since there is no item at
// that position in the array.
[2, 5, , 9].forEach(logArrayElements);
// logs:
// a[0] = 2
// a[1] = 5
// a[3] = 9
Example 2:
function Counter() {
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array) {
array.forEach(function(entry) {
this.sum += entry;
++this.count;
}, this);
// ^---- Note
};
var obj = new Counter();
obj.add([2, 5, 9]);
obj.count
// 3
obj.sum
// 16
Not a single return statement to in these examples.
Now look at this .every example.
function isBigEnough(element, index, array) {
return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough);
And finally, from my previous question, I need to add a second return statement like this to avoid the value error.
function isBigEnough(element, index, array) {
return element >= 10;
}
function whenToUseReturn(array) {
return array.every(isBigEnough);
}
whenToUseReturn([12, 5, 8, 130, 44]);
So....... in conclusion, for my original function that started this how am I supposed to exit the loop when I reach false and return it and likewise when all the characters are in the string, how do I return a 'cumulative' true and avoid a Value error. I hope this makes sense and I can clarify with edits to better illustrate my point.
I am returning something so why is it that I have a Value is not what was expected?.
The return statement returns from the callback you pass to forEach, not from isMerge. return statements don't cross function boundaries. isMerge doesn't contain a return statement, hence it returns undefined. If we rewrite the function slightly it might become clearer:
function doSomething(part1, part2) {
return function(character) {
if (part1.includes(character) || part2.includes(character)) {
return true;
} else {
return false;
}
}
}
function isMerge(s, part1, part2) {
splitString = s.split('');
splitString.forEach(doSomething(part1, part2));
}
isMerge('codewars','cdw','oears')
This is equivalent to your code. As you can see, there is no return statement in isMerge.
Not a single return statement to in these examples.
There are no return statements in the forEach examples because forEach doesn't do anything with the return value of the callback, so there is no point in returning anything.
forEach is just a different way to iterate over an array, but it doesn't produce a value like reduce or every.
how am I supposed to exit the loop when I reach false and return it and likewise when all the characters are in the string, how do I return a 'cumulative' true and avoid a Value error.
You cannot exit a forEach "loop". If you have to stop the iteration early, you need to use a normal for (for/in, for/of) loop.
To return and produce a value, you can use your original solution that uses every.
My friend, since you decided to go the "callback way" using .each and the like, you should consider using callbacks, since you cannot return anything in this case. If you do not wish to go the callback way, just use standard javascript, such as:
splitString.forEach(function(character) {
Replace with
for(var i = 0 ; i < splitString.length; i++){
And now you can return. Using "each" to loop an array is just plain unnecessary and prevents you to return.

Checking for collisions using filter()?

Normally I don't have too much trouble figuring out a problem in JS but this time I really need some help understanding this block of code. Mary Rose Cook used this logic in her space invaders game to filter through the bodies array to find collisions with other bodies.
var bodies = [];
...
update: function () {
// bodies is an array of all bodies in the game
var bodies = this.bodies;
var notCollidingWithAnything = function (b1) {
return bodies.filter(function(b2) { return colliding(b1, b2); }).length === 0;
};
this.bodies = this.bodies.filter(notCollidingWithAnything)
// ...insert function to draw the bodies that are in the new bodies array...
}
Can someone please explain how this.bodies.filter(notCollidingWIthAnything) works without passing in any parameters to the argument function? How does the the compiler know to check each element of the array against each other element of the array? Please guide me through what exactly happens in the compiler so that I can understand this.
Can someone please explain how this.bodies.filter(notCollidingWIthAnything) works without passing in any parameters to the argument function? How does the the compiler know to check each element of the array against each other element of the array?
The compiler (well, the JavaScript engine) doesn't know how to call notCollidingWIthAnything with the elements; Array#filter does.
notCollidingWIthAnything is a reference to the function. (Functions are proper objects in JavaScript, so we have references to them just like we have references to other objects.) The code passes that reference into Array#filter, and then Array#filter calls that function once for each element in the array, passing in the element value (and index, and array; it passes three args although we usually only use the first). Then it uses the return value of the callback to decide whether to include the element in the new array it builds.
Here's simplified code for Array#filter so you can see what's going on:
function arrayFilter(callback) {
// Remember this is called with `this` referring to an array-like object
// Create a new, empty array for the result
var result = [];
// Loop through the items
for (var index = 0; index < this.length; ++index) {
// Get the value for this entry
var value = this[index];
// Call the callback
if (callback(value, index, this)) {
// Got a truthy return value, include the value in the result
result.push(value);
}
}
// Return the new array
return result;
}
Again, that's simplified, not perfectly correct; for the perfectly correct steps, see the algorithm in the spec.
Here's an example with logging showing exactly who's doing what:
function arrayFilter(callback) {
console.log("Starting arrayFilter");
var result = [];
for (var index = 0; index < this.length; ++index) {
var value = this[index];
console.log("arrayFilter calling callback with value " + value);
if (callback(value, index, this)) {
console.log("arrayFilter got truthy result, include the value");
result.push(value);
} else {
console.log("arrayFilter got falsy result, don't include the value");
}
}
console.log("arrayFilter done");
return result;
}
function isOdd(value) {
var retval = value % 2 == 1;
console.log("isOdd called with " + value + ", returning " + retval);
return retval;
}
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log("calling `arrayFilter` with `a` as `this`, using `isOdd` callback");
var odds = arrayFilter.call(a, isOdd);
console.log("Resulting array: ", odds);

Categories