Interesting JavaScript Features from a Ruby Perspective
Samuel O'Daniels
Posted on June 10, 2023
Hi there. I recently began relearning JavaScript, as it had been a year since I last did anything meaningful with it. So, I thought to share some of the differences that stood out to me as a Ruby developer. There should be a part 2, so you might want to watch out for that if you find this one entertaining.
Enough said. Let's jump right to the first difference.
No Error for Calling an Undefined Property
This tripped me up at first, but what's really happening is that classes in JavaScript are basically templates for creating Objects (hashes in Ruby). So, what looks like a "method" call (if you're coming from Ruby) is actually just accessing the property, including accessor properties like getters and setters, using dot notation. JS methods still need the ()
to be invoked.
Whereas in Ruby, any information you want from an object must be gotten through a method call. I'll attempt an example.
// JavaScript Class
class DemoClass {
constructor() {
this.codeName = 'You know who';
}
regularName = 'Unknown';
}
let target = new DemoClass;
console.log(target.codeName);
// => You know who
console.log(target['codeName']); // using brackets
// => You know who
Here, we can see that target
is just a regular object (hash), and as a result, its properties can also be accessed using bracket notation, just like a hash in Ruby.
To access the same property in Ruby, we'll have to define a getter method that returns that property when called. target
will be an instance of a real class, not a hash, so bracket notation won't work here. Also, I think referring to hashes as "objects" in JavaScript is just confusing, lol.
Optional Chaining
This dot notation for properties in JavaScript objects also affects the use of optional chaining (safe navigation in Ruby) because, depending on the usage, you will need less of the operator compared to Ruby.
Let's look at an example.
Say we have a user who, unbeknownst to us, doesn't have a currentAccount
property, and we want to safely check their current account balance; this is how we can do that in JavaScript:
// ?. is the optional chaining operator in JS
user.currentAccount?.balance;
// => undefined
And in Ruby:
# &. is the safe navigation operator in Ruby
user&.current_account&.balance
# => nil
Notice that we had to use the operator twice in Ruby to avoid an error in case current_account
was not a valid/defined method for the user
object.
There's another difference when it comes to safely calling functions or methods. With JavaScript, ?.
is placed between the function name and the parenthesis like so:
// supposing 'name' is undefined or null
user.name.toUpperCase?.();
In Ruby, the use appears more straightforward:
user&.name&.upcase
# even if you use parenthesis
# which you shouldn't use without arguments
user&.name()&.upcase()
Update: While testing the code examples for this section and the one before it, I made the mistake of "calling" JS functions as if they were properties. This created a hole in my understanding, causing me to make an inaccurate claim. Thankfully, u/jrochkind pointed it out on Reddit, so I made modifications to clarify the affected sections.
Accessing the Last Array Element
If you're coming from Ruby, you may instinctively try array[-1]
, but it'll return undefined
. So, to actually get the last element in a JS array, you have to do:
array[array.length - 1];
// or less preferably
array.slice(-1)[0];
Using slice
this way looks hacky, and you wouldn't be able to modify the element in place since it returns a new array.
With Ruby, however, you simply do:
array[-1]
# or to modify the element:
array[-1] = 'new last element'
Incrementing Numbers
Javascript has an increment (++
) operator that you can use instead of doing something like num += 1
or num = num + 1
.
However, Ruby does not have such an operator, so you have to use num += 1
.
Function Parameters are Optional by Default
In JavaScript, missing arguments become undefined
instead of raising an exception. Ruby, however, will raise an ArgumentError
. Let's look at an example.
In JavaScript
// passing only one argument instead of two
function greet(timeOfDay, name) {
return `Good ${timeOfDay}, ${name}`;
}
greet('morning');
// => Good morning, undefined
In Ruby:
def greet(time_of_day, name)
"Good #{time_of_day}, #{name}"
end
greet('morning')
# => wrong number of arguments (given 1, expected 2) (ArgumentError)
No none()
Even Though There's every()
JavaScript Arrays have every()
and some()
predicate methods, just like Array#all?
and Array#any?
in Ruby, but unlike JavaScript, Ruby Arrays have a #none?
method, which is the opposite of #any?
.
You might achieve a similar effect in JavaScript by either defining a none()
function:
let none = (array, callback) => !array.some(callback);
// and use it like so
let arr = [1, 3, 4, 9, 15];
let result = none(arr, item => item > 20);
// => true
or simply negating the call like so:
arr = [1, 3, 4, 9, 15]
result = !array.some(item => item > 20);
// => true
In Ruby though, you write the same logic this way:
arr = [1, 3, 4, 9, 15]
result = arr.none? { |item| item > 20 }
# => true
Array.forEach() returns undefined
This might seem intuitive, and it probably is, but in Ruby, Array#each
returns self
, which is the object the method was called on. Here's an example.
In JavaScript:
const letters = ['a', 'b', 'c'];
result = letters.forEach((letter) => console.log(letter));
// result => undefined
In Ruby:
letters = ['a', 'b', 'c']
result = letters.each { |letter| puts letter }
# result => ['a', 'b', 'c']
Alright. That's the last one. I hope you found these rather interesting or insightful. Thanks for reading.
Until next time.
Posted on June 10, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.