Moriを使用した不変のデータと機能的JavaScript
キーテイクアウト
- moriは、Clojureの永続的なデータ構造を活用し、JavaScript開発者にコードのシンプルさと信頼性を高める不変のデータオプションを提供します。 森の使用は、不変性を強制することにより、JavaScriptの機能的なプログラミングパラダイムを促進し、意図しない副作用を防ぎ、アプリケーションライフサイクル全体でデータの一貫性を確保します。
- ライブラリは、データがデータ構造で個別に動作するデータを処理するための異なるアプローチを促進し、JavaScriptの典型的なオブジェクト指向の方法とは対照的であり、よりクリーンで予測可能なコードを可能にします。 森の構造共有手法により、可能な限り既存のデータ構造を再利用することにより、データ操作を効率的にし、アプリケーションのパフォーマンスの改善につながる可能性があります。
- 機能を備えたPixelエディターのような例を通じて、MORIは不変のデータ構造の実用的なアプリケーションを実証し、開発者に洗練された堅牢な機能を構築するツールを提供します。
- この記事は、クレイグ・ビルナーとエイドリアン・サンドゥによって査読されました。 SetePointコンテンツを最高にするためにSitePointのピアレビュアーのすべてに感謝します!
- 機能的プログラミングと不変のデータは、コードをよりシンプルで推論しやすくする方法を見つけようとする多くのJavaScript開発者にとって現在の焦点です。 JavaScriptは常にいくつかの機能的なプログラミング手法をサポートしてきましたが、ここ数年で本当に人気があり、伝統的に不変のデータに対するネイティブサポートもありませんでした。 JavaScriptはまだ両方について多くのことを学んでおり、これらの手法をすでに試してテストした言語からの最高のアイデアはあります。 プログラミングの世界の別のコーナーでは、Clojureは、特にデータ構造が関係する場合、真のシンプルさに専念する機能的なプログラミング言語です。 Moriは、JavaScriptから直接Clojureの永続的なデータ構造を使用できるライブラリです。
永続的なデータとは? clojureは、変更できない
永続的な値と、変異間の時間的な寿命を持つ一時的な
値を区別します。永続的なデータ構造を変更しようとすると、変更が適用された状態で新しい構造を返すことで、基礎となるデータの変異を避けます。この区別が理論的なプログラミング言語でどのように見えるかを見るのに役立つかもしれません。
<span>// transient list </span>a <span>= [1, 2, 3]; </span>b <span>= a.push(4); </span><span>// a = [1, 2, 3, 4] </span><span>// b = [1, 2, 3, 4] </span> <span>// persistent list </span>c <span>= #[1, 2, 3] </span>d <span>= c.push(4); </span><span>// c = #[1, 2, 3] </span><span>// d = #[1, 2, 3, 4] </span>
値を押したときに一時的なリストが変異したことがわかります。 AとBの両方が同じ可変値を指します。対照的に、永続的なリストでプッシュを呼び出すと新しい値が返され、CとDが離散リストとは異なることがわかります。
これらの永続的なデータ構造を変異させることはできません。つまり、値を参照したら、変更されないという保証もあります。これらの保証は、通常、より安全でシンプルなコードを書くのに役立ちます。たとえば、引数として永続的なデータ構造を採用する関数は、それらを変化させることができないため、関数が意味のある変更を伝達したい場合、返品値から来る必要があります。これは、参照的に透明で純粋な機能を書くことにつながります。これは、テストと最適化が簡単です。 より簡単に言えば、不変のデータにより、より機能的なコードを書くことが強制されます。森とは何ですか?
Moriは、ClojureScriptコンパイラを使用して、Clojureの標準ライブラリのデータ構造の実装をJavaScriptにコンパイルします。コンパイラは最適化されたコードを発します。つまり、追加の考慮がなくても、JavaScriptのコンパイルされたClojureと通信するのは簡単ではありません。森は、追加の考慮事項の層です。
Clojureと同様に、Moriの機能は、操作しているデータ構造から分離されており、JavaScriptのオブジェクト指向の傾向とは対照的です。この違いがコードを書く方向が変わることがわかります。
moriは、可能な限り多くの元の構造を共有することにより、構造共有を使用してデータに効率的な変更を加えます。これにより、永続的なデータ構造は、通常の一時的なデータ構造とほぼ同じように効率的になります。これらの概念の実装については、このビデオではさらに詳細に説明しています。
なぜそれが役立つのですか?最初に、継承したJavaScriptコードベースのバグを追跡しようとしていると想像してみましょう。私たちは、フェローシップの間違った価値になった理由を理解しようとしてコードを読んでいます。
<span>// standard library </span><span>Array(1, 2, 3).map(x => x * 2); </span><span>// => [2, 4, 6] </span> <span>// mori </span><span>map(x => x * 2, vector(1, 2, 3)) </span><span>// => [2, 4, 6] </span>
コンソールにログに記録されたフェローシップの価値は何ですか?
コードを実行したり、deleteperson()の定義を読んでも、知る方法はありません。空の配列である可能性があります。 3つの新しいプロパティがあります。 2番目の要素が削除されたアレイであることを願っていますが、可変データ構造に合格したため、保証はありません。 さらに悪いことに、この機能は参照を保持し、将来非同期に変異させる可能性があります。ここからのフェローシップへのすべての言及は、予測不可能な価値で作業する予定です。
これを森の代替案と比較します。<span>// transient list </span>a <span>= [1, 2, 3]; </span>b <span>= a.push(4); </span><span>// a = [1, 2, 3, 4] </span><span>// b = [1, 2, 3, 4] </span> <span>// persistent list </span>c <span>= #[1, 2, 3] </span>d <span>= c.push(4); </span><span>// c = #[1, 2, 3] </span><span>// d = #[1, 2, 3, 4] </span>
deleteperson()の実装に関係なく、単に変異できないという保証があるという理由だけで、元のベクトルが記録されることを知っています。関数を便利にしたい場合は、指定されたアイテムが削除された状態で新しいベクトルを返す必要があります。
不変のデータで機能する機能を介したフローを理解するのは簡単です。なぜなら、それらの唯一の効果は、明確な不変の価値を導き出して返すことであることを知っているからです。
可変データで動作する関数は常に値を返すわけではなく、入力を変異させることができ、時にはプログラマに任せて反対側で再び値を拾うことができます。
実際には
Codepenをフォローしているか、MORIと次のHTMLを使用してES2015環境で作業していると仮定します。
セットアップ&ユーティリティ
これは主に文体的な好みです。 Moriオブジェクト(例:mori.list())に直接アクセスすることで、MORIの関数を使用することもできます。
最初に行うことは、永続的なデータ構造を表示するためのヘルパー関数をセットアップすることです。森の内部表現はコンソールではあまり意味がないため、tojs()関数を使用して理解可能な形式に変換します。<span>// standard library </span><span>Array(1, 2, 3).map(x => x * 2); </span><span>// => [2, 4, 6] </span> <span>// mori </span><span>map(x => x * 2, vector(1, 2, 3)) </span><span>// => [2, 4, 6] </span>
森のデータ構造を検査する必要がある場合、console.log()の代替としてこの関数を使用できます。
>次に、いくつかの構成値とユーティリティ関数を設定します。
<span>const fellowship = [ </span> <span>{ </span> <span>title: 'Mori', </span> <span>race: 'Hobbit' </span> <span>}, </span> <span>{ </span> <span>title: 'Poppin', </span> <span>race: 'Hobbit' </span> <span>} </span><span>]; </span> <span>deletePerson(fellowship, 1); </span><span>console.log(fellowship); </span>
データの構造
to2d()関数を使用して、キャンバス上のすべてのピクセルを表す一連の座標を作成します。
<span>import <span>{ vector, hashMap }</span> from 'mori'; </span> <span>const fellowship = vector( </span> <span>hashMap( </span> <span>"name", "Mori", </span> <span>"race", "Hobbit" </span> <span>), </span> <span>hashMap( </span> <span>"name", "Poppin", </span> <span>"race", "Hobbit" </span> <span>) </span><span>) </span> <span>const newFellowship = deletePerson(fellowship, 1); </span><span>console.log(fellowship); </span>
座標の構造を視覚化するのに役立つかもしれません。
<span><span><span><div</span>></span> </span> <span><span><span><h3</span>></span>Mori Painter<span><span></h3</span>></span> </span><span><span><span></div</span>></span> </span><span><span><span><div</span> id<span>="container"</span>></span> </span> <span><span><span><canvas</span> id<span>='canvas'</span>></span><span><span></canvas</span>></span> </span><span><span><span></div</span>></span> </span><span><span><span><div</span>></span> </span> <span><span><span><button</span> id<span>='undo'</span>></span>↶<span><span></button</span>></span> </span><span><span><span></div</span>></span> </span>
各座標と並んで、色の値も保存する必要があります。
<span>// transient list </span>a <span>= [1, 2, 3]; </span>b <span>= a.push(4); </span><span>// a = [1, 2, 3, 4] </span><span>// b = [1, 2, 3, 4] </span> <span>// persistent list </span>c <span>= #[1, 2, 3] </span>d <span>= c.push(4); </span><span>// c = #[1, 2, 3] </span><span>// d = #[1, 2, 3, 4] </span>
repeat()関数を使用して、「#FFF」文字列の無限シーケンスを作成しています。 MORIシーケンスはレイジー評価をサポートしているため、このメモリを埋めてブラウザをクラッシュさせることを心配する必要はありません。後で尋ねると、シーケンス内のアイテムの値を計算します。
最後に、ハッシュマップの形で座標と色を組み合わせたい。
<span>// standard library </span><span>Array(1, 2, 3).map(x => x * 2); </span><span>// => [2, 4, 6] </span> <span>// mori </span><span>map(x => x * 2, vector(1, 2, 3)) </span><span>// => [2, 4, 6] </span>
ZIPMAP()関数を使用してハッシュマップを作成し、キーとしての座標と色を値として使用しています。繰り返しますが、データの構造を視覚化するのに役立つかもしれません。
<span>const fellowship = [ </span> <span>{ </span> <span>title: 'Mori', </span> <span>race: 'Hobbit' </span> <span>}, </span> <span>{ </span> <span>title: 'Poppin', </span> <span>race: 'Hobbit' </span> <span>} </span><span>]; </span> <span>deletePerson(fellowship, 1); </span><span>console.log(fellowship); </span>
ピクセル
を描画しますピクセルの色を変更するには、ハッシュマップの座標の1つを新しい文字列に関連付けます。単一のピクセルを色付けする純粋な関数を書きましょう。
xおよびy座標を使用して、キーとして使用できる座標ベクトルを作成し、そのキーを新しい色に関連付けるためにassoc()を使用します。データ構造は永続的であるため、Assoc()関数は古いマップを変異させるのではなく、
new<span>import <span>{ vector, hashMap }</span> from 'mori'; </span> <span>const fellowship = vector( </span> <span>hashMap( </span> <span>"name", "Mori", </span> <span>"race", "Hobbit" </span> <span>), </span> <span>hashMap( </span> <span>"name", "Poppin", </span> <span>"race", "Hobbit" </span> <span>) </span><span>) </span> <span>const newFellowship = deletePerson(fellowship, 1); </span><span>console.log(fellowship); </span>
絵を描く これで、キャンバスにシンプルな画像を描くために必要なものはすべて揃っています。ピクセルに対して座標のハッシュマップを取得し、それらをrenderingContext2Dに描画する関数を作成しましょう。
ここで何が起こっているのかを理解しましょう。
各()を使用して、ピクセルのハッシュマップを反復します。各キーと値(シーケンスとして一緒に)をpaspとしてコールバック関数に渡します。次に、IntherArray()関数を使用して、それを破壊できる配列に変換するため、必要な値を選択できます。
<span><span><span><div</span>></span> </span> <span><span><span><h3</span>></span>Mori Painter<span><span></h3</span>></span> </span><span><span><span></div</span>></span> </span><span><span><span><div</span> id<span>="container"</span>></span> </span> <span><span><span><canvas</span> id<span>='canvas'</span>></span><span><span></canvas</span>></span> </span><span><span><span></div</span>></span> </span><span><span><span><div</span>></span> </span> <span><span><span><button</span> id<span>='undo'</span>></span>↶<span><span></button</span>></span> </span><span><span><span></div</span>></span> </span>
一緒に配線する
ここで、これらすべての部品をまとめて動作させるためだけに、少し配管する必要があります。
<span>const { </span> list<span>, vector, peek, pop, conj, map, assoc, zipmap, </span> range<span>, repeat, each, count, intoArray, toJs </span><span>} = mori; </span>
キャンバスを手に入れ、それを使用して画像をレンダリングするためのコンテキストを作成します。また、次元を反映するために適切にサイズを変更します。
最後に、ペイントメソッドによって描画されるピクセルでコンテキストを渡します。運が良ければ、キャンバスは白いピクセルとしてレンダリングする必要があります。最もエキサイティングな公開ではありませんが、私たちは近づいています。<span>const log = (<span>...args</span>) => { </span> <span>console.log(...args.map(toJs)) </span><span>}; </span>
インタラクティブ
クリックイベントを聞き、それらを使用して特定のピクセルの色を変更して、以前のdraw()関数を変更したい。
<span>// the dimensions of the canvas </span><span>const [height, width] = [20, 20]; </span> <span>// the size of each canvas pixel </span><span>const pixelSize = 10; </span> <span>// converts an integer to a 2d coordinate vector </span><span>const to2D = (i) => vector( </span> i <span>% width, </span> <span>Math.floor(i / width) </span><span>); </span>
この時点で、黒いピクセルをキャンバスに描くことができ、各フレームは前のフレームに基づいて、複合画像を作成します。
トラッキングフレーム元に戻すには、各歴史的な改訂版をピクセルハッシュマップに保存して、将来再び取得できるようにする必要があります。
<span>// transient list </span>a <span>= [1, 2, 3]; </span>b <span>= a.push(4); </span><span>// a = [1, 2, 3, 4] </span><span>// b = [1, 2, 3, 4] </span> <span>// persistent list </span>c <span>= #[1, 2, 3] </span>d <span>= c.push(4); </span><span>// c = #[1, 2, 3] </span><span>// d = #[1, 2, 3, 4] </span>
クリックリスナーを変更して、フレームスタックを使用して作業する必要があります。
PEEK()関数を使用して、スタックの上部にフレームを取得しています。次に、それを使用して、draw()関数を備えた新しいフレームを作成します。最後に、conj()を使用して、フレームスタックの上部に新しいフレームを
conjoin<span>// standard library </span><span>Array(1, 2, 3).map(x => x * 2); </span><span>// => [2, 4, 6] </span> <span>// mori </span><span>map(x => x * 2, vector(1, 2, 3)) </span><span>// => [2, 4, 6] </span>
を使用します。 ローカル状態(frame = conj(frames、newFrame))を変更していますが、実際にデータを変異させていません。 変更を取り消す
最後に、スタックからトップフレームをポップするために元に戻すボタンを実装する必要があります。
元に戻すボタンをクリックしたら、元に戻すフレームがあるかどうかを確認し、POP()関数を使用して、フレームを上部フレームを含む新しいリストに置き換えます。
最後に、変更を反映するために、新しいスタックの上部フレームをpaint()関数に渡します。この時点で、キャンバスの変更を描画して元に戻すことができるはずです。
demo<span>const fellowship = [ </span> <span>{ </span> <span>title: 'Mori', </span> <span>race: 'Hobbit' </span> <span>}, </span> <span>{ </span> <span>title: 'Poppin', </span> <span>race: 'Hobbit' </span> <span>} </span><span>]; </span> <span>deletePerson(fellowship, 1); </span><span>console.log(fellowship); </span>
これが私たちが最終的に次のことをします:
codepenでSitePoint(@SitePoint)でペン森のピクセルを参照してください。
拡張機能
このアプリケーションを改善できる方法のアイデアのリストは次のとおりです。
カラーパレットを追加して、ユーザーが描画する前に色を選択できるようにします
ローカルストレージを使用して、セッション間のフレームを保存
に保存しますctrl zキーボードショートカットを元に戻す
を作成します- マウスをドラッグしながらユーザーが描画できるようにします
- スタックからフレームを削除するのではなく、インデックスポインターを移動してやり直しを実装
- 同じプログラムのClojureScriptソースを読み取ります
- 結論
- ベクトル、リスト、範囲、ハッシュマップを調べましたが、Moriにはセット、ソート付きセット、キューも付属しており、これらのデータ構造にはそれぞれ協力するための多型機能が補完されます。
- 可能なことの表面をかろうじて引っ掻いたが、永続的なデータを強力な単純な機能セットとペアリングすることの重要性を十分に評価するのに十分なことを願っています。
不変のデータと機能的なJavaScriptに関するよくある質問
JavaScriptの不変性の概念は何ですか?
JavaScriptの不変性は、作成後に変更できないオブジェクトの状態を指します。これは、変数に値が割り当てられたら、変更できないことを意味します。この概念は、副作用を回避し、コードをより予測可能で理解しやすくするため、機能的なプログラミングにおいて重要です。また、効率的なデータ検索とメモリの使用を許可することにより、アプリケーションのパフォーマンスを向上させます。 JavaScriptへの永続的なデータ構造。これらのデータ構造は不変です。つまり、作成されると変更できません。これは、データの完全性を維持するのに役立ち、偶発的な変更を回避します。 Moriは、これらのデータ構造を操作しやすくする機能的プログラミングユーティリティの豊富なセットも提供します。不変のデータを処理する方法を提供している森は、より効率的で堅牢な方法を提供します。森の永続的なデータ構造は、ネイティブのJavaScriptメソッドよりも速く、消費量が少ないメモリを消費します。さらに、MoriはJavaScriptで利用できない幅広い機能プログラミングユーティリティを提供します。 。不変のオブジェクトを作成すると変更できないため、変更されるリスクなしに複数の機能呼び出しで安全に再利用できます。これにより、効率的なメモリ使用量とデータの検索が速くなり、アプリケーションの全体的なパフォーマンスが向上します。作成後に変更されます。一方、不変のデータ構造は作成されると変更できません。不変のデータ構造の操作は、新しいデータ構造をもたらします。
MORIはデータの操作をどのように処理しますか?
MORIは、データを操作するための機能的なプログラミングユーティリティの豊富なセットを提供します。これらのユーティリティを使用すると、元のデータを変更せずにデータ構造でマップ、削減、フィルターなど、さまざまな操作を実行できます。森には、変更されたときに以前のバージョンのデータを保存する不変のデータ構造があります。これは、永続的なデータ構造で操作を実行するたびに、データ構造の新しいバージョンが作成され、古いバージョンが保持されることを意味します。
MORIは、データの整合性をどのように保証しますか?
Moriは、不変のデータ構造を提供することにより、データの整合性を保証します。これらのデータ構造を作成すると変更できないため、偶発的なデータ変更のリスクは排除されます。これは、データの整合性を維持するのに役立ちます。
JavaScriptを使用したJavaScriptの機能的プログラミングの利点は何ですか?副作用を回避することで、コードがより予測可能で理解しやすくなります。また、効率的なデータ取得とメモリ使用量を許可することにより、アプリケーションのパフォーマンスを向上させます。プロジェクトに森ライブラリを含める必要があります。これを行うには、NPM経由でインストールするか、HTMLファイルに直接含めることができます。ライブラリが含まれたら、コード内のMoriの機能とデータ構造の使用を開始できます。
以上がMoriを使用した不変のデータと機能的JavaScriptの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

Web開発におけるJavaScriptの主な用途には、クライアントの相互作用、フォーム検証、非同期通信が含まれます。 1)DOM操作による動的なコンテンツの更新とユーザーインタラクション。 2)ユーザーエクスペリエンスを改善するためにデータを提出する前に、クライアントの検証が実行されます。 3)サーバーとのリフレッシュレス通信は、AJAXテクノロジーを通じて達成されます。

現実世界でのJavaScriptのアプリケーションには、フロントエンドとバックエンドの開発が含まれます。 1)DOM操作とイベント処理を含むTODOリストアプリケーションを構築して、フロントエンドアプリケーションを表示します。 2)node.jsを介してRestfulapiを構築し、バックエンドアプリケーションをデモンストレーションします。

JavaScriptエンジンが内部的にどのように機能するかを理解することは、開発者にとってより効率的なコードの作成とパフォーマンスのボトルネックと最適化戦略の理解に役立つためです。 1)エンジンのワークフローには、3つの段階が含まれます。解析、コンパイル、実行。 2)実行プロセス中、エンジンはインラインキャッシュや非表示クラスなどの動的最適化を実行します。 3)ベストプラクティスには、グローバル変数の避け、ループの最適化、constとletsの使用、閉鎖の過度の使用の回避が含まれます。

PythonとJavaScriptには、コミュニティ、ライブラリ、リソースの観点から、独自の利点と短所があります。 1)Pythonコミュニティはフレンドリーで初心者に適していますが、フロントエンドの開発リソースはJavaScriptほど豊富ではありません。 2)Pythonはデータサイエンスおよび機械学習ライブラリで強力ですが、JavaScriptはフロントエンド開発ライブラリとフレームワークで優れています。 3)どちらも豊富な学習リソースを持っていますが、Pythonは公式文書から始めるのに適していますが、JavaScriptはMDNWebDocsにより優れています。選択は、プロジェクトのニーズと個人的な関心に基づいている必要があります。

開発環境におけるPythonとJavaScriptの両方の選択が重要です。 1)Pythonの開発環境には、Pycharm、Jupyternotebook、Anacondaが含まれます。これらは、データサイエンスと迅速なプロトタイピングに適しています。 2)JavaScriptの開発環境には、フロントエンドおよびバックエンド開発に適したnode.js、vscode、およびwebpackが含まれます。プロジェクトのニーズに応じて適切なツールを選択すると、開発効率とプロジェクトの成功率が向上する可能性があります。

CとCは、主に通訳者とJITコンパイラを実装するために使用されるJavaScriptエンジンで重要な役割を果たします。 1)cは、JavaScriptソースコードを解析し、抽象的な構文ツリーを生成するために使用されます。 2)Cは、Bytecodeの生成と実行を担当します。 3)Cは、JITコンパイラを実装し、実行時にホットスポットコードを最適化およびコンパイルし、JavaScriptの実行効率を大幅に改善します。

JavaScriptは、Webサイト、モバイルアプリケーション、デスクトップアプリケーション、サーバー側のプログラミングで広く使用されています。 1)Webサイト開発では、JavaScriptはHTMLおよびCSSと一緒にDOMを運用して、JQueryやReactなどのフレームワークをサポートします。 2)ReactNativeおよびIonicを通じて、JavaScriptはクロスプラットフォームモバイルアプリケーションを開発するために使用されます。 3)電子フレームワークにより、JavaScriptはデスクトップアプリケーションを構築できます。 4)node.jsを使用すると、JavaScriptがサーバー側で実行され、高い並行リクエストをサポートします。
