Written by Ibadehin Mojeed ✏️
The JavaScript .at()
method was introduced in the August release of the ECMA International TC39’s relative indexing proposal to allow developers to grab elements based on their indexes.
Selecting elements in JavaScript is a common occurrence in development, but, before the introduction of the .at()
method, JavaScript had existing methods and techniques to select elements or characters from the start or end of lists or within a string.
The bracket notation, []
, is commonly used to get an element at a specific index. But, this has its drawbacks. For instance, we cannot use a negative index syntax like arr[-1]
to access the last item in a list, which has become popular in Python.
Consequently, developers resort to using the slice()
method and the length
property to grab items from the end of a list. Nonetheless, they also have their respective drawbacks.
In this tutorial, we’ll look at the JavaScript .at()
method, its use cases, and how it can improve developers’ experiences compared to preexisting methods.
The indexable object prototype
The .at()
method is located on the prototype
of indexable objects.
These objects, which can formulate index entries, include classes like Array
, String
, and TypedArray
, which are Array.prototype.at()
, String.prototype.at()
, and %TypedArray%.prototype.at()
, respectively.
Because of this, we can perform the .at()
method directly on these indexable objects.
Existing methods for getting list elements
To see the benefits of the .at()
method, we will quickly walk through some of the existing methods for comparison purposes. This will also serve as a refresher for beginners.
Let’s consider an array of elements called arr
:
const arr = [1, 2, "three", 4, 5, true, false];
By using the bracket notation, []
, on the arr
array, we can get an element at a specific index. For instance, arr[0]
returns the first element, 1
, and so on. But, to get an item from the end of an unknown length, we use the length
property or the slice()
method.
Using the length
property
The length
property’s syntax is written as the following:
arr[arr.length - N];
Here, N
equals the nth element from the end of a list, and by using the syntax, we can grab any element from the end of that list.
In the following code, we use the syntax to grab the last element of the arr
array:
const arr = [1, 2, "three", 4, 5, true, false];
const lastItem = arr[arr.length - 1];
console.log(lastItem); // Expected Output: false
This works fine, but the syntax can be inconvenient and tedious for a simple task. Besides, one of its drawbacks when working with the returned value of a function compels us to first store the returned value in a variable before applying the syntax:
function appendNumber(arr, N) {
arr.push(N);
return arr;
}
const tempArr = appendNumber([1, 2, "three", 4, 5, true, false], 6);
console.log(tempArr[tempArr.length - 1]); // Expected Output: 6
In the code above, the returned value of the appendNumber()
function is first stored in the tempArr
variable before the length
property is applied.
The slice()
method
Developers can also employ the slice()
method to grab the last item of a list using the following syntax:
arr.slice(-1)[0]
This syntax allows a negative index, something you’ll see in the .at()
method later in this tutorial.
The negative index here indicates an offset from the end of the array. For instance, slice(-1)
removes the last item from the back and returns a new array; slice(-2)
removes the last two, and so on.
But here, the focus is on the last item, hence slice(-1)
in the syntax. Then, the [0]
notation selects the item at that index.
Using the syntax, we can grab the last item of the arr
array like so:
const arr = [1, 2, "three", 4, 5, true, false];
console.log(arr.slice(-1)[0]); // Expected Output: false
Unlike the length
property above, this method does not force us to store the returned value of a function before using the syntax. Hence making it more flexible:
function appendNumber(arr, N) {
arr.push(N);
return arr;
}
console.log(appendNumber([1, 2, "three", 4, 5, true, false], 6).slice(-1)[0]); // 6
Nonetheless, the syntax looks odd and doesn’t portray its intention. And of course, it can be inconvenient as well.
Why not access the last array element witharr[-1]
?
This question often comes up from JavaScript beginners, especially if they come from a programming language like Python.
The arr[-1]
notation in JavaScript is a valid object property. Remember everything in JavaScript, including arrays, are objects. So anytime we use the bracket notation, for instance, arr[0]
, we reference the property of the object with the key 0
.
By rewriting the arr
array in the object notation, we have something like this:
const arr = {
0: 1,
1: 2,
3: "three",
// ...
};
console.log(arr[0]); // Expected Output: 1
In the above code, we don’t have a key -1
. So, arr[-1]
returns a value of undefined
. If the object property has a key -1
, as seen in the code below, arr[-1]
returns its corresponding value:
const arr = {
"-1": "valid"
};
console.log(arr[-1]); // Expected Output: valid
This means we can’t use the arr[-1]
to grab the last element because it’s a valid syntax already. To use the negative index syntax to return an element from the end of a list, we will use the .at()
method.
The .at()
syntax
When using the .at()
syntax, it receives an index of an item to return. When passing a negative index, it counts from the end of the list or string and returns the item or character found. Else, it returns undefined
:
at(index)
The .at()
method in practice
As mentioned earlier, the .at()
method receives an index of the item to return. In this section, we will cover its use cases.
Let’s revisit the arr
array and see how the .at()
method allows us to seamlessly return an index element:
const arr = [1, 2, "three", 4, 5, true, false];
console.log(arr.at(0)); // Expected Output: 1
console.log(arr.at(2)); // Expected Output: "three"
console.log(arr.at(-1)); // Expected Output: false
console.log(arr.at(-3)); // Expected Output: 5
When a positive index passes to the .at()
method, it returns the element at that index. For a negative index, it counts back from the last element in the list and returns the element.
In the above code, at(-1)
counts one from the end of the array and returns false
, which is the element found. In the same way, at(-3)
counts three from the end and returns 5
.
Like arrays, we can do the same for strings:
const str = "The last alphabet is z";
console.log(str.at(0)); // Expected Output: T
console.log(str.at(-1)); // Expected Output: z
As we can see, this method is pleasant to use. With just .at(-1)
, we get the last character of the str
string. If we did the same task with the length
property, we would have a longer syntax, like so:
console.log(str[str.length - 1]); // Expected Output: z
Working with the returned value of a function
The .at()
method, unlike the length
property, does not compel us to store the returned value of a function in a variable before we can use it.
The following code outputs the last element pushed to an array:
function appendNumber(arr, N) {
arr.push(N);
return arr;
}
console.log(appendNumber([1, 2, "three", 4, 5, true, false], 6).at(-1));
// Expected Output: 6
In the code, the .at()
method applies directly to the returned value without first storing the value in a variable.
The .at()
method accepts numbers with a decimal
When a number with a decimal passes to the .at()
method, it considers the value before the decimal point and returns the item at that index.
Let’s take a look at the following code:
const arr = [1, 2, "three", 4, 5, true, false];
console.log(arr.at(0.6)); // Expected Output: 1
console.log(arr.at(-3.6)); // Expected Output: 5
In the code above, the first console outputs the item at the 0
index while the second console counts three from the end of the array and outputs the found item.
This is beneficial when we want to randomly select an index element. This can be demonstrated with a rock-paper-scissors game project. We can use the .at()
method syntax to determine a random selection for the computer.
The following code illustrates how we can apply the .at()
method to randomly select the computer’s choice:
const computerOptions = ["rock", "paper", "scissors"];
const randomIndex = Math.random() * computerOptions.length;
console.log(computerOptions.at(randomIndex));
Try it yourself on CodeSandbox.
WithMath.random()
, we get a floating-point number between 0
(inclusive) and the array length. Then, when the .at()
method receives this floating-point number as an index, it considers the value before the decimal point and returns the element at that index.
Using the square bracket notation for the same task is not possible unless we first floor the random number (that is, round the number down to the nearest integer), as seen in the code below:
const randomIndex = Math.floor(Math.random() * computerOptions.length);
console.log(computerOptions[randomIndex]);
This is because the bracket notation returns an undefined
value for numbers with decimals:
const arr = [1, 2, "three", 4, 5, true, false];
console.log(arr[0.6]); // Expected Output: undefined
The .at()
method saves us the extra steps of using Math.floor
to floor a random number.
Browser support
At the time of writing this tutorial, the .at()
method works pretty well with the popular modern browsers, except a few. See browser support here.
Conclusion
The .at()
method, as we have seen in this tutorial, is pleasant when grabbing items based on their index. It is also concise to use compared to the preexisting methods.
If you have any questions or contributions, please share them in the comment section. And if you like this tutorial, endeavor to share it around the web.
LogRocket: Debug JavaScript errors more easily by understanding the context
Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.
LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!
Top comments (0)