「JavaScriptDeveloper。Professional」コースの将来の学生には、「Node.jsでインタラクティブなテレグラムボットを作成する」というトピックに関する公開レッスンにサインアップすることをお勧めします。そして今、私たちは有用な資料の伝統的な翻訳を共有しています。
デコレータ機能を理解する
デコレータとは何ですか?
デコレータは、ある機能を別の機能にラップしてその機能を拡張できるようにする機能です。既存のコードを他のコードでラップして「装飾」します。このトリックは、関数の構成や高次の関数に精通している人なら誰でも知っています。
デコレータは新しいものではありません。これらは、Pythonなどの他の言語でも使用され、JavaScriptでの機能プログラミングでも使用されます。ただし、これについては後で説明します。
なぜデコレータが必要なのですか?
これらを使用すると、よりクリーンなコードを記述し、構成の概念に準拠し、かつて開発された機能を複数の関数やクラスに拡張できます。デコレータを使用することで、デバッグと保守が容易なコードを記述できます。
, , , . , .
2- , .
, .
Bit Node, TypeScript, React, Vue, Angular JS.

?
— . , -. -, . , .
?
.
— . , Java, , , . JavaScript , . , .
, . , , , .
, , .
//decorator function
const allArgsValid = function(fn) {
return function(...args) {
if (args.length != fn.length) {
throw new Error('Only submit required number of params');
}
const validArgs = args.filter(arg => Number.isInteger(arg));
if (validArgs.length < fn.length) {
throw new TypeError('Argument cannot be a non-integer');
}
return fn(...args);
}
}
//ordinary multiply function
let multiply = function(a,b){
return a*b;
}
//decorated multiply function that only accepts the required number of params and only integers
multiply = allArgsValid(multiply);
multiply(6, 8);
//48
multiply(6, 8, 7);
//Error: Only submit required number of params
multiply(3, null);
//TypeError: Argument cannot be a non-integer
multiply('',4);
//TypeError: Argument cannot be a non-integer
- allArgsValid
, . , -. - , . . : , .
multiply
, . - allArgsValid
, , , . multiply
. , .
//ordinary add function
let add = function(a,b){
return a+b;
}
//decorated add function that only accepts the required number of params and only integers
add = allArgsValid(add);
add(6, 8);
//14
add(3, null);
//TypeError: Argument cannot be a non-integer
add('',4);
//TypeError: Argument cannot be a non-integer
: , TC39
JavaScript . 2- .
JavaScript — . — , .
, — . ? .
, .
function log(fn) {
return function() {
console.log("Execution of " + fn.name);
console.time("fn");
let val = fn();
console.timeEnd("fn");
return val;
}
}
class Book {
constructor(name, ISBN) {
this.name = name;
this.ISBN = ISBN;
}
getBook() {
return `[${this.name}][${this.ISBN}]`;
}
}
let obj = new Book("HP", "1245-533552");
let getBook = log(obj.getBook);
console.log(getBook());
//TypeError: Cannot read property 'name' of undefined
, getBook
, - log
. obj.getBook
. this
, Book
. TypeError
.
, Book
getBook
.
function log(classObj, fn) {
return function() {
console.log("Execution of " + fn.name);
console.time("fn");
let val = fn.call(classObj);
console.timeEnd("fn");
return val;
}
}
class Book {
constructor(name, ISBN) {
this.name = name;
this.ISBN = ISBN;
}
getBook() {
return `[${this.name}][${this.ISBN}]`;
}
}
let obj = new Book("HP", "1245-533552");
let getBook = log(obj, obj.getBook);
console.log(getBook());
//[HP][1245-533552]
Book
- log
, obj.getBook
, this
.
, . , .
.
Babel. JSFiddle
— , . , : .
@. - log
:
@log
- . - , . target
, .
target
, . , . .
, Book
, .
function log(target) {
return function(...args) {
console.log("Constructor called");
return new target(...args);
};
}
@log
class Book {
constructor(name, ISBN) {
this.name = name;
this.ISBN = ISBN;
}
getBook() {
return `[${this.name}][${this.ISBN}]`;
}
}
let obj = new Book("HP", "1245-533552");
//Constructor Called
console.log(obj.getBook());
//HP][1245-533552]
, log
target
. log
, target
, Book
. target
target.prototype.property.
, -, :
function logWithParams(...params) {
return function(target) {
return function(...args) {
console.table(params);
return new target(...args);
}
}
}
@log
@logWithParams('param1', 'param2')
class Book {
//Class implementation as before
}
let obj = new Book("HP", "1245-533552");
//Constructor called
//Params will be consoled as a table
console.log(obj.getBook());
//[HP][1245-533552]
, , @
. , , .
, , . , :
descriptor. 4 :
configurable
— , , ;
enumerable
— , , ;
value
— . ;
writable
— , , .
Book
.
//readonly decorator function
function readOnly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
class Book {
//Implementation here
@readOnly
getBook() {
return `[${this.name}][${this.ISBN}]`;
}
}
let obj = new Book("HP", "1245-533552");
obj.getBook = "Hello";
console.log(obj.getBook());
//[HP][1245-533552]
- readOnly
, getBook
Book
. writable
false
. true
.
writable
, getBook
, , :
obj.getBook = "Hello";
console.log(obj.getBook);
//Hello
. TypeScript , JavaScript 3- .
-, , , . . , value
. initializer
. , initializer
. initializer
.
(undefined
), writable
.
. Book
.
function upperCase(target, name, descriptor) {
if (descriptor.initializer && descriptor.initializer()) {
let val = descriptor.initializer();
descriptor.initializer = function() {
return val.toUpperCase();
}
}
}
class Book {
@upperCase
id = "az092b";
getId() {
return `${this.id}`;
}
//other implementation here
}
let obj = new Book("HP", "1245-533552");
console.log(obj.getId());
//AZ092B
id . - upperCase
initializer
, , ( undefined
). , « » (. .: . truthy — , true
Boolean
), . getId
. , .
. , .
Angular
TypeScript Angular, Angular, @Component
, @NgModule
, @Injectable
, @Pipe
. . .
MobX
MobX 6- . @observable
, @computed
@action
. MobX , . :
« ES, . , ».
Core Decorators
JavaScript, . 0, , 3- .
, @readonly
, @time
, @deprecate
. .
Redux React
Redux React connect
, React Redux. connect
.
//Before decorator
class MyApp extends React.Component {
// ...define your main app here
}
export default connect(mapStateToProps, mapDispatchToProps)(MyApp);
//After decorator
@connect(mapStateToProps, mapDispatchToProps)
export default class MyApp extends React.Component {
// ...define your main app here
}
connect
, Redux . , 2- , , .
— , . .
, , !
"JavaScript Developer. Professional".
" telegram Node.js".