can someone explain how these two pieces of code achieve the exact same thing* or explain why they may appear to but do not?
for (i = 0; i < xx.length; i++) {
xx[i].classList.remove(...y);
}
xx.forEach(x => {
x.classList.remove(...y);
});
forEach is a method on the Array prototype. It iterates through each element of an array and passes it to a callback function.
So basically, forEach is a shorthand method for the use-case "pass each element of an array to a function". Here is a common example where I think Array.forEach is quite useful, compared to a for loop:
// shortcut for document.querySelectorAll
function $$(expr, con) {
return Array.prototype.slice.call((con || document).querySelectorAll(expr));
}
// hide an element
function hide(el) {
el.style.display = 'none';
}
// hide all divs via forEach
$$('div').forEach(hide);
// hide all divs via for
for (var divs = $$('div'), i = 0; i < divs.length; i++) {
hide(divs[i])
}
As you can see, the readability of the forEach statement is improved compared to a for loop.
On the other hand, a for statement is more flexible: it does not necessarily involve an array. The performance of a normal for loop is slightly better, because there is no function call for each element involved. Despite of this, it is recommended to avoid for loops when it can be written as a forEach statement.
Careful, if you declare "i" without "var" keyword it will be global.
You can check what forEach can do here:
forEach MDN
Related
I have a nested foreach which I want to break out of if a condition is met. It seems that using a return means that the execution breaks out of the outer foreach, not the inner:
angular.forEach(objectA, function(objA, key) {
angular.forEach(ObjectB, function(objB, key) {
if(foo !== bar) {
return;
}
});
});
I read that it is better to use s native for each for this purpose, so I created this fiddle which works as I need it to:
https://jsfiddle.net/3dfx14nt/
However when I apply the logic to my Angular app it behaves in the same way as the Angular.foreach does. When returning on the inner foreach it breaks out to the outer foreach.
forEach accepts a function and runs it for every element in the array. You can't break the loop. If you want to exit from a single run of the function, you use return
forEach doesn't support break, you should use return
If you want to be able to break the loop, you have to use for..of loop:
for(let name of group.names){
if (name == 'SAM') {
break;
}
}
I have a JS file that i'm working in from a previous employee and there code has functions that look like this:
funcName: function funcName() {
//Some stuff here
}
I've never seen a function start with "funcName:" before.
I removed it and the code still works.
Can someone explain the purpose of putting the function name as a property? (I assume it is a property since it has a leading colon. This function is not wrapped in anything else)
All else being equal, funcName: is a label and is meaningless because it isn't labelling a loop.
For future edification.
Labels can affect the functioning of your code and can be useful. Take a nested loop for example:
primaryLoop:
for (i = 0; i < 3; i++) {
secondaryLoop:
for (x = 0; x < 3; x++) {
if (i === 1 && x === 1) {
break primaryLoop;
}
}
}
We can break out of the primaryLoop by referring to it using the label.
It becomes an anonymous function. before it had the name that you could reference inside itself, after you deleted it the function still exists, just not a named way to reference it.
I have a variable 'fieldCount' that is equal to 5 (the number of fields I have). How can I write the following out without specifically stating each index:
if (
fields[0].checkValidity() && fields[1].checkValidity() && fields[2].checkValidity()
&& fields[3].checkValidity() && fields[4].checkValidity()
) {
fieldsPass = true;
}
You can use a loop, or some fairly standard built-in browser functions.
Loop
Here's how to do it with a loop. This should be the fastest code, but you probably don't need the fastest code unless you are checking a huge number of fields on a huge number of items. I recommend instead the "correct, clear, concise, fast" priorities, so think you should start with the built-in browser functions. But here it is for reference:
var fieldsPass = true;
var i, l = fields.length;
for (i = 0; i < l; i += 1) {
if (fields[i].checkValidity()) continue;
fieldsPass = false;
break;
}
Note that declaring the i variable outside of the loop and capturing the field length outside the loop are optional.
The first I did because many people don't know about hoisting, and the fact that for (var i ... does NOT create a variable only available inside the for loop, it is the same as having declared var i at the top of the function, and this behavior can lead to bugs.
The second I did out of habit, though as I said you can put it inside the loop check. See this discussion. If you do use the loop method, you're probably looking for better performance, so might want to use the captured length way for the best possible performance. (And if it really matters that much, you can do the var i = fields.length; while (i--) { } method.)
Browser Function Array.Every
You can use Array.prototype.every() (from ECMAScript 2015 6th edition):
Mozilla Developer Network
The every() method tests whether all elements in the array pass the test implemented by the provided function.
fieldsPass = fields.every(function(field) {
return field.checkValidity();
});
It returns truewhen the passed-in function, when run on every item in the array, returns true for all of them. If any one returns false, it stops and returns false. In some languages, they call the same concept all, if you're familiar with that.
Alternately, it might be better to declare your checkValidity function once, instead of putting it on every field. This may not be possible, depending on how you implemented it (perhaps it has access to private variables?). Notice that the first argument of the callback function you provide (see the documentation in the links above) is the currentValue of the iteration, the field you want to check. If your function thus looks like this, it will work:
function checkValidity(field) { /* check validity */ }
fieldsPass = fields.every(checkValidity);
Browser Function Array.Some
You can use also Array.prototype.some() (from ECMAScript 2015 6th edition):
Mozilla Developer Network
The some() method tests whether some element in the array passes the test implemented by the provided function.
fieldsPass = !fields.some(function(field) {
return !field.checkValidity();
});
Notice that it is basically just the inverse of every, as "ALL VALID" is the same as "NOT (ANY INVALID)". It just means that it checks for any one item in the array that passes the function, and if so, returns true. In some languages, they call the same concept any, if you're familiar with that.
General Browser Compatibility Notes
Note that for these two functions, browser compatibility is pretty good. If you don't care about IE below version 9 then you're pretty much safe. If you do, then you would want to use a polyfill, which is available on the above-linked MDN pages. You would include that code in the global scope of your javascript file, and then would be able to use it as normally in IE 8 and below. (I'm talking about the code block starting like this:)
if (!Array.prototype.every) {
Array.prototype.every = function(callbackfn, thisArg) {
...
}
You have an array of fields, and you want to iterate them, and invoke a validity check method on each of them. Only if all of them pass, the flag fieldsPass would be true.
This is exactly the behavior of Array#every. According to MDN:
The every() method tests whether all elements in the array pass the
test implemented by the provided function.
Use Array.every to invoke checkValidity() on each field. If all fields are valid, the result would be true. If one field fails checkValidity(), the loop would return false immediately, without checking the other fields.
var fieldPass = fields.every(function(field) {
return field.checkValidity();
})
Like you said, use a loop.
var fieldsPass = true;
for (var i = 0; i < fields.length; i++) {
// Exit early if one of them fails
if (!fields[i].checkValidity()) {
fieldsPass = false;
break;
}
}
You can use the .some() method and reverse the logic to tell if all passed like so:
var allPassed = !fields.some(function (field) {
return !field.checkValidity();
});
You can do it like this:
fieldsPass = true;
for (i = 0; i < fieldCount; i++)
{
if(!fields[i].checkValidity())
{
fieldsPass = false;
break;
}
}
Is there a way to return all elements with a given classname for use outside a loop with JavaScript?
I mean, even if you use querySelectorAll(), if you are not in a foreach or an i loop, it won't do anything.
For example, you can do this:
var test = document.querySelectorAll(".test");
for (var i =0; i < test.length; i++) {
var allClasses = test[i]
// do stuff
}
However, this is less than ideal for more complicated functions, where you don't want to work inside the loop itself. Or you don't want to run loops in loops in loops.
The same goes foreach, you need to work inside the foreach function.
var test = document.querySelectorAll(".test");
[].forEach.call(test, function(t){
var allClasses = t;
// do stuff
});
So is there a simple function to return all classes as a variable for use? Or can you only store it in an array? Either way, how do you do it? Also how would you then use it?
I have some javascript as shown below
for (var titleKey in data.d) {
var title = data.d[titleKey];
}
This is actually coming back from a JQuery call to a .NET webservice but I don't believe that's related.
My loop iterates over each element in the collection correctly, it then continues through the loop one more time. The titleKey here is 'indexof' and title is 'undefined'.
This is happening in two different places in my code.
What is causing this? How can I prevent it?
Thanks in advance.
You need to exclude from the loop the properties of the prototype. The for ... in structure will loop through everything* it finds in the prototype chain, not only the properties of the child object.
for (var titleKey in data.d) {
if (data.d.hasOwnProperty(titleKey)) {
// own property //
}
else {
// inherited property //
}
}
From what the console log says my suspicion is that you have a library that implements the indexof for Array in its prototype.
My recommendation would be to use the correct way to walk Arrays:
for (var index = 0; index < data.d.length; index++) {...}
for in is for Objects, not Arrays. This is a common beginner mistake, where one abuses the fact that Array is derived from Object.
* See comment from davidchambers
Javascript's "for-in" iterates through all properties of an object, and this includes method names.
Use the following loop mechanism:
for (var i = 0; i < titleKey.length; i++) {
var title = data.d[titleKey[i]];
}
The for:in loop loops through all properties of an object, rather than just ones that are indexable.
If indexof comes from the prototype chain, you must use hasOwnProperty to skip over it.
for (var titleKey in data.d) {
if (data.d.hasOwnProperty(titleKey))
{
var title = data.d[titleKey];
}
}
Based on the information at hand, I'd say that data.d.indexof is in fact undefined. The following is a perfectly valid data structure:
foo: 42
bar: [1, 2, 3]
baz: false
indexof: undefined
As a sanity check, try the following:
console.log(Object.prototype.hasOwnProperty.call(data.d, 'indexof'))
Edit: Now that it has become clear that the data structure is in fact an array, this answer does not solve the OP's problem. It's still valid, though, so I won't remove it.