cdbbf149c68c9ab358bf28733f49e0ca783da57a
Javascript/How to Implement JavaScript Decorators.md
| ... | ... | @@ -0,0 +1,164 @@ |
| 1 | +https://blog.carlosrojas.dev/how-to-implement-javascript-decorators-c2d9e2a16a63 |
|
| 2 | + |
|
| 3 | +# How to Implement JavaScript Decorators | by Carlos A. Rojas | Medium |
|
| 4 | +[ |
|
| 5 | + |
|
| 6 | + |
|
| 7 | + |
|
| 8 | + |
|
| 9 | + |
|
| 10 | +](https://blog.carlosrojas.dev/?source=post_page---byline--c2d9e2a16a63--------------------------------) |
|
| 11 | + |
|
| 12 | +Photo by [Spacejoy](https://unsplash.com/@spacejoy?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral) |
|
| 13 | + |
|
| 14 | +JavaScript decorators are a powerful feature that allows you to modify the behavior of classes and their members in a declarative manner. Initially popularized in other programming languages like Python and Java, decorators provide a way to add functionality to methods, properties, and entire classes without altering their original code. This article will guide you through the process of implementing JavaScript decorators, explaining the concepts with clear examples and detailing their results. |
|
| 15 | + |
|
| 16 | +What are Decorators? |
|
| 17 | +-------------------- |
|
| 18 | + |
|
| 19 | +Decorators are functions that take a target (the element to be decorated) and optionally, some metadata (like property descriptors). They return a new target or modify the existing one. Decorators can be applied to classes, methods, accessors, properties, and parameters. |
|
| 20 | + |
|
| 21 | +Basic Syntax |
|
| 22 | +------------ |
|
| 23 | + |
|
| 24 | +The basic syntax of a decorator is straightforward. Here’s a simple example of a decorator function: |
|
| 25 | + |
|
| 26 | +``` |
|
| 27 | +function myDecorator(target) { |
|
| 28 | + // Modify the target in some way |
|
| 29 | + target.decorated = true; |
|
| 30 | +} |
|
| 31 | +``` |
|
| 32 | + |
|
| 33 | + |
|
| 34 | +Applying Decorators to a Class |
|
| 35 | +------------------------------ |
|
| 36 | + |
|
| 37 | +To apply a decorator to a class, you use the `@` symbol before the class definition. Here’s an example: |
|
| 38 | + |
|
| 39 | +``` |
|
| 40 | +function addTimestamp(target) { |
|
| 41 | + target.prototype.timestamp = new Date(); |
|
| 42 | +} |
|
| 43 | +@addTimestamp |
|
| 44 | +class MyClass {} |
|
| 45 | +const instance = new MyClass(); |
|
| 46 | +console.log(instance.timestamp); // Outputs the current date and time |
|
| 47 | +``` |
|
| 48 | + |
|
| 49 | + |
|
| 50 | +In this example, the `addTimestamp` decorator adds a `timestamp` property to the `MyClass` class. When you create an instance of `MyClass`, it includes the `timestamp` property initialized to the current date and time. |
|
| 51 | + |
|
| 52 | +Method Decorators |
|
| 53 | +----------------- |
|
| 54 | + |
|
| 55 | +Method decorators can modify the behavior of class methods. Here’s how you can create and apply a method decorator: |
|
| 56 | + |
|
| 57 | +``` |
|
| 58 | +function logMethod(target, propertyKey, descriptor) { |
|
| 59 | + const originalMethod = descriptor.value; |
|
| 60 | + descriptor.value = function (...args) { |
|
| 61 | + console.log(`Calling ${propertyKey} with arguments:`, args); |
|
| 62 | + return originalMethod.apply(this, args); |
|
| 63 | + }; |
|
| 64 | + return descriptor; |
|
| 65 | +} |
|
| 66 | +class MyClass { |
|
| 67 | + @logMethod |
|
| 68 | + greet(name) { |
|
| 69 | + return `Hello, ${name}!`; |
|
| 70 | + } |
|
| 71 | +} |
|
| 72 | +const instance = new MyClass(); |
|
| 73 | +console.log(instance.greet('World')); |
|
| 74 | +// Output: |
|
| 75 | +// Calling greet with arguments: [ 'World' ] |
|
| 76 | +// Hello, World! |
|
| 77 | +``` |
|
| 78 | + |
|
| 79 | + |
|
| 80 | +In this example, the `logMethod` decorator logs the method name and its arguments whenever the method is called, and then calls the original method. |
|
| 81 | + |
|
| 82 | +Accessor Decorators |
|
| 83 | +------------------- |
|
| 84 | + |
|
| 85 | +Accessor decorators can be used to modify the behavior of getters and setters. Here’s an example: |
|
| 86 | + |
|
| 87 | +``` |
|
| 88 | +function capitalize(target, propertyKey, descriptor) { |
|
| 89 | + const originalGetter = descriptor.get; |
|
| 90 | + descriptor.get = function () { |
|
| 91 | + const result = originalGetter.call(this); |
|
| 92 | + return result.toUpperCase(); |
|
| 93 | + }; |
|
| 94 | + return descriptor; |
|
| 95 | +} |
|
| 96 | +class Person { |
|
| 97 | + constructor(name) { |
|
| 98 | + this._name = name; |
|
| 99 | + } |
|
| 100 | + @capitalize |
|
| 101 | + get name() { |
|
| 102 | + return this._name; |
|
| 103 | + } |
|
| 104 | +} |
|
| 105 | +const person = new Person('john'); |
|
| 106 | +console.log(person.name); // Outputs: JOHN |
|
| 107 | +``` |
|
| 108 | + |
|
| 109 | + |
|
| 110 | +In this case, the `capitalize` decorator modifies the getter for the `name` property to return the name in uppercase. |
|
| 111 | + |
|
| 112 | +Property Decorators |
|
| 113 | +------------------- |
|
| 114 | + |
|
| 115 | +Property decorators can be used to add metadata to properties. However, they don’t directly modify property values. Here’s an example: |
|
| 116 | + |
|
| 117 | +``` |
|
| 118 | +function readOnly(target, propertyKey) { |
|
| 119 | + Object.defineProperty(target, propertyKey, { |
|
| 120 | + writable: false |
|
| 121 | + }); |
|
| 122 | +} |
|
| 123 | +class Car { |
|
| 124 | + @readOnly |
|
| 125 | + make = 'Toyota'; |
|
| 126 | +} |
|
| 127 | +const myCar = new Car(); |
|
| 128 | +console.log(myCar.make); // Outputs: Toyota |
|
| 129 | +myCar.make = 'Honda'; // This will fail silently or throw an error in strict mode |
|
| 130 | +console.log(myCar.make); // Still outputs: Toyot |
|
| 131 | +``` |
|
| 132 | + |
|
| 133 | + |
|
| 134 | +In this example, the `readOnly` decorator makes the `make` property non-writable, so any attempts to change its value will not succeed. |
|
| 135 | + |
|
| 136 | +Parameter Decorators |
|
| 137 | +-------------------- |
|
| 138 | + |
|
| 139 | +Parameter decorators can be used to add metadata to method parameters. Here’s an example: |
|
| 140 | + |
|
| 141 | +``` |
|
| 142 | +function logParameter(target, propertyKey, parameterIndex) { |
|
| 143 | + const originalMethod = target[propertyKey]; |
|
| 144 | + target[propertyKey] = function (...args) { |
|
| 145 | + console.log(`Parameter at index ${parameterIndex} in method ${propertyKey} is:`, args[parameterIndex]); |
|
| 146 | + return originalMethod.apply(this, args); |
|
| 147 | + }; |
|
| 148 | +} |
|
| 149 | +class User { |
|
| 150 | + greet(@logParameter name) { |
|
| 151 | + return `Hello, ${name}!`; |
|
| 152 | + } |
|
| 153 | +} |
|
| 154 | +const user = new User(); |
|
| 155 | +console.log(user.greet('Alice')); |
|
| 156 | +// Output: |
|
| 157 | +// Parameter at index 0 in method greet is: Alice |
|
| 158 | +// Hello, Alice! |
|
| 159 | +``` |
|
| 160 | + |
|
| 161 | + |
|
| 162 | +In this example, the `logParameter` decorator logs the value of a specific parameter whenever the method is called. |
|
| 163 | + |
|
| 164 | +JavaScript decorators provide a powerful and flexible way to enhance the functionality of classes and their members. By using decorators, you can keep your code clean and modular, adding features like logging, validation, or transformation in a declarative manner. The examples provided in this article demonstrate the various types of decorators and how they can be applied to different parts of your code. As decorators become more widely supported and standardized in JavaScript, they will undoubtedly become an essential tool in every developer’s toolkit. |
|
| ... | ... | \ No newline at end of file |