Decorator in JavaScript

Decorator in JavaScript

What is decorator? How do they work, and what are they useful for? In this post, we'll explains all in this helpful introduction and using decorators to clean up, streamline your code.

This feature isn’t included in the newest ECMA-262, JavaScript in other words. You should always use Babel to use this in your project.

The examples I’ve attached to this post were written in JSFiddle, with the Babel + JSX configuration. If you want to use this feature in your project, you ought to set up Babel on your own.

Without a Decorator
class Medium {
  constructor(writer) {
    this.writer = writer;
  }

  getWriter() {
    return this.writer;
  }
}

There’s a class, Medium, that takes the name of the writer in its constructor. And there’s a function that returns the writer's name.

Let’s create a property that is of Medium type.

const medium = new Medium('Jane');

const fakeMedium = {
  writer: 'Fake Jane',
  getWriter: medium.getWriter,
};

medium is created using Medium’s constructor function, unlike fakeMedium which is an object literal. But it has the same properties as medium.

Now, let’s compare the result of getWriter from each.

medium.getWriter(); // Jane
fakeMedium.getWriter(); // Fake Jane

Why are the values different?

It’s because JavaScript’s normal function this is bound to the object that actually invokes the function.

medium.getWriter() is called by the medium object, however, fakeMedium.getWriter() is called by fakeMedium. So, the this inside the function, getWriter, looks up the value from fakeMedium.

To get the same result as when medium.getWriter is called, let’s use Object.defineProperty. What Object.defineProperty does is define new properties on the object or modify the existing properties on the object and then it returns the object.

const fakeMedium = { ... };
let isDefining;
let fn = fakeMedium.getWriter;
Object.defineProperty(fakeMedium, 'getWriter', {
  get() {
    console.log('Access to getWriter');
    if (isDefining) {
      return fn;
    }
    isDefining = true;
    const boundFn = this.getWriter.bind(medium);
    isDefining = false;
    
    return boundFn;
  }
});

Whenever fakeMedium.getWriter is called, Access to getWriter will be printed twice. But why twice?

  1. When you call fakeMedium.getWriter(), its getter-mode is detected and runs the customized get method.
  2. Inside the get method, the getWriter is newly bound by mediumthis.getWriter.bind(medium). Here, this refers to fakeMedium itself. So it’s the same as fakeMedium.getWriter.bind(medium). That’s why its get is called once again.
  3. But before the function is bound, isDefining is set to true, so the codes under the if-condition won’t be executed until isDefining is set back to false again.

But this way is really a pain in the neck. Because every time you make a new instance of Medium, you should do this again.

Can’t we do this in a more elegant way?

With a Decorator

Any function can be a decorator. Basically, you can use a decorator for either a class or a method in a class. It takes three arguments — target, value, and descriptor.

function decorator(target, value, descriptor) {}
  1. target refers to either the class or a prototype of the class.
  2. value is undefined for a class and is the name of the method for a method.
  3. descriptor is an object that contains definable properties on an object — such as configurable, writable, enumerable, and value. It’s undefined for a class.
function autobind(target, value, descriptor) {}
class Medium {
  ...
  @autobind
  getWriter() {
    return this.writer;
  }
}

A decorator is used with an at sign (@), with the name of the function that you’ll use as a decorator — and it takes three arguments as I just explained.

function autobind(target, value, descriptor) {
  const fn = descriptor.value;
  
  return {
    configurable: true,
    get() {
      return fn.bind(this);
    }
  }
}

descriptor.value is the name of the function on which you put the decorator function — in this case, it’s getWriter itself.

Note that the return value of autobind is a new object, then getWriter adopts the return value to its environment.

What’s good about using decorators is that they are reusable. All you need to do after defining the decorator function is merely to write @autobind on functions.

Here’s another example of making class member properties read-only, which is even easier.

function readonly(target, value, descriptor) {
  descriptor.writable = false;
  return descriptor;
}
class Medium {
  @readonly
  signUpDate = '2019-04-23';
}
const medium = new Medium();
medium.signUpDate; // 2019-04-23
medium.signUpDate = '1999-11-11'; 
medium.signUpDate; // 2019-04-23
^ The value isn't changed!

This time, the descriptor of the property has been changed by setting the writable property as false and that is all. Dead simple. Right?

Full Code Comparison

Here’s the comparison of the full code.

class Medium {
  constructor(writer) {
    this.writer = writer;
  }

  getWriter() {
    console.log(this.writer);
  }
}

const medium = new Medium('Jane');
const fakeMedium = {
  writer: 'Fake Jane',
  getWriter: medium.getWriter,
};

medium.getWriter(); // Jane
fakeMedium.getWriter(); // Fake Jane

/* Do auto-binding job for the same values */
let isDefining;
let fn = fakeMedium.getWriter;
Object.defineProperty(fakeMedium, 'getWriter', {
  get() {
    if (isDefining) {
      return fn;
    }
    isDefining = true;
    const boundFn = this.getWriter.bind(medium);
    isDefining = false;
    
    return boundFn;
  }
});

medium.getWriter(); // Jane
fakeMedium.getWriter(); // Jane

Without decorator

function autobind(target, value, descriptor) {
  const fn = descriptor.value;
  
  return {
    configurable: true,
    get() {
      return fn.bind(this);
    }
  }
}

class Medium {
  constructor(writer) {
    this.writer = writer;
  }
  
  @autobind
  getWriter() {
    console.log(this.writer);
  }
}

const medium = new Medium('Jane');
const fakeMedium = {
  writer: 'Fake Jane',
  getWriter: medium.getWriter,
};

medium.getWriter(); // Jane
fakeMedium.getWriter(); // Jane

With decorator

Try it out by yourself!

Conclusion

A decorator is very useful, powerful, amazing, and remarkable. Honestly, I don’t see any reason to say no to use this awesome feature.

You can check the proposal out on GitHub.

Thank you for reading!

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

What’s new in HTML6

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Hire React Js Developer from Expert React JS Development Company

Hire React Js Developer from Expert React JS Development Company

Are you looking to [hire React JS developer](https://www.mobiwebtech.com/react-js-development-company/ "hire React JS developer") from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team...

Are you looking to hire React JS developer from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team of expert React JS developers, you can hire them on an hourly, weekly or monthly basis for the successful completion of your dream project.

Top 7 Most Popular Node.js Frameworks You Should Know

Top 7 Most Popular Node.js Frameworks You Should Know

Node.js is an open-source, cross-platform, runtime environment that allows developers to run JavaScript outside of a browser. In this post, you'll see top 7 of the most popular Node frameworks at this point in time (ranked from high to low by GitHub stars).

Node.js is an open-source, cross-platform, runtime environment that allows developers to run JavaScript outside of a browser.

One of the main advantages of Node is that it enables developers to use JavaScript on both the front-end and the back-end of an application. This not only makes the source code of any app cleaner and more consistent, but it significantly speeds up app development too, as developers only need to use one language.

Node is fast, scalable, and easy to get started with. Its default package manager is npm, which means it also sports the largest ecosystem of open-source libraries. Node is used by companies such as NASA, Uber, Netflix, and Walmart.

But Node doesn't come alone. It comes with a plethora of frameworks. A Node framework can be pictured as the external scaffolding that you can build your app in. These frameworks are built on top of Node and extend the technology's functionality, mostly by making apps easier to prototype and develop, while also making them faster and more scalable.

Below are 7of the most popular Node frameworks at this point in time (ranked from high to low by GitHub stars).

Express

With over 43,000 GitHub stars, Express is the most popular Node framework. It brands itself as a fast, unopinionated, and minimalist framework. Express acts as middleware: it helps set up and configure routes to send and receive requests between the front-end and the database of an app.

Express provides lightweight, powerful tools for HTTP servers. It's a great framework for single-page apps, websites, hybrids, or public HTTP APIs. It supports over fourteen different template engines, so developers aren't forced into any specific ORM.

Meteor

Meteor is a full-stack JavaScript platform. It allows developers to build real-time web apps, i.e. apps where code changes are pushed to all browsers and devices in real-time. Additionally, servers send data over the wire, instead of HTML. The client renders the data.

The project has over 41,000 GitHub stars and is built to power large projects. Meteor is used by companies such as Mazda, Honeywell, Qualcomm, and IKEA. It has excellent documentation and a strong community behind it.

Koa

Koa is built by the same team that built Express. It uses ES6 methods that allow developers to work without callbacks. Developers also have more control over error-handling. Koa has no middleware within its core, which means that developers have more control over configuration, but which means that traditional Node middleware (e.g. req, res, next) won't work with Koa.

Koa already has over 26,000 GitHub stars. The Express developers built Koa because they wanted a lighter framework that was more expressive and more robust than Express. You can find out more about the differences between Koa and Express here.

Sails

Sails is a real-time, MVC framework for Node that's built on Express. It supports auto-generated REST APIs and comes with an easy WebSocket integration.

The project has over 20,000 stars on GitHub and is compatible with almost all databases (MySQL, MongoDB, PostgreSQL, Redis). It's also compatible with most front-end technologies (Angular, iOS, Android, React, and even Windows Phone).

Nest

Nest has over 15,000 GitHub stars. It uses progressive JavaScript and is built with TypeScript, which means it comes with strong typing. It combines elements of object-oriented programming, functional programming, and functional reactive programming.

Nest is packaged in such a way it serves as a complete development kit for writing enterprise-level apps. The framework uses Express, but is compatible with a wide range of other libraries.

LoopBack

LoopBack is a framework that allows developers to quickly create REST APIs. It has an easy-to-use CLI wizard and allows developers to create models either on their schema or dynamically. It also has a built-in API explorer.

LoopBack has over 12,000 GitHub stars and is used by companies such as GoDaddy, Symantec, and the Bank of America. It's compatible with many REST services and a wide variety of databases (MongoDB, Oracle, MySQL, PostgreSQL).

Hapi

Similar to Express, hapi serves data by intermediating between server-side and client-side. As such, it's can serve as a substitute for Express. Hapi allows developers to focus on writing reusable app logic in a modular and prescriptive fashion.

The project has over 11,000 GitHub stars. It has built-in support for input validation, caching, authentication, and more. Hapi was originally developed to handle all of Walmart's mobile traffic during Black Friday.

Node.js for Beginners - Learn Node.js from Scratch (Step by Step)

Node.js for Beginners - Learn Node.js from Scratch (Step by Step)

Node.js for Beginners - Learn Node.js from Scratch (Step by Step) - Learn the basics of Node.js. This Node.js tutorial will guide you step by step so that you will learn basics and theory of every part. Learn to use Node.js like a professional. You’ll learn: Basic Of Node, Modules, NPM In Node, Event, Email, Uploading File, Advance Of Node.

Node.js for Beginners

Learn Node.js from Scratch (Step by Step)

Welcome to my course "Node.js for Beginners - Learn Node.js from Scratch". This course will guide you step by step so that you will learn basics and theory of every part. This course contain hands on example so that you can understand coding in Node.js better. If you have no previous knowledge or experience in Node.js, you will like that the course begins with Node.js basics. otherwise if you have few experience in programming in Node.js, this course can help you learn some new information . This course contain hands on practical examples without neglecting theory and basics. Learn to use Node.js like a professional. This comprehensive course will allow to work on the real world as an expert!
What you’ll learn:

  • Basic Of Node
  • Modules
  • NPM In Node
  • Event
  • Email
  • Uploading File
  • Advance Of Node