JavaScript decorators have been came out, but they are still largely ‘experimental’ in JavaScript engines that support it. What are they? If you’re familiar with Java, you know how methods can be wrapped . The gist of it: a decorator wraps a JavaScript class method within another function, and it is invoked by annotation. a language feature since ES2015 through annotations I’ll be using NodeJS for the , so there will be several modules, plugins, and configurations that are needed. example code Installation 1. initialize an npm project in a new folder: npm init -y 2. install the babel command-line tools; we’ll be needing this to transpile the decorated methods: npm i --save-dev babel-cli 3. install plugins need for the transpilation: npm install --save-dev babel-eslint babel-plugin-transform-decorators-legacy babel-polyfill babel-preset-env babel-register eslint eslint-plugin-node 4. Create a .babelrc file to your project with these settings: { "presets": ["env"], "plugins": ["transform-decorators-legacy"] } : If you are using VSCode as an editor, go to Settings and turn on Experimental Decorators to make any warnings go away. Additional note 5. To run sample code, add the following run target in package.json: "scripts": { "start": "babel-node yourdecoratedcode.js --require babel-polyfill" }, Standard Log Example Before delving into parameter injection, let's first look at the ubiquitous @log example and later tweak it a bit. It is a rather plain decorator, of which you can find many variations in many languages. log = { original = descriptor.value; ( original === ) { descriptor.value = { result = original.apply( , args); .log( ) } } } const ( ) => target, name, descriptor /* target: the class instance of the method name: the name of the method descriptor: value: the method itself */ const // hold onto the original function if typeof 'function' //ensure that it is a function // substitute a new function for the original, this is what will be called instead ( ) function ...args const this // call the now-wrapped original console ` ( ) = ` ${name} ${args} ${result} The comments should make clear what is going on. The log function is the decorator, which wraps the class method that follows the decorator @log annotation. To use it, a class method is annotated with: { @log sum(a, b) { a + b; } } instance = MyClass() instance.sum( , ); class MyClass return const new 2 3 // execute the decorated method // the decorator dumps this string to the console: // sum(2,3) = 5 The console output shown as a comment at bottom comes from line 15 of the previous listing. Logging with Parameter Names With a little bit of digging, I was able to that illustrate a means of obtaining parameter names from a method signature. Now let’s have the decorator dump not just the parameter values, but the parameter names as well: find code examples STRIP_COMMENTS = ; ARGUMENT_NAMES = ; { fnStr = func.toString().replace(STRIP_COMMENTS, ); result = fnStr.slice(fnStr.indexOf( ) + , fnStr.indexOf( )).match(ARGUMENT_NAMES); (result === ) result = []; result; } log2 = { original = descriptor.value; ( original === ) { paramNames = getParamNames(original) descriptor.value = { params = paramNames.reduce( { obj[pn] = args[i]; obj;}, {} ) result = original.apply( , args); .log( ) } } } { @log2 sum(a, b) { a + b; } } instance = MyClass(); instance.sum( , ); var /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg var /([^\s,]+)/g ( ) function getParamNames func var '' var '(' 1 ')' if null return const ( ) => target, name, descriptor const if typeof 'function' const ( ) function ...args const ( ) => obj, pn, i return const this console ` ( ) = ` ${name} ${ .stringify(params)} JSON ${result} class MyClass return const new 4 5 // decorator outputs: sum2({"a":4,"b":5}) = 9 From the last line, the decorator will output sum2({ : , : }) = "a" 4 "b" 5 9 Injecting Parameters With decorators, you’re not actually limited to the arguments that you are given, but can infer parameter values from within the decorator, passing those onto the wrapped method. The following decorator is going to infer the argument values to be passed in. Instead of the decorator dumping a string to the console, we'll have the wrapped function log its parameter values: by parameter name insertStuff = { original = descriptor.value; ( original === ) { paramNames = getParamNames(original) descriptor.value = { args = paramNames.reduce( { arr[i] = .newStuff[pn]; arr;}, [] ) result = original.apply( , [...args]); } } } { @insertStuff getStuff(isWombat, sugar) { .log({isWombat, sugar}) } } const ( ) => target, name, descriptor const if typeof 'function' const ( ) function const ( ) => arr, pn, i this return const this // console.log(`${name}(${JSON.stringify(args)}) = ${result}`) class MyClass console This decorator will pass as arguments to the original function whatever is in and at the time the function is called; for instance, the following lists (via ) both the original values of , and then the new values: MyClass.newStuff.isWombat MyClass.newStuff.sugar getStuff() stuff stuff = { : , : [ , , ] } instance.newStuff = stuff; instance.getStuff(); instance.newStuff.sugar = [ , ]; instance.getStuff(); const isWombat true sugar "in the morning" "in the evening" "at suppertime" "You are my candy, girl" "and you keep me wanting you" And the output is… { isWombat: true, sugar: [ 'in the morning', 'in the evening', 'at suppertime' ] } { isWombat: true, sugar: [ 'You are my candy, girl', 'and you keep me wanting you' ] } Well, that wraps it up. All code in this article can be found . here