The Neverending Object
Tags: javascript, proxy, debugging
Published: 2023-04-24 13:35:02

---
title: The Neverending Object
published: true
date: 2023-04-24 15:35:39 UTC
tags: javascript,proxy,debugging
canonical_url: https://blog.birk-jensen.dk/the-neverending-object
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4drhb2out064pxzrhn1s.png
---

I've been messing with the [Proxy](https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-proxy-object-internal-methods-and-internal-slots) object lately, and I've created a very simple proxy, that allows for a neverending object chain. It's basically a chain of [No operations](https://en.wikipedia.org/wiki/NOP_(code)), and yes I know it's a hard sell, but I used it to rekindle one of my old projects and it can work a (tiny) bit like [Optional chaining (`?.`)](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-OptionalExpression), so I thought I'd share.

## Implementation

It's a small proxy, with a _[[ProxyHandler]]_ that traps [_[[Get]]_](https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver) and [_[[Call]]_](https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist), it simply returns the proxy object itself in each trap:

```javascript
const neverendingObject = new Proxy(function () { }, {
  apply: function () { return neverendingObject; },
  get: function () { return neverendingObject; }
});
```

Note the _[[ProxyTarget]]_ is a function, this will allow a `neverendingObject()` function call without any extra chaining.

For educational purposes, I'll add logging in the traps, this way it's easier to get what's happening:

```javascript
const neverendingObject = new Proxy(function () { }, {
  apply: function (target) { console.log('[[Call]] ()'); return neverendingObject; },
  get: function (target, prop) { console.log('[[Get]]', prop); return neverendingObject; }
});

neverendingObject.aProperty.aFunction().aProperty

// [[Get]] aProperty
// [[Get]] aFunction
// [[Call]] ()
// [[Get]] aProperty
```

### How it works

I had some trouble wrapping my head around the `apply` part, the extra `get` call getting the reference to `aFunction` first and then calling it after.  
I've included the call chain below highlighting each step of the way, thinking of it that way helped me out (remember that each _[[Get]]_ and _[[Call]]_ will return the `neverendingObject` object).

1. `neverendingObject` **_[[Get]]_** _aProperty_

2. `neverendingObject.aProperty` **_[[Get]]_** _aFunction_

3. `neverendingObject.aProperty.aFunction` **_[[Call]]_** _()_

4. `neverendingObject.aProperty.aFunction()` **_[[Get]]_** _aProperty_

5. `neverendingObject.aProperty.aFunction().aProperty`

Hopefully, this didn't add to any confusion.

## The optional chaining (`?.`) case

The `neverendingObject` can not replace the `?.` operator, but it's possible to mimic the behavior of chaining without testing every step. but only if the last action is a function, and yes that's a show-stopper for most use cases.

```javascript
const person = {
  arms(hasNoArms) {
    if (hasNoArms === true) {
      return neverendingObject
    } 

    return {
      wave() { console.log('Waving arms'); }
    };
  }
}

person.arms().wave(); // "Waving arms"
person.arms(true).wave(); // Nothing happens
```

Using the `?.` operator, it would look like the following (assuming `null` is returned instead of the `neverendingObject`, when the `hasNoArms` parameter is set):

```javascript
person.arms(true)?.wave(); // Nothing happens
```

The `?.` operator is more explicit and would be the preferred way to go most of the time. As a bonus the `?.` operator stops execution of the rest of the chain, where the `neverendingObject` will need to run the chain to its end, including any expensive calculation as an argument.

## An actual use case

With everything, there's wrong about the `neverendingObject`, I found a use case where it shines, which is adding conditional chaining to existing objects.

A very simple case is adding an `and` function to the `console` object, giving an easy way to only log output if a _condition_ is true without the added if:

```javascript
console.and = function (condition) {
  if (condition) {
    return console;
  }
  return neverendingObject;
}

console.and(1 == 1).log('is logged'); // "is logged"
console.and(1 == 2).log('is logged'); // Nothing happens
```

## Conclusion

Let's compare a simple `if` with the `and()` function:

```javascript
if (isDebugging) {
  console.log('Some message');
}

// vs

console.and(isDebugging).log('some message');
```

It saves a couple of lines, and it's (subjectively) easier to read. When using the `time()` and `timeEnd()` to do some light profiling those couple of lines add up:

```javascript
if (isDebugging) {
  console.time('abcd');
}
// Expensive stuff
if (isDebugging) {
  console.timeEnd('abcd');
}

// vs

console.and(isDebugging).time('abcd');
// Expensive stuff
console.and(isDebugging).timeEnd('abcd');
```

I've started a new branch of an old project: [ConditionalConsole](https://github.com/BirkAndMe/ConditionalConsole).  
The previous version needed to create another object than `console`. Using this technique I'm able to decorate it and make the functionality have a more native feel.