Use Lazy Values to Speed Up Your JS Apps
Tags: javascript
Published: 2019-08-07 15:45:30

Defining properties with "lazy values" is a technique you can use to improve performance in Object-Oriented JavaScript. It is especially beneficial because it can improve application load time, which can greatly impact important metrics like bounce rate and revenue.

In this article, we’ll answer:

- What is a Lazy Value?
- How do Lazy Values work?
- Should you use Lazy Values?

## What is a Lazy Value?

Normally when a property is created on an object, the expression that defines its value is evaluated _eagerly_. Eager evaluation means that the `getUniqueId` function in the following example is called immediately to compute the value for `id` whenever `MyObj` is called:

```js
function getUniqueId() {
    // some magic to create an ID
}

function MyObj() {
    this.id = getUniqueId();
}
var obj1 = new MyObj();
console.log(obj1); // -> { id: 1 }
```

The `getUniqueId` function is called even if the `id` property is never used in the rest of the code. Lazy Values avoid this evaluation. Lazy Values are not computed until the first time the property is read:

```js
var obj2 = new MyObjWithLazyId();
console.log(obj2); // -> { }
console.log(obj2.id); // -> 2
console.log(obj2); // -> { id: 2 }
```

## How do Lazy Values work?

The key to setting up Lazy Values is [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty). This API lets you define properties and gives you lots of control over how they behave.

To define a Lazy Value, first we define the property as a [getter](getter):

```js
Object.defineProperty(MyObjWithLazyId.prototype, 'id', {
    get: function() {
        return getUniqueId()
    }
});
```

> Notice how we're adding this to the `prototype` of our `MyObjWithLazyId` constructor. This allows us to define this property once and have it used by any instance of `MyObjWithLazyId`.

This gets us part of the way there, but with just this, the `id` property would change every time it is read:

```js
var obj3 = new MyObjWithLazyId();
console.log(obj3.id); // -> 2
console.log(obj3.id); // -> 3
console.log(obj3.id); // -> 5
console.log(obj3.id); // -> 8 
```

To make this work correctly, we first define the property using a getter, but when the getter is called, we use `Object.defineProperty` again to redefine the property as a value:

```js
Object.defineProperty(MyObjWithLazyId.prototype, 'id', {
    get: function() {
        var id = getUniqueId();
    	
        Object.defineProperty(this, 'id', {
            value: id
        });
 
        return id;
    }
}); 
```

Redefining the property as a value means that the next time it is read, the value will be returned without any function needing to be called.

## Should you use Lazy Values?

The benefit of using Lazy Values is that they don't have to be computed during the initialization of your app. As you can see from this [benchmark](https://jsbench.github.io/#3da95139f6e8599bd44386e958220621), the performance of  Lazy Values (the orange bar below) is very close to the performance of constructing a completely empty object, which is shown in the blue bar (larger is better):

![](https://lh6.googleusercontent.com/ivc_D5KWaFAUCURLN19lZeO4eCEzZePrDRZuQSIJYbxTcZfoOUoxOeoynW8G50tlHCYIPmPDjT7iJLPeHwPVVZ6qe5UD5k9K0D-brg3_GY3sUZeo4TLCtdq5KD0ph7EdqXAj1geu)

If you're building a module that will only be used in a few places in your application, the benefits of using this technique are probably outweighed by the complexity you're adding for yourself the next time you read this code. Make sure to performance test in your app to see if the benefits are worth any decrease in readability.

If the code you're writing will be used many times throughout your application or you are building a [world-class JavaScript library](https://canjs.com/), it is likely that your users will benefit from the performance improvement Lazy Values provide.

If you want to use this technique, we just published [can-define-lazy-value](https://canjs.com/doc/can-define-lazy-value.html) to make it very easy to get started. You can install it from [npm](https://www.npmjs.com/package/can-define-lazy-value) and take a look at its [25 lines](https://github.com/canjs/can-define-lazy-value/blob/4c8529d566f33eb6566bbb9da93e003192545e85/dist/cjs/define-lazy-value.js) of code to see exactly how it works.