A Definitive guide on JavaScript every() Method

Author: Abdullah Numan

Introduction

This article is about the every() method in JavaScript. This is the third part of the series titled Handy JavaScript Iteration Methods.

In this post, we expound on with examples of what the Array.prototy…


This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Necati Özmen

Author: Abdullah Numan

Introduction

This article is about the every() method in JavaScript. This is the third part of the series titled Handy JavaScript Iteration Methods.

In this post, we expound on with examples of what the Array.prototype.every() is, how it works - with and without the thisArg and see the impact of modifying the caller array from inside.

We'll also discuss what happens when we call JavaScript every() on sparse and empty arrays. But let's begin with the basics.

Steps we'll cover:

  • What is Array.prototype.every()?
  • How JavaScript every Works
  • JavaScript every() With thisArg Argument
  • every(callback, thisArg) Doesn't Work With Arrow Functions
  • every(callback, thisArg) Works With Non-Arrow Functions
  • Modifying the Caller Array
  • every() With Sparse Arrays
  • JavaScript every() With Empty Arrays

What is Array.prototype.every()?

Array.prototype.every() is a JavaScript iteration method that checks whether every element in an array satisfies a given condition. The method is called on an array of items, and the condition is checked with a callback function, callbackFn, and any necessary thisArg object passed to the execution context of the callback function:

// Method signature

every(callbackFn)
every(callbackFn, thisArg)

The first argument, callbackFn, is mandatory, and the second argument, thisArg is optional.

callbackFn(), in turn, takes three arguments. The first is the element being traversed to, element, which is mandatory. The second argument is the current index, index and the third is array, the array being iterated. Both the second and third arguments are optional:

// Method signature

every(function(element){...});
every(function(element, index){...});
every(function(element, index, array){...});

How JavaScript every Works

JavaScript every tests whether all elements satisfy the condition specified in the callback function,callbackFn. It attempts to execute callbackFn() once for each item in the array. If it finds one that evaluates to a falsy value, it immediately returns with the Boolean false. Otherwise, it seeks to traverse to the end of the array and returns true if all are truthy:

const numbers = [1, 2, 3, 4, 5];
const numbersDoubled = [2, 4, 6, 8, 10];

const even = element => element % 2 === 0;

const areAllEven = numbers.every(even);
const areAllDoubledEven = numbersDoubled.every(even);

console.log(areAllEven); // false
console.log(areAllDoubledEven); // true

In the chunk of code above, even() is our callback function, which we pass to every(). Apparently, we have at least one odd number in our numbers array. So, every() returns false for areAllEven. In contrast, all items in numbersDoubled are even, so we get true for areAllDoubledEven.

JavaScript every() With thisArg Argument

We can pass in the thisArg object to every() to add it to the execution context of the callback function. Let's start doing that now by first making some modifications to our callback.

Instead of checking for an even number, let's say we want to generalize our callback function to check if the item is divisible by a given number. We would like our callback to be something like the below:

function divisible(element, divisor) {
  return element % divisor === 0;
};

However, we cannot pass divisor as the second argument to divisible(), as our callback accepts index and array as the second and third arguments respectively. And it becomes overcrowded if we introduce a fourth with divisor.

We can get around this problem by passing divisor as a property of the thisArg object, the second argument to JavaScript every(). And then access the object with this from inside the callback:

const numbers = [1, 2, 3, 4, 5];
const numbersDoubled = [2, 4, 6, 8, 10];

function divisible(element) {
  return element % this?.divisor === 0;
};

const areAllEven = numbers.every(divisible, { divisor: 2 });
const areAllDoubledEven = numbersDoubled.every(divisible, { divisor: 2 });

console.log(areAllEven); // false
console.log(areAllDoubledEven); // true

Here, we set the thisArg object to { divisor: 2 }, which leads to checking whether the item is even or not.

We can try other divisor options, like checking if we have a number divisible by 3 or 7. Thanks to thisArg, this became very easily reproducible:

const areAllDivisibleByThree = numbers.every(divisible, { divisor: 3 });
const areAllDivisibleBySeven = numbers.every(divisible, { divisor: 7 });

console.log(areAllDivisibleByThree); // false
console.log(areAllDivisibleBySeven ); // false

every(callback, thisArg) Doesn't Work With Arrow Functions

If we look back at the first example that involves the even() callback, we defined it as an arrow function. And it worked.

We defined its extension, the divisible() function with named declaration syntax. And it worked as well.

If we declare divisible() as an arrow function, we run into problems:

const numbers = [1, 2, 3, 4, 5];
const numbersDoubled = [2, 4, 6, 8, 10];

const divisible = element => element % this?.divisor === 0;

const areAllDoubledEven = numbersDoubled.every(divisible, { divisor: 2 });
const areAllDoubledDivisibleByThree = numbersDoubled.every(divisible, { divisor: 3 });
const areAllDoubledDivisibleBySeven = numbersDoubled.every(divisible, { divisor: 7 });

console.log(areAllDoubledEven); // false
console.log(areAllDoubledDivisibleByThree); // false
console.log(areAllDoubledDivisibleBySeven); // false

All returning false, although we know areAllDoubledEven should be true and the other two false.

If we investigate with a modified divisible() function that logs this to the console, we see that this is undefined in strict mode:

// strict mode

const numbers = [1, 2, 3, 4, 5];
const numbersDoubled = [2, 4, 6, 8, 10];

const divisible = element => {
  console.log(this);
  return element % this?.divisor === 0;
};

const areAllDoubledEven = numbersDoubled.every(divisible, { divisor: 2 });

console.log(areAllDoubledEven);
// undefined
// false

Now, if we introduce a this.divisor property to the lexical environment of divisible(), we get its value logged to the console:

const numbers = [1, 2, 3, 4, 5];
const numbersDoubled = [2, 4, 6, 8, 10];

this.divisor = 'Hi';

const divisible = element => {
  console.log(this);
  return element % this.divisor === 0;
};

const areAllDoubledEven = numbersDoubled.every(divisible, { divisor: 2 });

console.log(areAllDoubledEven);
// { divisor: 'Hi' }
// false

Here, clearly, we have { divisor: 'Hi' } coming from divisible's closure. It turns out, the problem is due to the binding of divisible()'s this to its lexical environment because of the arrow syntax. It was undefined before we introduced this.divisor = 'Hi';. Now this is { divisor: 'Hi' }. In other words, { divisor: 2} is not being relayed to divisible's this.

So, JavaScript every() with thisArg does not work as expected with callbackFn defined with arrow syntax.

Backend devs love this React framework!

Meet the headless, React-based solution to build sleek CRUD applications. With refine, you can be confident that your codebase will always stay clean and boilerplate-free.

Try refine to rapidly build your next CRUD project, whether it's an admin panel, dashboard, internal tool or storefront.


refine blog logo

every(callback, thisArg) Works With Non-Arrow Functions

But as we have seen before, it works with callbacks defined with named function declarations:

function divisible(element) {
  return element % this?.divisor === 0;
};

const areAllDoubledEven = numbersDoubled.every(divisible, { divisor: 2 });

console.log(areAllDoubledEven); // true

It also works with anonymous function expressions:

const divisible = function(element) {
  return element % this?.divisor === 0;
};

const areAllDoubledEven = numbersDoubled.every(divisible, { divisor: 2 });

console.log(areAllDoubledEven); // true

Modifying the Caller Array

Array.prototype.every() sets the range of the items to be processed before the first invocation of the callback function. And if an item is changed after traversal, the change is disregarded by the callback function. That is, the callback function only respects the existing value of an item at the time it is visited.

We can witness this in a scenario where the caller array is mutated from inside JavaScript every().

every() itself does not modify the caller array, but the caller is available to the callback function as its third argument, array. This means we can deliberately mutate the caller when we need to from inside our callback, divisible():

function divisible(element, index, array) {
  array[0] = 7;
  array[4] = 7;

  console.log(array);

  return element % this?.divisor === 0;
};

In this scenario, if an unvisited item is changed ahead of time, the callback function - here divisible() - finds the new value when it visits the item and so the new value is processed. In contrast, it disregards changes to items that are already traversed:

const numbers = [1, 2, 3, 4, 5];
const numbersDoubled = [2, 4, 6, 8, 10];

const divisible = function(element, index, array) {
  array[0] = 7;
  array[4] = 7;

  console.log(array);

  return element % this?.divisor === 0;
};

const areAllDoubledEven = numbersDoubled.every(divisible, { divisor: 2 });
console.log(areAllDoubledEven);
console.log(numbersDoubled);
/*
  [ 7, 4, 6, 8, 7 ]
  [ 7, 4, 6, 8, 7 ]
  [ 7, 4, 6, 8, 7 ]
  [ 7, 4, 6, 8, 7 ]
  [ 7, 4, 6, 8, 7 ]
  false
  [ 7, 4, 6, 8, 7 ]
*/

In the console log statements above, the numbersDoubled array is being logged five times due to the console.log(array); statement we placed inside divisible().

As we can see, numbersDoubled is being mutated twice in the first call to divisible(). The first mutation happens when at numbersDoubled[0], i.e. after being visited, which changes the value of itself to 7. So, even though 7 is not divisible by the divisor 2, every() didn't immediately return false at index 0. Instead, it returned false in the next instance when it visited the unvisited and mutated value of 7 at numbersDoubled[4].

This shows that the callback function processes the value of an item as it finds it at traversal and disregards the changes made to it when and after it is at that index.


discord banner

every() With Sparse Arrays

Now, let's consider what happens when we have empty slots in the caller array.

We'll add a couple of empty items to numbersDouble and remove the mutations from divisible:

const numbers = [1, 2, 3, 4, 5];
const numbersDoubled = [2, 4, , 6, 8, , 10];

const divisible = function(element, index, array) {
  console.log(array);
  return element % this?.divisor === 0;
};

const areAllDoubledEven = numbersDoubled.every(divisible, { divisor: 2 });
console.log(areAllDoubledEven);
console.log('Caller length: ' + numbersDoubled.length);
/*
  [ 2, 4, <1 empty item>, 6, 8, <1 empty item>, 10 ]
  [ 2, 4, <1 empty item>, 6, 8, <1 empty item>, 10 ]
  [ 2, 4, <1 empty item>, 6, 8, <1 empty item>, 10 ]
  [ 2, 4, <1 empty item>, 6, 8, <1 empty item>, 10 ]
  [ 2, 4, <1 empty item>, 6, 8, <1 empty item>, 10 ]
  true
  [ 2, 4, <1 empty item>, 6, 8, <1 empty item>, 10 ]
*/

As we can see, we have two empty slots and the length of the caller array is 7. However, the numbersDoubled is logged 5 times, indicating 5 calls to divisible(). This is because divisible() was not invoked for the empty items.

JavaScript every() With Empty Arrays

If we call every() with divisible on an empty array, it returns true:

const emptyArray = [];

const divisible = element => {
  return element % this?.divisor === 0;
};

const testEmptyArray = emptyArray.every(divisible, {divisor: 2 });

console.log(testEmptyArray); // true

This is so because "all items" in an empty array vacuously satisfy the condition that they are even or anything else. Supposedly.

Conclusion

In this article, we focused on JavaScript every which helps us test whether all items in an array pass the test we implement in a callback function. We saw that the callback function can take three arguments, and additional arguments can be bound to its execution context by setting its this value with a thisArg passed to every() as the second argument.

We also found out that if we use arrow syntax to declare the callback function, its lexical environment messes with the binding of thisArg to its this object. So, we should be using non-arrow functions to declare a callback that uses this.


This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Necati Özmen


Print Share Comment Cite Upload Translate Updates
APA

Necati Ă–zmen | Sciencx (2022-11-02T14:24:01+00:00) A Definitive guide on JavaScript every() Method. Retrieved from https://www.scien.cx/2022/11/02/a-definitive-guide-on-javascript-every-method/

MLA
" » A Definitive guide on JavaScript every() Method." Necati Ă–zmen | Sciencx - Wednesday November 2, 2022, https://www.scien.cx/2022/11/02/a-definitive-guide-on-javascript-every-method/
HARVARD
Necati Ă–zmen | Sciencx Wednesday November 2, 2022 » A Definitive guide on JavaScript every() Method., viewed ,<https://www.scien.cx/2022/11/02/a-definitive-guide-on-javascript-every-method/>
VANCOUVER
Necati Ă–zmen | Sciencx - » A Definitive guide on JavaScript every() Method. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/11/02/a-definitive-guide-on-javascript-every-method/
CHICAGO
" » A Definitive guide on JavaScript every() Method." Necati Ă–zmen | Sciencx - Accessed . https://www.scien.cx/2022/11/02/a-definitive-guide-on-javascript-every-method/
IEEE
" » A Definitive guide on JavaScript every() Method." Necati Ă–zmen | Sciencx [Online]. Available: https://www.scien.cx/2022/11/02/a-definitive-guide-on-javascript-every-method/. [Accessed: ]
rf:citation
» A Definitive guide on JavaScript every() Method | Necati Ă–zmen | Sciencx | https://www.scien.cx/2022/11/02/a-definitive-guide-on-javascript-every-method/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.