ブックついに締め切り、今日少しの空き時間、我々はJavaScript言語のスタイルについての記事を捧げるよう、主人公は、宣言型の関数です。
柔軟なJavaScriptとマルチパラダイム
私はもはや奇妙なフロントエンドの開発者の多くは、「機能性」という概念を信じて:私たちは、JavaScriptがマルチモード(マルチパラダイム)言語の非常に柔軟な統合であることを知って、この記事では、命令型言語と宣言型スタイルでJavaScriptを表示されませんスタイルスイッチは、目的は、読者が自分の特性の二つの異なる言語パターンを理解させるため、日々の開発に合理的な選択を達成するため、JavaScriptをパワーを最大限にすることです。
利便性のために、私たちは、開始するために、典型的なイベントから機能的なスタイルの変身を完了するためのステップバイステップのシステムをサブスクライブ公開します。イベントパブリッシュ・サブスクライブ「高凝集、低カップリング」のデザインを実現するために、イベント・ドリブン(イベント駆動型)の考え方に付着し、システム、いわゆるオブザーバ・モード(パブ/サブモード)。:読者は、このモードのために知られていない場合、私はあなたが元の記事を読むことをお勧めします自分の購読公開システムのイベントソースNode.jsのイベントを作成するためのメカニズムを探ります。この記事では、ES次の文法、実装モードを公開コマンド形式のイベントに基づいて、Node.jsのソースで始まるシステムをサブスクライブパブリッシュ達成するためのイベントを分析し、そして。このコンテンツの基礎のために、紙はあまり拡大しないであろう。
典型的な持つEventEmitterと変革の挑戦
理解イベントは、パブリッシュ・サブスクライブシステム導入の考え方を、我々は、基本的な実装のシンプルかつ典型的に見て:
class EventManager {
construct (eventMap = new Map()) {
this.eventMap = eventMap;
}
addEventListener (event, handler) {
if (this.eventMap.has(event)) {
this.eventMap.set(event, this.eventMap.get(event).concat([handler]));
} else {
this.eventMap.set(event, [handler]);
}
}
dispatchEvent (event) {
if (this.eventMap.has(event)) {
const handlers = this.eventMap.get(event);
for (const i in handlers) {
handlers[i]();
}
}
}
}
上記のコードを実装EventManagerクラス:私たちは地図タイプeventMapを維持し、さまざまなイベント(ハンドラ)のために、すべてのコールバック関数の保守を行います。
- addEventListenerメソッドは、イベントコールバック関数が格納されている指定されました。
- dispatchEventメソッド指定されたトリガ・イベント、一つ一つは、そのコールバック関数を実装します。
消費者レベルで:
const em = new EventManager();
em.addEventListner('hello', function() {
console.log('hi');
});
em.dispatchEvent('hello'); // hi
これらは、よりよく理解されています。ここに私たちの挑戦は次のとおりです。
- 20以上のマルチラインコマンドのタイプコードは、2行目は7つの宣言式コードに変換されます。
- {...}は、もはや使用されない、と決意条件であれば、
- 副作用を回避するために、純粋な関数の実装、。
- すなわち、一つだけのパラメータ方程式の機能、メンバーシップ関数を使用しています。
- 関数は、(Composablesこと)の組み合わせであってもよいことを理解します。
- コードの実装は、クリーンでエレガントな、低カップリングする必要があります。
ステップ1:機能を使用して置換クラス
上記の課題の内容に基づいて、addEventListenerをとのdispatchEventは、もはや法のEventManagerクラスとして表示されず、2つの別々の機能となって、変数としてeventMap:
const eventMap = new Map();
function addEventListener (event, handler) {
if (eventMap.has(event)) {
eventMap.set(event, eventMap.get(event).concat([handler]));
} else {
eventMap.set(event, [handler]);
}
}
function dispatchEvent (event) {
if (eventMap.has(event)) {
const handlers = this.eventMap.get(event);
for (const i in handlers) {
handlers[i]();
}
}
}
モジュラーニーズでは、我々は2つの機能をエクスポートすることができます。
export default {addEventListener, dispatchEvent};
同時に、輸入の使用を注意し、輸入に依存導入シングルトン(シングルトン)です。
import * as EM from './event-manager.js';
EM.dispatchEvent('event');
モジュールは完全に予想に沿ったもので、別のファイルに導入されたときに、内部変数がeventMapが共有され、単一の場合のシナリオであるため。
ステップ2:矢印機能
より機能的な「味」に沿ったもので、伝統的な関数式から機能別の矢印:
const eventMap = new Map();
const addEventListener = (event, handler) => {
if (eventMap.has(event)) {
eventMap.set(event, eventMap.get(event).concat([handler]));
} else {
eventMap.set(event, [handler]);
}
}
const dispatchEvent = event => {
if (eventMap.has(event)) {
const handlers = eventMap.get(event);
for (const i in handlers) {
handlers[i]();
}
}
}
ここでは、矢印のこの結合機能に特別な注意を払う必要があります。
ステップ3:副作用の除去、戻り値増加
、上記のプロセスは異なる純粋な関数特性を確保するために、我々はeventMapを変更するために行くことはできませんが、変更を行うためにパラメータのaddEventListenerとのdispatchEventメソッドながら、新しいマップ型の変数を返す必要があり、「1つの状態」eventMapを追加します新しいeventMapを推定するために:
const addEventListener = (event, handler, eventMap) => {
if (eventMap.has(event)) {
return new Map(eventMap).set(event, eventMap.get(event).concat([handler]));
} else {
return new Map(eventMap).set(event, [handler]);
}
}
const dispatchEvent = (event, eventMap) => {
if (eventMap.has(event)) {
const handlers = eventMap.get(event);
for (const i in handlers) {
handlers[i]();
}
}
return eventMap;
}
はい、このプロセスは減速機能に非常に似ており、Reduxの。純粋の機能を維持するために、機能的な概念は非常に重要なポイントです。
ステップ4:ループの削除スタイルの声明
次に、forループのforEachの代わりに使用します。
const addEventListener = (event, handler, eventMap) => {
if (eventMap.has(event)) {
return new Map(eventMap).set(event, eventMap.get(event).concat([handler]));
} else {
return new Map(eventMap).set(event, [handler]);
}
}
const dispatchEvent = (event, eventMap) => {
if (eventMap.has(event)) {
eventMap.get(event).forEach(a => a());
}
return eventMap;
}
ステップ5:バイナリ演算子
私たちは、コードより機能的なスタイルをしなければならない||使用&&と:
const addEventListener = (event, handler, eventMap) => {
if (eventMap.has(event)) {
return new Map(eventMap).set(event, eventMap.get(event).concat([handler]));
} else {
return new Map(eventMap).set(event, [handler]);
}
}
const dispatchEvent = (event, eventMap) => {
return (
eventMap.has(event) &&
eventMap.get(event).forEach(a => a())
) || event;
}
私たちは、return文の発現に特に注意を払う必要があります:
return (
eventMap.has(event) &&
eventMap.get(event).forEach(a => a())
) || event;
STEP6:代わりにあれば三項演算子を使用します
三項演算子より直感的な説明:
const addEventListener = (event, handler, eventMap) => {
return eventMap.has(event) ?
new Map(eventMap).set(event, eventMap.get(event).concat([handler])) :
new Map(eventMap).set(event, [handler]);
}
const dispatchEvent = (event, eventMap) => {
return (
eventMap.has(event) &&
eventMap.get(event).forEach(a => a())
) || event;
}
STEP7:中括弧{...}を除去
矢印関数は常に式の値を返しますので、私たちはいずれかを必要としません{...}:
const addEventListener = (event, handler, eventMap) =>
eventMap.has(event) ?
new Map(eventMap).set(event, eventMap.get(event).concat([handler])) :
new Map(eventMap).set(event, [handler]);
const dispatchEvent = (event, eventMap) =>
(eventMap.has(event) && eventMap.get(event).forEach(a => a())) || event;
ステップ8:カリー化の完了
最後のステップは、カリー化操作を実現することである、私達の機能に固有のアイデアは、1元(一つのパラメータのみを受け入れる)高階関数(高階関数)を使用すること、実現になります。理解を簡単にするために、読者はパラメータであると考えられる(A、B、C)は、単に=> B => Cの方法を以下のようになります。
const addEventListener = handler => event => eventMap =>
eventMap.has(event) ?
new Map(eventMap).set(event, eventMap.get(event).concat([handler])) :
new Map(eventMap).set(event, [handler]);
const dispatchEvent = event => eventMap =>
(eventMap.has(event) && eventMap.get(event).forEach (a => a())) || event;
読者は、これは難しいだろう理解している場合、ここに着手しないように、カリー化の知識を追加することをお勧めします。
もちろん、このプロセスは、パラメータの順序を考慮する必要があります。ここでは、例によって消化する必要があります。
使用のカリー化:
const log = x => console.log (x) || x;
const myEventMap1 = addEventListener(() => log('hi'))('hello')(new Map());
dispatchEvent('hello')(myEventMap1); // hi
部分的に使用:
const log = x => console.log (x) || x;
let myEventMap2 = new Map();
const onHello = handler => myEventMap2 = addEventListener(handler)('hello')(myEventMap2);
const hello = () => dispatchEvent('hello')(myEventMap2);
onHello(() => log('hi'));
hello(); // hi
pythonに精通している読者は、部分の概念をより良く理解するかもしれません。簡単に言えば、一部の機能アプリケーションは、次のように理解することができます。
呼び出すために必要なすべてのパラメータを持って来るために、実行された機能。しかし、時には、パラメータは関数が呼び出される前に事前に通知することができます。この場合、一個の以上のパラメータの関数は、関数が少ないパラメータを呼び出すことができるように、事前に過ごすことができます。
コールバックパラメータは、イベントがトリガハローときことを意味onHello機能について。ここでmyEventMap2とハローイベントは、事前に定義されています。ハロー機能のために同じように、それだけでハローイベントを開始する必要があります。
組み合わせで:
const log = x => console.log (x) || x;
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
const addEventListeners = compose(
log,
addEventListener(() => log('hey'))('hello'),
addEventListener(() => log('hi'))('hello')
);
const myEventMap3 = addEventListeners(new Map()); // myEventMap3
dispatchEvent('hello')(myEventMap3); // hi hey
あなたはCOMPOSE方法に特に注意を払う必要があります。Reduxの、Reduxの読者に精通あなたは、ソースコード、構成する確かには見知らぬ人を読んでいる場合。我々は、ハローイベントの組み合わせだけでなく、ログの組み合わせの機能のための道具2つのコールバックを構成します。
コン方法と異なる実装についてのミステリー、注意を払う著者ください:ルーカスHC、私は記事を書きます、具体的説明と分析Reduxのは少し不明瞭コンを達成する理由が、実装のより直感的な分析。
概要
機能的なアイデアは、初心者のために非常に友好的ではないかもしれません。自分の好みや親しみやすさに応じて読者は、8つのステップで、読むのをやめて自由に感じます。同時に、議論を歓迎しました。
この記事では、マーティン・ノヴァークのMEE 新しい記事、偉大な神の論文へようこそ。
時間を広告:
あなたはフロントエンドの開発をしている場合は、特にテクノロジー・スタックに興味を持っているが反応:私の新しい本、あなたが見たい、おそらくコンテンツを。注意ルーカスHC、本の出版は、書籍の活動を送信する必要があります。
コーディングハッピー!
PS:オン のGithubリポジトリ と 、ほぼQリンクは知っている コミュニケーションのすべてのフォームを歓迎します。