JSアクセサリー、1編で十分です
10584 ワード
詳細は、Github blogでご覧ください
クラスオブジェクトに関する定義と操作(classやextendsなど)がES 6に追加され、複数の異なるクラス間でいくつかの方法や動作を共有したり拡張したりするときに、それほど優雅ではありません.この時、私たちはこれらのことを完成させるためにもっと優雅な方法が必要です.
デコレーションとは
Pythonのアクセサリー
オブジェクト(OOP)向けのデザインモードではdecoratorを装飾モードと呼ぶ.OOPの装飾モードは継承と組合せで実現する必要があるが,PythonはOOPのdecoratorをサポートできるほか,直接文法階層からdecoratorをサポートする.
pythonに詳しいなら、きっと見慣れないでしょう.では、pythonのアクセサリーがどんなものか見てみましょう.
ここの@decoratorは私たちが言った装飾器です.上記のコードでは、デザイナを使用してターゲットメソッドを実行する前にテキストを1行印刷し、元のメソッドを変更しませんでした.コードは基本的に次のとおりです.
コードからも分かるように、デコレーションdecoratorはパラメータ、すなわちデコレーションされたターゲットメソッドを受信し、拡張されたコンテンツを処理した後、後で呼び出すためのメソッドを返し、元のメソッドオブジェクトへのアクセスを失う.ある装飾を適用すると,実際には被装飾方法の入口参照を変更し,装飾器が返す方法の入口点を再指向させ,元の関数の拡張,修正などの操作を実現する.
ES 7の装飾器
ES 7のdecoratorも同様にこの文法糖を参考にしているが、ES 5の
Object.defineProperty
この方法では、オブジェクトのプロパティを正確に追加または変更できます.値を割り当てて追加する通常の属性は、属性列挙中に表示される属性(for...inまたはObject.keysメソッド)を作成します.これらの値は変更したり削除したりできます.この方法では、これらの追加の詳細をデフォルト値から変更できます.デフォルトでは、
構文 戻り値:関数に渡されたオブジェクト.
ES 6では、Symbolタイプの特殊性のため、Symbolタイプの値でオブジェクトを作成するkeyは、従来の定義または変更とは異なり、
属性記述子
オブジェクトに現在存在するプロパティ記述子には、データ記述子とアクセス記述子の2つの主要な形式があります.データ記述子は、書き込み可能であるか、書き込み可能でないかのいずれかの値を有する属性である. アクセス記述子はgetter-setter関数ペアによって記述される属性である.
記述子はこの2つの形式の1つでなければならない.同時に両者ではいけない.
データ記述子とアクセス記述子には、次のオプションキー値があります.
configurable
属性記述子は、属性のconfigurableがtrueである場合にのみ変更され、対応するオブジェクトからも削除される.デフォルトはfalseです.
enumerable
Enumerableは、オブジェクトのプロパティがfor...InサイクルとObject.keys()に列挙される.
このプロパティは、プロパティのenumerableがtrueである場合にのみ、オブジェクトの列挙プロパティに表示されます.デフォルトはfalseです.データ記述子には、次のオプションキー値があります.
value
この属性に対応する値.有効なJavaScript値(数値、オブジェクト、関数など)でもかまいません.デフォルトはundefinedです.
writable
valueは、属性のwritableがtrueである場合にのみ、付与演算子によって変更されます.デフォルトはfalseです.
アクセス記述子には、次のオプションキー値があります.
get
属性にgetterを提供する方法で、getterがなければundefinedです.このメソッドの戻り値は、属性値として使用されます.デフォルトはundefinedです.
set
属性にsetterを提供する方法で、setterがなければundefinedです.このメソッドは、一意のパラメータを受け入れ、そのパラメータの新しい値を属性に割り当てます.デフォルトはundefinedです.
記述子がvalue,writable,get,setのいずれかのキーワードを持たない場合、データ記述子とみなされます.記述子に(valueまたはwritable)キーと(getまたはset)キーが同時にある場合、例外が発生します.
使用法
類の装飾
上記のコードでは、
基本的に、装飾器の振る舞いは以下の通りです.
すなわち,アクセラレータはクラスを処理する関数である.装飾関数の最初のパラメータは、装飾するターゲットクラスです.
1つのパラメータが足りないと思ったら、装飾器の外にもう1つの関数をカプセル化することができます.
上記のコードでは、アクセサリーtestableはパラメータを受け入れることができ、これはアクセサリーの動作を変更できることに等しい.
注意:クラスの動作に対する装飾器の変更は、実行時ではなくコードコンパイル時に発生します.これは、装飾器がコンパイル段階でコードを実行できることを意味します.すなわち,装飾器の本質はコンパイル時に実行される関数である.
前の例では、クラスに静的プロパティを追加し、インスタンスプロパティを追加する場合は、ターゲットクラスのprototypeオブジェクトで操作できます.
次は別の例です.
上のコードは,アクセラレータmixinsにより,FooオブジェクトのメソッドをMyClassのインスタンスに追加した.
方法の装飾
アクセサリーはクラスを飾るだけでなく、クラスの属性を飾ることもできます.
上のコードでは、アクセサリーreadonlyは「クラス」のnameメソッドを飾るために使用されます.
アクセサリー関数readonlyは全部で3つのパラメータを受け入れることができます.装飾器の最初のパラメータはクラスの原型オブジェクトであり、前例はPersonである.prototypeは、装飾器の本意は「装飾」クラスのインスタンスであるが、このときインスタンスはまだ生成されていないので、原型を装飾するしかない(これはクラスの装飾とは異なり、その場合targetパラメータはクラス自体を指す). の2番目のパラメータは、装飾する属性名 である.の3番目のパラメータは、属性の記述オブジェクト である.
また、上記のコードは、
関数メソッドの装飾
アクセラレータはクラスとクラスのメソッドにのみ使用でき、関数のアップグレードがあるため、関数には使用できません.
一方,関数を装飾する必要がある場合は,高次関数の形式で直接実行することができる.
core-decorators.js
core-decorators.jsはサードパーティモジュールであり、いくつかの一般的な装飾器を提供し、それによって装飾器をよりよく理解することができる.
@autobind
Autobind装飾器は、メソッド内のthisオブジェクトを元のオブジェクトにバインドします.
@readonly
readonly装飾器は属性や方法を書くことができません.
@override
overrideアクセラレータは、子クラスのメソッドが親クラスの同名メソッドを正しく上書きしているかどうかをチェックし、正しくない場合はエラーを報告します.
@deprecate(別名@deprecated)
deprecateまたはdeprecated装飾器は、コンソールに警告を表示し、この方法が廃止されることを示します.
@suppressWarnings
suppressWarnings装飾器はdeprecated装飾器によるconsoleを抑制する.warn()呼び出し.ただし、非同期コードが発行する呼び出しは除外されます.
シーンの操作
アクセサリーには注釈の役割があります
上記のコードから,Personクラスはテスト可能であり,nameメソッドは読み取り専用で枚挙にいとまがないことが一目で分かる.
Reactの接続
実際の開発では,ReactとReduxライブラリを組み合わせて使用する場合,次のように書くことがしばしば必要である.
アクセサリーがあれば、上のコードを書き換えることができます.に飾りを付ける
対照的に、後者の書き方は理解しやすいように見えます.
新しい機能のアラートまたは権限
メニューがクリックされるとイベントブロックが行われ、そのメニューに新しい機能が更新されるとポップアップ表示されます.
loading
Reactプロジェクトでは、バックグラウンドにデータを要求するときに、ページにloadingアニメーションを表示する必要がある場合があります.このとき、アクセサリーを使って、優雅に機能を実現することができます.
loadingWrap関数は次のとおりです.
質問:ここで皆さんは考えてみてください.もし私たちがデータを要求するたびにloadingが現れることを望んでいないならば、バックグラウンドの要求時間が300 msより大きい限り、loadingを表示することを要求します.ここはどのように変更する必要がありますか?
リファレンス Object.defineProperty() JavaScript Decorators: What They Are and When to Use Them ECMAScript 6入門 原文住所:https://segmentfault.com/a/1190000014495089
転入先https://www.cnblogs.com/datiangou/p/9759792.html
クラスオブジェクトに関する定義と操作(classやextendsなど)がES 6に追加され、複数の異なるクラス間でいくつかの方法や動作を共有したり拡張したりするときに、それほど優雅ではありません.この時、私たちはこれらのことを完成させるためにもっと優雅な方法が必要です.
デコレーションとは
Pythonのアクセサリー
オブジェクト(OOP)向けのデザインモードではdecoratorを装飾モードと呼ぶ.OOPの装飾モードは継承と組合せで実現する必要があるが,PythonはOOPのdecoratorをサポートできるほか,直接文法階層からdecoratorをサポートする.
pythonに詳しいなら、きっと見慣れないでしょう.では、pythonのアクセサリーがどんなものか見てみましょう.
def decorator(f):
print "my decorator"
return f
@decorator
def myfunc():
print "my function"
myfunc()
# my decorator
# my function
ここの@decoratorは私たちが言った装飾器です.上記のコードでは、デザイナを使用してターゲットメソッドを実行する前にテキストを1行印刷し、元のメソッドを変更しませんでした.コードは基本的に次のとおりです.
def decorator(f):
def wrapper():
print "my decorator"
return f()
return wrapper
def myfunc():
print "my function"
myfunc = decorator(myfuc)
コードからも分かるように、デコレーションdecoratorはパラメータ、すなわちデコレーションされたターゲットメソッドを受信し、拡張されたコンテンツを処理した後、後で呼び出すためのメソッドを返し、元のメソッドオブジェクトへのアクセスを失う.ある装飾を適用すると,実際には被装飾方法の入口参照を変更し,装飾器が返す方法の入口点を再指向させ,元の関数の拡張,修正などの操作を実現する.
ES 7の装飾器
ES 7のdecoratorも同様にこの文法糖を参考にしているが、ES 5の
Object.defineProperty
法に依存している.Object.defineProperty
Object.defineProperty()
メソッドは、オブジェクトに直接新しいプロパティを定義するか、オブジェクトの既存のプロパティを変更してオブジェクトに戻ります.この方法では、オブジェクトのプロパティを正確に追加または変更できます.値を割り当てて追加する通常の属性は、属性列挙中に表示される属性(for...inまたはObject.keysメソッド)を作成します.これらの値は変更したり削除したりできます.この方法では、これらの追加の詳細をデフォルト値から変更できます.デフォルトでは、
Object.defineProperty()
を使用して追加された属性値は可変ではありません.構文
Object.defineProperty(obj, prop, descriptor)
obj
:プロパティを定義するオブジェクト.prop
:定義または変更する属性の名前.descriptor
:定義または変更される属性記述子.ES 6では、Symbolタイプの特殊性のため、Symbolタイプの値でオブジェクトを作成するkeyは、従来の定義または変更とは異なり、
Object.defineProperty
は、keyがSymbolの属性であることを定義する方法の1つである.属性記述子
オブジェクトに現在存在するプロパティ記述子には、データ記述子とアクセス記述子の2つの主要な形式があります.
記述子はこの2つの形式の1つでなければならない.同時に両者ではいけない.
データ記述子とアクセス記述子には、次のオプションキー値があります.
configurable
属性記述子は、属性のconfigurableがtrueである場合にのみ変更され、対応するオブジェクトからも削除される.デフォルトはfalseです.
enumerable
Enumerableは、オブジェクトのプロパティがfor...InサイクルとObject.keys()に列挙される.
このプロパティは、プロパティのenumerableがtrueである場合にのみ、オブジェクトの列挙プロパティに表示されます.デフォルトはfalseです.データ記述子には、次のオプションキー値があります.
value
この属性に対応する値.有効なJavaScript値(数値、オブジェクト、関数など)でもかまいません.デフォルトはundefinedです.
writable
valueは、属性のwritableがtrueである場合にのみ、付与演算子によって変更されます.デフォルトはfalseです.
アクセス記述子には、次のオプションキー値があります.
get
属性にgetterを提供する方法で、getterがなければundefinedです.このメソッドの戻り値は、属性値として使用されます.デフォルトはundefinedです.
set
属性にsetterを提供する方法で、setterがなければundefinedです.このメソッドは、一意のパラメータを受け入れ、そのパラメータの新しい値を属性に割り当てます.デフォルトはundefinedです.
記述子がvalue,writable,get,setのいずれかのキーワードを持たない場合、データ記述子とみなされます.記述子に(valueまたはwritable)キーと(getまたはset)キーが同時にある場合、例外が発生します.
使用法
類の装飾
@testable
class MyTestableClass {
// ...
}
function testable(target) {
target.isTestable = true;
}
MyTestableClass.isTestable // true
上記のコードでは、
@testable
が装飾器です.MyTestableClassというクラスの動作を修正し、静的属性isTestableを追加しました.testable関数のパラメータtargetはMyTestableClassクラスそのものである.基本的に、装飾器の振る舞いは以下の通りです.
@decorator
class A {}
//
class A {}
A = decorator(A) || A;
すなわち,アクセラレータはクラスを処理する関数である.装飾関数の最初のパラメータは、装飾するターゲットクラスです.
1つのパラメータが足りないと思ったら、装飾器の外にもう1つの関数をカプセル化することができます.
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
上記のコードでは、アクセサリーtestableはパラメータを受け入れることができ、これはアクセサリーの動作を変更できることに等しい.
注意:クラスの動作に対する装飾器の変更は、実行時ではなくコードコンパイル時に発生します.これは、装飾器がコンパイル段階でコードを実行できることを意味します.すなわち,装飾器の本質はコンパイル時に実行される関数である.
前の例では、クラスに静的プロパティを追加し、インスタンスプロパティを追加する場合は、ターゲットクラスのprototypeオブジェクトで操作できます.
次は別の例です.
// mixins.js
export function mixins(...list) {
return function (target) {
Object.assign(target.prototype, ...list)
}
}
// main.js
import { mixins } from './mixins'
const Foo = {
foo() { console.log('foo') }
};
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo() // 'foo'
上のコードは,アクセラレータmixinsにより,FooオブジェクトのメソッドをMyClassのインスタンスに追加した.
方法の装飾
アクセサリーはクラスを飾るだけでなく、クラスの属性を飾ることもできます.
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}
上のコードでは、アクセサリーreadonlyは「クラス」のnameメソッドを飾るために使用されます.
アクセサリー関数readonlyは全部で3つのパラメータを受け入れることができます.
function readonly(target, name, descriptor){
// descriptor
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
descriptor.writable = false;
return descriptor;
}
readonly(Person.prototype, 'name', descriptor);
//
Object.defineProperty(Person.prototype, 'name', descriptor);
また、上記のコードは、
(readonly
)が属性の (descriptor)
を変更し、変更された記述オブジェクトが属性を定義するために使用されることを示している.関数メソッドの装飾
アクセラレータはクラスとクラスのメソッドにのみ使用でき、関数のアップグレードがあるため、関数には使用できません.
一方,関数を装飾する必要がある場合は,高次関数の形式で直接実行することができる.
function doSomething(name) {
console.log('Hello, ' + name);
}
function loggingDecorator(wrapped) {
return function() {
console.log('Starting');
const result = wrapped.apply(this, arguments);
console.log('Finished');
return result;
}
}
const wrapped = loggingDecorator(doSomething);
core-decorators.js
core-decorators.jsはサードパーティモジュールであり、いくつかの一般的な装飾器を提供し、それによって装飾器をよりよく理解することができる.
@autobind
Autobind装飾器は、メソッド内のthisオブジェクトを元のオブジェクトにバインドします.
@readonly
readonly装飾器は属性や方法を書くことができません.
@override
overrideアクセラレータは、子クラスのメソッドが親クラスの同名メソッドを正しく上書きしているかどうかをチェックし、正しくない場合はエラーを報告します.
import { override } from 'core-decorators';
class Parent {
speak(first, second) {}
}
class Child extends Parent {
@override
speak() {}
// SyntaxError: Child#speak() does not properly override Parent#speak(first, second)
}
// or
class Child extends Parent {
@override
speaks() {}
// SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain.
//
// Did you mean "speak"?
}
@deprecate(別名@deprecated)
deprecateまたはdeprecated装飾器は、コンソールに警告を表示し、この方法が廃止されることを示します.
import { deprecate } from 'core-decorators';
class Person {
@deprecate
facepalm() {}
@deprecate('We stopped facepalming')
facepalmHard() {}
@deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm' })
facepalmHarder() {}
}
let person = new Person();
person.facepalm();
// DEPRECATION Person#facepalm: This function will be removed in future versions.
person.facepalmHard();
// DEPRECATION Person#facepalmHard: We stopped facepalming
person.facepalmHarder();
// DEPRECATION Person#facepalmHarder: We stopped facepalming
//
// See http://knowyourmeme.com/memes/facepalm for more details.
//
@suppressWarnings
suppressWarnings装飾器はdeprecated装飾器によるconsoleを抑制する.warn()呼び出し.ただし、非同期コードが発行する呼び出しは除外されます.
シーンの操作
アクセサリーには注釈の役割があります
@testable
class Person {
@readonly
@nonenumerable
name() { return `${this.first} ${this.last}` }
}
上記のコードから,Personクラスはテスト可能であり,nameメソッドは読み取り専用で枚挙にいとまがないことが一目で分かる.
Reactの接続
実際の開発では,ReactとReduxライブラリを組み合わせて使用する場合,次のように書くことがしばしば必要である.
class MyReactComponent extends React.Component {}
export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);
アクセサリーがあれば、上のコードを書き換えることができます.に飾りを付ける
@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {}
対照的に、後者の書き方は理解しやすいように見えます.
新しい機能のアラートまたは権限
メニューがクリックされるとイベントブロックが行われ、そのメニューに新しい機能が更新されるとポップアップ表示されます.
/**
* @description , ,
* @param code code
* @returns {function(*, *, *)}
*/
const checkRecommandFunc = (code) => (target, property, descriptor) => {
let desF = descriptor.value;
descriptor.value = function (...args) {
let recommandFuncModalData = SYSTEM.recommandFuncCodeMap[code];
if (recommandFuncModalData && recommandFuncModalData.id) {
setTimeout(() => {
this.props.dispatch({type: 'global/setRecommandFuncModalData', recommandFuncModalData});
}, 1000);
}
desF.apply(this, args);
};
return descriptor;
};
loading
Reactプロジェクトでは、バックグラウンドにデータを要求するときに、ページにloadingアニメーションを表示する必要がある場合があります.このとき、アクセサリーを使って、優雅に機能を実現することができます.
@autobind
@loadingWrap(true)
async handleSelect(params) {
await this.props.dispatch({
type: 'product_list/setQuerypParams',
querypParams: params
});
}
loadingWrap関数は次のとおりです.
export function loadingWrap(needHide) {
const defaultLoading = (
<div className="toast-loading">
<Loading className="loading-icon"/>
<div> ...</div>
</div>
);
return function (target, property, descriptor) {
const raw = descriptor.value;
descriptor.value = function (...args) {
Toast.info(text || defaultLoading, 0, null, true);
const res = raw.apply(this, args);
if (needHide) {
if (get('finally')(res)) {
res.finally(() => {
Toast.hide();
});
} else {
Toast.hide();
}
}
};
return descriptor;
};
}
質問:ここで皆さんは考えてみてください.もし私たちがデータを要求するたびにloadingが現れることを望んでいないならば、バックグラウンドの要求時間が300 msより大きい限り、loadingを表示することを要求します.ここはどのように変更する必要がありますか?
リファレンス
転入先https://www.cnblogs.com/datiangou/p/9759792.html