What is a Decorator in JavaScript?
When you write a class in JavaScript, you might have had to add more features to the methods in a class. But sometimes they look quite nasty and messy.
How can you make the process more elegant? In this post, we will talk about a promising feature, a decorator.
Before Reading
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
.
This article outlines the difference between normal functions and arrow functions.
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?
- When you call
fakeMedium.getWriter()
, its getter-mode is detected and runs the customizedget
method. - Inside the
get
method, thegetWriter
is newly bound bymedium
–this.getWriter.bind(medium)
. Here,this
refers tofakeMedium
itself. So it’s the same asfakeMedium.getWriter.bind(medium)
. That’s why itsget
is called once again. - But before the function is bound,
isDefining
is set to true, so the codes under the if-condition won’t be executed untilisDefining
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) {}
target
refers to either the class or a prototype of the class.value
isundefined
for a class and is the name of the method for a method.descriptor
is an object that contains definable properties on an object – such as configurable, writable, enumerable, and value. It’sundefined
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 we 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
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
Try it out by yourself!
Conclusion
A decorator is very useful, powerful, amazing, and remarkable. Honestly, we don’t see any reason to say no to use this awesome feature.
But, remember that it’s still at stage 2 and the way we used this in this post is more like Babel’s style, not the currently proposed one at stage 2. So, things might be different, like how to use it or what you can actually do with it.
So, we absolutely recommend you use this feature with the appropriate Babel configurations for your project but we also want to mention to keep an eye on this feature in TC39.
For more information and to develop your web app using front-end technology, Hire Front-End Developer from us as we give you a high-quality solution by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft“. To develop your custom website using JS, please visit our technology page.
Content Source:
- medium.com
New Features In ES2020
ECMAScript has grown to be one of the world’s most widely used general-purpose programming languages. ECMAScript is based on several originating technologies, the most well-known being JavaScript (Netscape) and JScript (Microsoft). Here are the new features of ES2020.
Dynamic Import
Now, you can import a file dynamically.
import { max } from '../math.js'; const nums = [1, 2, 3]; max(...nums); // 3
This has been the way we could import a file. And the JavaScript engine reads the modules in the file and bring them into the file where those modules are called. But now, you can do this as follows.
const numbs = [1, 2, 3]; if (numbs.length) { const math = '../math'; import(math) .then(module => { module.max(...numbs); }) }
A dynamic import returns a promise. Which means you can write it this way as well.
const math = '../math.js'; const module = await import(math); module.max(...numbs);
Why this feature is good is that you can use a dynamic import in a regular JavaScript code like the example above.
Here’s the browser support for Dynamic Import.
- Chrome: 63
- Edge: No Support
- Firefox: 67
- IE: No Support
- Opera: 50
- Safari: 11.1
BigInt
When you had to add two numbers that are too big enough to cause an overflow, weren’t you suffered?
Number.MAX_VALUE * 2 // Infinity
BigInt is a savior in this case.
You can make a BigInt by calling BigInt()
with parenthesis or 2n
with ‘n’ at the end of a number.
const num = 2; const bigNum = BigInt(num); bigNum; // 2n bigNum === 2n; // true
You can also add, subtract, multiply and divide it.
const bigN = BigInt(10); bigN + bigN; // 20n bigN * 3n; // 30n bigN - BigInt('55'); // 45n bigN / 3n; // 3n
Note that bigN / 3n
returns 3n
, not 3.33333n
. Because as you also can assume from its name, it only handles the integers. So bigN / 3n
is similar to Math.floor(10 / 3)
.
However, unfortunately, you can’t make a BigInt with a float number. And also, you can’t use a BigInt and a Number together, either.
BigInt(3.3); // Uncaught RangeError BigInt('3.3'); // Uncaught SyntaxError BigInt(1) + 1; // Uncaught TypeError // Cannot mix BigInt and other types
Instead, the only allowed case to mix the operations is when you compare the size of the operations.
BigInt(1) < 2 // true
And a BigInt can be evaluated like a Number if it’s in if condition.
function print(n) { if (BigInt(n)) { console.log('hi'); } else { console.log('bye'); } } print(1); // hi print(0); // bye
Here’s the browser support for BigInt
- Chrome: 67
- Edge: No Support
- Firefox: 68
- IE: No Support
- Opera: 54
- Safari: No Support
Promise.allSettled
This is quite similar to Promise.all , but there’s a significant difference between them. Promise.all waits for all the promises being fulfilled or an any promise being rejected. On the other hand, Promise.allSettled doesn’t care about that. What it cares is to know if all the promises are done, whichever their status is. So every input promise could be fulfilled or rejected, but it doesn’t matter to Promise.allSettled . Just all of them have to be done.
const promises = [ Promise.resolve(1), Promise.reject(2), Promise.resolve(3) ]; const onResolve = (data, prefix) => { console.log(prefix, 'Resolved with', data); }; const onReject = (err, prefix) => { console.log(prefix, 'Rejected with', err); }; Promise.all(promises) .then(onResolve.bind(null, 'all')) .catch(onReject.bind(null, 'all')); // Result: // all Rejected with 2 Promise.allSettled(promises) .then(onResolve.bind(null, 'allSettled')) .catch(onReject.bind(null, 'allSettled')); // Result: // allSettled Resolved with // [ // { // "status": "fulfilled", // "value": 1 // }, // { // "status": "rejected", // "reason": 2 // }, // { // "status": "fulfilled", // "value": 3 // } // ]
This might be quite useful when you want to do some works before some action, for example, getting all of the required data before the user sees the list page. But the user could see the empty items because the fetch might be failed.
Here’s the browser support for Promise.allSettled
.
- Chrome: 76
- Edge: No Support
- Firefox: 71
- IE: No Support
- Opera: Unknown
- Safari: Unknown
globalThis
This is lit. It’s dead simple and easy to use.
globalThis
refers to the global this
context on which your running context is. If you’re on Browsers, globalThis
will be this , if you’re on Node, globalThis
will be global
. Hence no need to think about the different environmental issues anymore.
// worker.js globalThis === self // node.js globalThis === global // browser.js globalThis === window
And this is how it works under the hood, but don’t use it in your code!
var getGlobal = function () { if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } throw new Error('unable to locate global object'); };
Here’s the environmental support for gloablThis
.
- Chrome: 71
- Edge: No Support
- Firefox: 65
- IE: No Support
- Opera: No Support
- Safari: 12.1
- Node: 12
For more information and to design a website using front-end technology, Hire Front-End Developer from us as we give you a high-quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft“. To develop your custom website using JS, please visit our technology page.
Content Source:
- medium.com
What’s new for Node.js in 2020
In 2019, Node.js turned 10 years old, and the number of packages available on npm crossed one million. Downloads for Node.js itself continues to rise, growing 40% year over year. Another significant milestone is Node.js recently joined the OpenJS Foundation, which promises to improve project health and sustainability, as well as improve collaboration with the JavaScript community at large.
What’s new in Node.js Version 13?
As of this writing, the most recent release of Node.js is 13. There are already a number of features and updates we can start playing around with leading into 2020. Here’s a list of highlights:
- ECMAScript modules
- WebAssembly support
- Full internationalization support for date, time, number, and currency formats
Before we dive into the details of these updates, let’s take a look at what we can expect from the Node.js release schedule.
The Node.js release process for 2020
Major Node.js versions enter Current release status for six months, which gives library authors time to add support for them. After six months, odd-numbered releases (9, 11, etc.) become unsupported, and even-numbered releases (10, 12, etc.) move to Active LTS status and are ready for general use. LTS release status is “long-term support”, which typically guarantees that critical bugs will be fixed for a total of 30 months. Production applications should only use Active LTS or Maintenance LTS releases.

pic courtesy: nodejs.org
Support for ECMAScript modules
As of v13.2.0, Node.js supports both traditional CommonJS modules and the new standard ECMAScript (ES) modules out of the box. This means you can finally use import and export syntax you may already be using for client-side JavaScript running in the browser. Also, it’s important to note ES modules in Node.js have JavaScript strict mode enabled by default, so you don’t have to specify “use strict”; at the top of every file.
// message file async function sendMessage { ... } export { sendMessage }; // index file import { sendMessage } from "./message";
However, you still need to do a little work to let Node.js know you are using ES modules. The two most common ways to do this are using the .mjs
file extension or specifying “type”: “module” in the nearest parent package.json file.
- Option 1: Rename .js files to .mjs files.
- Option 2: Update the root package.json file, or add a package.json to the folder that contains ES modules and specify the type as module.
{ "type": "module" }
Another possibility is enabling ES module in the root package.json file, and then renaming all CommonJS module files to use the .cjs
extension.
Personally, I find the .mjs
and .cjs
extensions a little gross, so I’m glad to see there are ways of specifying ES and CommonJS module usage with a package.json file.
Node.js can import WebAssembly modules
Along with ES module support comes the ability to import WebAssembly (Wasm) modules! A WebAssembly module is a portable compiled binary format that can be parsed faster than JavaScript and executed at native speeds. WebAssembly modules can be created using a language such as C/C++, Go, C#, Java, Python, Elixir, Rust, and many others.
WebAssembly module support is still in the experimental stage as of this writing. To enable the feature, you must pass a command-line flag when executing a Node.js application. For example:
node --experimental-wasm-modules index.js
As an example, imagine you have an image processing library implemented as a WebAssembly module. The syntax for using this Wasm module might look like the following.
import * as imageUtils from "./imageUtils.wasm"; import * as fs from "fs"; ( async () => { const image = await fs.promises.readFile( "./image.png" ); const updatedImage = await imageUtils.rotate90degrees( image ); } )();
It’s also possible to import a WebAssembly module using the new dynamic import()
statement in Node.js
"use strict"; const fs = require("fs"); ( async () => { const imageUtils = await import( "./imageUtils.wasm" ); const image = await fs.promises.readFile( "./image.png" ); const updatedImage = await imageUtils.rotate90degrees( image ); } )();
WebAssembly System Interface (WASI)
Similar to JavaScript, WebAssembly is designed with security in mind to prevent access to any of the underlying operating system, sometimes referred to as “sandboxed.” However, there are times when a WebAssembly module in your control in Node.js may benefit from being able to make system-level calls.
This is where the new WebAssembly System Interface (WASI) comes in. WASI is designed to be a standard interface for making calls to the underlying system, such as the host application, native operating system, and so forth.
Initial WASI support was recently committed to the Node.js project. WASI is another exciting feature we may see come to Node.js in 2020!
Internationalization support expands in 2020
As of v13.x, Node.js comes compiled with full ICU (International Components for Unicode). ICU is a mature and popular globalization library. Among many features, ICU includes support for formatting numbers, dates, times and currencies, performing time calculations and string comparisons, and converting text between Unicode and other character sets.
Hire Node.js Developer from us, as we give you high quality product by utilizing all the latest tools and advanced technology. E-mail us any clock at – hello@hkinfosoft.com or Skype us: “hkinfosoft“.
To develop the custom web app using Node.js, please visit our technology page.
Content Source:
- developer.okta.com
- nodejs.org