DEV Community

Samuel O'Daniels
Samuel O'Daniels

Posted on

Interesting JavaScript Features from a Ruby Perspective

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

And in Ruby:

# &. is the safe navigation operator in Ruby
user&.current_account&.balance
# => nil
Enter fullscreen mode Exit fullscreen mode

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?.();
Enter fullscreen mode Exit fullscreen mode

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()

Enter fullscreen mode Exit fullscreen mode

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];
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

or simply negating the call like so:

arr = [1, 3, 4, 9, 15]
result = !array.some(item => item > 20);
// => true
Enter fullscreen mode Exit fullscreen mode

In Ruby though, you write the same logic this way:

arr = [1, 3, 4, 9, 15]
result = arr.none? { |item| item > 20 }
# => true
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

In Ruby:

letters = ['a', 'b', 'c']
result = letters.each { |letter| puts letter }
# result => ['a', 'b', 'c']
Enter fullscreen mode Exit fullscreen mode

Alright. That's the last one. I hope you found these rather interesting or insightful. Thanks for reading.
Until next time.

Top comments (4)

Collapse
 
mkadirtan profile image
mkadirtan

Actually, the undefined return from Array.forEach is intuitive. Because the method is meant to apply some logic to all elements and not modify the array. So, returning itself is not very useful, but if you use the Array.map operator it returns the array back.

Collapse
 
samuelodan profile image
Samuel O'Daniels

Hmm, I see. I hear that since all methods return something, it might as well return the same array instead of nil. The for loop in Ruby also returns self. Though I'm not sure I can justify this one, lol. I hear it's because it uses each under the hood.

Collapse
 
miketalbot profile image
Mike Talbot ⭐

Just a note, the preferred way to get the last item in an array in JavaScript is array.at(-1) these days.

Collapse
 
samuelodan profile image
Samuel O'Daniels

That looks pretty slick. A lot better than using the length property. Thanks for sharing.