Interesting JavaScript Features from a Ruby Perspective
Tags: javascript, ruby, programming, beginners
Published: 2023-06-10 22:05:06

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.
```js
// 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) ([safe navigation](https://ruby-doc.org/core-2.6/doc/syntax/calling_methods_rdoc.html#label-Safe+navigation+operator) 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:
```js
// ?. is the optional chaining operator in JS
user.currentAccount?.balance;
// => undefined
```

And in Ruby:
```rb
# &. 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:
```js
// supposing 'name' is undefined or null
user.name.toUpperCase?.();
```
In Ruby, the use appears more straightforward:
```rb
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:
```js
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:
```rb
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
```js
// passing only one argument instead of two
function greet(timeOfDay, name) {
    return `Good ${timeOfDay}, ${name}`;
}

greet('morning');
// => Good morning, undefined
```
In Ruby:
```rb
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:

```js
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:

```js
arr = [1, 3, 4, 9, 15]
result = !array.some(item => item > 20);
// => true
```

In Ruby though, you write the same logic this way:
```rb
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:
```js
const letters = ['a', 'b', 'c'];
result = letters.forEach((letter) => console.log(letter));
// result => undefined
```

In Ruby:
```rb
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.