Basic JavaScript Design Patterns — Factories, Iterators, and Decorators

John Au-Yeung - Jan 30 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

To organize our code, we should use some basic design patterns. In the article, we’ll look at factories, iterators, and decorators.

Built-in Object Factory

JavaScript’s standard library comes with several factory methods.

They include Object , Boolean , Number , String , and Array .

For instance, we can use the Boolean factory function as follows:

const bool = Boolean(1);
Enter fullscreen mode Exit fullscreen mode

Then bool is true since 1 is truthy.

All they do is return different objects or values according to what we pass in.

Many of them are useful. However, the Object constructor isn’t all that useful since we can create objects with object literals.

Iterator

JavaScript has iterators since ES6.

Therefore, we don’t have to write anything from scratch to implement this pattern

We just have to use a generator function to return the values we’re looking for sequentially.

Then we can call the built-in next function to get the value returned after the generator is returned from the generator function.

For instance, we can write:

const numGen = function*() {
  while (true) {
    yield (Math.random() * 100).toFixed(1);
  }
}
Enter fullscreen mode Exit fullscreen mode

to create a generator function.

A generator function is denoted by the function* keyword. There’s an asterisk after the function keyword.

The yield keyword will return the next value sequentially.

Then we can use it to return a random number as follows:

const nums = numGen();
console.log(nums.next().value);
console.log(nums.next().value);
console.log(nums.next().value);
Enter fullscreen mode Exit fullscreen mode

We first call numGen to return an iterator.

Then we call nums.next().value to get the values we want.

We can also have a generator function that returns a finite number of values.

For instance, we can write:

const numGen = function*() {
  const arr = [1, 2, 3];
  for (const a of arr) {
    yield a;
  }
}

const nums = numGen();
let value = nums.next().value;
while (value) {
  console.log(value);
  value = nums.next().value;
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we changed the numGen function to return an array’s entries in sequence.

This way, we can call the next method in a loop to get the entries in sequence with the next method.

In addition to value property, the object returned by the next method also has the done property.

So we can write:

const nums = numGen();
let {
  done,
  value
} = nums.next();

while (!done) {
  console.log(value);
  const next = nums.next();
  done = next.done;
  value = next.value;
}
Enter fullscreen mode Exit fullscreen mode

Once all the values are returned, done will be set to true.

Additional Functionality

If we want more functionality, then we’ve to implement our own iterator.

For instance, if we want rewind functionality, we can create our own iterator as follows:

const iterator = (() => {
  const arr = [1, 2, 3];
  let index = 0;
  return {
    next() {
      index++;
      return arr[index];
    },
    rewind() {
      index--;
      return arr[index];
    }
  }
})();
Enter fullscreen mode Exit fullscreen mode

We just added an array with the next method to move to the next arr entry and rewind to move to the previous arr entry.

Then we can use iterator as follows:

console.log(iterator.next());
console.log(iterator.rewind());
Enter fullscreen mode Exit fullscreen mode

And we see:

2
1
Enter fullscreen mode Exit fullscreen mode

from the console log output.

Decorator

Decorators let us add functionality to an object on the fly.

JavaScript has decorators now so that we can use them to create our own decorator and mutate objects on the fly.

For instance, we can create one as follows:

const foo = () => {
  return (target, name, descriptor) => {
    descriptor.value = "foo";
    return descriptor;
  };
};

class Foo {
  @foo()
  bar() {
    return "baz";
  }
}

const f = new Foo();
console.log(f.bar);
Enter fullscreen mode Exit fullscreen mode

A decorator in JavaScript, it’s just a function that returns a function with the parameters target , name and descriptor .

descriptor is the property descriptor, so we can set the value , writable , and enumerable properties for it.

Our foo decorator transformed the value property of it to the string 'foo' .

So we get that Foo instance’s bar property becomes the string 'foo' instead of a method.

JavaScript decorators are still experimental syntax, so we need Babel to use it.

Conclusion

JavaScript has various factory functions that let us create objects.

There’s the Object , Boolean , Number , String functions and more.

JavaScript has built-in support for iterators. However, if we want more functionality that it provides, then we’ve to implement our own.

Decorators exist in JavaScript, so we can use it to create decorators to modify properties.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .