JavaScript 関数プログラミング チュートリアルに関する簡単な説明 (写真)
前書き
4月初旬に私が北京にいたとき、クラスメートの徐昊は、会社の同僚が書いた記事は単純すぎて、細部に注意を払いすぎていると言ったので、私はゴマを拾い、スイカをなくしてしまいました。ブログの更新をやめてしまいました(実はプロジェクトが多忙のため全く更新していませんでした)。先週、私は他の数人の同僚と一緒に「Martin Fowler Shenzhen Tour」イベントに参加し、同僚の Tashi と、Yang Yun (江湖の大悪魔として知られています) と一緒に「FullStack Language JavaScript」を投稿しました。テーマは「マスター関数スタイルのプログラミングと制御システムの複雑さ」、Li Xin (江湖語ではXinyeとして知られています) のテーマは「同時実行性: 過去と死後」です。
他の同僚とリハーサルをしているときに、私たちのトピックが多かれ少なかれ関連していることに突然気づきました。私が話した部分には、イベントベースの同時実行メカニズムと関数型プログラミングも含まれていました。よく考えてみると、これは JavaScript 自体の特性に関連しているはずです。
イベントベース Node.js は同時実行における非常に典型的なモデルです
関数型プログラミングによりそれが可能になります 当然コールバックをサポートします非同期/イベントメカニズムに非常に適しています
関数型プログラミング機能により、DSLの作成に非常に適しています
会議の翌日、突然プロジェクトコードに集計モデルを追加したくなりました書き直しました関数型プログラミングを使用してみたところ、そのアイデアが NoSQL と漠然と関連していることがわかり、さらに自分には多くの欠点があることがわかりました。
次の例は実際のプロジェクトのシーンからのものですが、ドメインが切り替えられますが、その背後にあるメカニズムの読み取りと理解にはまったく影響しません。
ブックマーク アプリケーション
ユーザーが購読した RSS のリストを表示できるアプリケーションを想像してください。リスト内の各項目 (フィードと呼ばれます) には、id
、記事のタイトル title
、および記事へのリンク url
が含まれています。 id
,一个文章的标题title
和一个文章的链接url
。
数据模型看起来是这样的:
var feeds = [ { 'id': 1, 'url': 'http://abruzzi.github.com/2015/03/list-comprehension-in-python/', 'title': 'Python中的 list comprehension 以及 generator' }, { 'id': 2, 'url': 'http://abruzzi.github.com/2015/03/build-monitor-script-based-on-inotify/', 'title': '使用inotify/fswatch构建自动监控脚本' }, { 'id': 3, 'url': 'http://abruzzi.github.com/2015/02/build-sample-application-by-using-underscore-and-jquery/', 'title': '使用underscore.js构建前端应用' } ];
当这个简单应用没有任何用户相关的信息时,模型非常简单。但是很快,应用需要从单机版扩展到Web版,也就是说,我们引入了用户的概念。每个用户都能看到一个这样的列表。另外,用户还可以收藏Feed。当然,收藏之后,用户还可以查看收藏的Feed列表。
由于每个用户可以收藏多个Feed,而每个Feed也可以被多个用户收藏,因此它们之间的多对多关系如上图所示。可能你还会想到诸如:
$ curl http://www.php.cn/:9999/user/1/feeds
来获取用户1
的所有feed
等,但是这些都不重要,真正的问题是,当你拿到了所有Feed之后,在UI上,需要为每个Feed填加一个属性makred
。这个属性用来标示该feed是否已经被收藏了。对应到界面上,可能是一枚黄色的星星,或者一个红色的心。
服务器端聚合
由于关系型数据库的限制,你需要在服务器端做一次聚合,比如将feed对象包装一下,生成一个FeedWrapper
之类的对象:
public class FeedWrapper { private Feed feed; private boolean marked; public boolean isMarked() { return marked; } public void setMarked(boolean marked) { this.marked = marked; } public FeedWrapper(Feed feed, boolean marked) { this.feed = feed; this.marked = marked; } }
然后定义一个FeedService
之类的服务对象:
public ArrayList<FeedWrapper> wrapFeed(List<Feed> markedFeeds, List<Feed> feeds) { return newArrayList(transform(feeds, new Function<Feed, FeedWrapper>() { @Override public FeedWrapper apply(Feed feed) { if (markedFeeds.contains(feed)) { return new FeedWrapper(feed, true); } else { return new FeedWrapper(feed, false); } } })); }
好吧,这也算是一个还凑合的实现,但是静态强类型的Java做这个事儿有点勉强,而且一旦发生新的变化(几乎肯定会发生),我们还是把这部分逻辑放在JavaScript中,来看看它是如何简化这一个过程的。
客户端聚合
快要说到主题了,这篇文章我们会使用lodash
作为函数式编程的库来简化代码的编写。由于JavaScript是一个动态弱类型的语言,我们可以随时为一个对象添加属性,这样一个简单的map
操作就可以完成上边的Java对应的代码了:
_.map(feeds, function(item) { return _.extend(item, {marked: isMarked(item.id)}); });
其中函数isMarked
会做这样一件事儿:
var userMarkedIds = [1, 2]; function isMarked(id) { return _.includes(userMarkedIds, id); }
即查看传入的参数是否在一个列表userMarkedIds
,这个列表可能由下列的请求来获得:
$ curl http://www.php.cn/:9999/user/1/marked-feed-ids
之所有只获取id是为了减少网络传输的数据大小,当然你也可以将全部的/marked-feeds
都请求到,然后在本地做_.pluck(feeds, 'id')
来抽取所有的id
{{#each feeds}} <li class="list-item"> <p class="section" data-feed-id="{{this.id}}"> {{#if this.marked}} <span class="marked icon-favorite"></span> {{else}} <span class="unmarked icon-favorite"></span> {{/if}} <a href="/feeds/{{this.url}}"> <p class="detail"> <h3 id="this-title">{{this.title}}</h3> </p> </a> </p> </li> {{/each}}

1
のすべての feed
を取得するための 🎜_.map(feeds, function(item) { return _.extend(item, {marked: true}); });
makred</ を追加する必要があります。コード>。この属性は、フィードが収集されたかどうかを示すために使用されます。インターフェースに応じて、黄色の星または赤いハートになります。 🎜🎜<img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/article/000/000/194/7e61e8d04626c3521e0f324d1028bc02-1.png" class="lazy" alt="JavaScript 関数プログラミング チュートリアルに関する簡単な説明 (写真)"/>🎜<h4 id="a-href-http-www-php-cn-js-js-jspopular-guide-service-html-target-blank-サービス-サーバー側集約"><a href=" http://www.php.cn/js/js-jspopular-guide-service.html" target="_blank">サービス🎜サーバー側集約</h4>🎜リレーショナル データベースの制限により、次のことが必要です。集約を実行します。たとえば、フィード <a href="http://www.php.cn/wiki/60.html" target="_blank"> オブジェクト 🎜 をラップして <code>FeedWrapper
を生成します。クラス オブジェクト: 🎜_.map(feeds, function(item) { return _.extend(item, {marked: isMarked(item.id)}); }); _.map(feeds, function(item) { return _.extend(item, {marked: true}); });
FeedService
のようなサービス オブジェクトを定義します: 🎜function wrapFeeds(feeds, predicate) { return _.map(feeds, function(item) { return _.extend(item, {marked: predicate(item.id)}); }); }
クライアント側の集約
🎜 この記事では、コードの記述を簡略化するために関数型プログラミング ライブラリとしてlodash
を使用します。 JavaScript は動的に弱い型指定の言語であるため、いつでもオブジェクトに属性を追加できます。このようにして、単純な map
操作で、上記の Java 対応コードを完成させることができます。 >isMarked は 1 つのことを行います: 🎜wrapFeeds(feeds, isMarked);
userMarkedIds
にあるかどうかを確認します。このリストは次のリクエストによって取得できます: 🎜wrapFeeds(feeds, function(item) {return true});
/marked-feeds
をリクエストしてから _.pluck(feeds, 'id ')
を実行することもできます。 > すべての id
属性を抽出します。 🎜嗯,代码是精简了许多。但是如果仅仅能做到这一步的话,也没有多大的好处嘛。现在需求又有了变化,我们需要在另一个页面上展示当前用户的收藏夹(用以展示用户所有收藏的feed)。作为程序员,我们可不愿意重新写一套界面,如果能复用同一套逻辑当然最好了。
比如对于上面这个列表,我们已经有了对应的模板:
{{#each feeds}} <li class="list-item"> <p class="section" data-feed-id="{{this.id}}"> {{#if this.marked}} <span class="marked icon-favorite"></span> {{else}} <span class="unmarked icon-favorite"></span> {{/if}} <a href="/feeds/{{this.url}}"> <p class="detail"> <h3 id="this-title">{{this.title}}</h3> </p> </a> </p> </li> {{/each}}
事实上,这段代码在收藏夹页面上完全可以复用,我们只需要把所有的marked
属性都设置为true就行了!简单,很快我们就可以写出对应的代码:
_.map(feeds, function(item) { return _.extend(item, {marked: true}); });
漂亮!而且重要的是,它还可以如正常工作!但是作为程序员,你很快就发现了两处代码的相似性:
_.map(feeds, function(item) { return _.extend(item, {marked: isMarked(item.id)}); }); _.map(feeds, function(item) { return _.extend(item, {marked: true}); });
消除重复是一个有追求的程序员的基本素养,不过要消除这两处貌似有点困难:位于marked:
后边的,一个是函数调用,另一个是值!如果要简化,我们不得不做一个匿名函数,然后以回调的方式来简化:
function wrapFeeds(feeds, predicate) { return _.map(feeds, function(item) { return _.extend(item, {marked: predicate(item.id)}); }); }
对于feed列表,我们要调用:
wrapFeeds(feeds, isMarked);
而对于收藏夹,则需要传入一个匿名函数:
wrapFeeds(feeds, function(item) {return true});
在lodash
中,这样的匿名函数可以用_.wrap
来简化:
wrapFeeds(feeds, _.wrap(true));
好了,目前来看,简化的还不错,代码缩减了,而且也好读了一些(当然前提是你已经熟悉了函数式编程的读法)。
更进一步
如果仔细审视isMarked
函数,会发现它对外部的依赖不是很漂亮(而且这个外部依赖是从网络异步请求来的),也就是说,我们需要在请求到markedIds
的地方才能定义isMarked
函数,这样就把函数定义绑定
到了一个固定的地方,如果该函数的逻辑比较复杂,那么势必会影响代码的可维护性(或者更糟糕的是,多出维护)。
要将这部分代码隔离出去,我们需要将ids
作为参数传递出去,并得到一个可以当做谓词(判断一个id是否在列表中的谓词)的函数。
简而言之,我们需要:
var predicate = createFunc(ids); wrapFeeds(feeds, predicate);
这里的createFunc
函数接受一个列表作为参数,并返回了一个谓词函数。而这个谓词函数就是上边说的isMarked
。这个神奇的过程被称为柯里化currying
,或者偏函数partial
。在lodash
中,这个很容易实现:
function isMarkedIn(ids) { return _.partial(_.includes, ids); }
这个函数会将ids
保存起来,当被调用时,它会被展开为:_.includes(ids, <id>)
。只不过这个<id>
会在实际迭代的时候才传入:
$('/marked-feed-ids').done(function(ids) { var wrappedFeeds = wrapFeeds(feeds, isMarkedIn(ids)); console.log(wrappedFeeds); });
这样我们的代码就被简化成了:
$('/marked-feed-ids').done(function(ids) { var wrappedFeeds = wrapFeeds(feeds, isMarkedIn(ids)); var markedFeeds = wrapFeeds(feeds, _.wrap(true)); allFeedList.html(template({feeds: wrappedFeeds})); markedFeedList.html(template({feeds: markedFeeds})); });
以上が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)

ホットトピック











JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

JavaScript で HTTP ステータス コードを取得する方法の紹介: フロントエンド開発では、バックエンド インターフェイスとの対話を処理する必要があることが多く、HTTP ステータス コードはその非常に重要な部分です。 HTTP ステータス コードを理解して取得すると、インターフェイスから返されたデータをより適切に処理できるようになります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法と、具体的なコード例を紹介します。 1. HTTP ステータス コードとは何ですか? HTTP ステータス コードとは、ブラウザがサーバーへのリクエストを開始したときに、サービスが

C++ ラムダ式は、関数型プログラミングに次のような利点をもたらします。 シンプルさ: 匿名インライン関数により、コードの可読性が向上します。コードの再利用: コードの再利用を容易にするために、ラムダ式を渡したり保存したりできます。カプセル化: 別の関数を作成せずにコードの一部をカプセル化する方法を提供します。実際のケース: リスト内の奇数をフィルタリングします。リスト内の要素の合計を計算します。ラムダ式は、関数型プログラミングの簡素化、再利用性、カプセル化を実現します。

Go では、遅延データ構造を使用して遅延評価を実装できます。実際の値をカプセル化し、必要な場合にのみ評価するラッパー型を作成します。関数型プログラムでのフィボナッチ数列の計算を最適化し、実際に必要になるまで中間値の計算を延期します。これにより、不要なオーバーヘッドが排除され、関数型プログラムのパフォーマンスが向上します。

Go で関数型プログラミングを使用する場合に注意すべき 5 つの一般的な間違いと落とし穴があります。 参照を誤って変更することを避け、新しく作成された変数が返されるようにしてください。同時実行の問題を解決するには、同期メカニズムを使用するか、外部の可変状態のキャプチャを避けます。コードの可読性と保守性を向上させるために、部分的な機能化は控えめに使用してください。アプリケーションの堅牢性を確保するために、常に関数内のエラーを処理してください。パフォーマンスへの影響を考慮し、インライン関数、フラット化されたデータ構造、操作のバッチ処理を使用してコードを最適化します。

pythonLambda 式は、簡潔で読みやすく、使いやすいコードを作成するための強力で柔軟なツールです。これらは、他の関数に引数として渡したり、変数に保存したりできる匿名関数をすばやく作成するのに最適です。 Lambda 式の基本構文は次のとおりです。 lambdaarguments:expression たとえば、次の Lambda 式は 2 つの数値を加算します: lambdax,y:x+y この Lambda 式は、次のように引数として別の関数に渡すことができます。 defsum( x ,y):returnx+yresult=sum(lambdax,y:x+y,1,2) この例では

Python のラムダ式は、匿名関数の別の構文形式です。これは、プログラム内のどこにでも定義できる小さな匿名関数です。ラムダ式はパラメータ リストと式で構成されます。式には有効な Python 式を使用できます。ラムダ式の構文は次のとおりです: lambdaargument_list:expression. たとえば、次のラムダ式は 2 つの数値の合計を返します: lambdax,y:x+y. このラムダ式は、マップなどの他の関数に渡すことができます。 () 関数:数値=[ 1,2,3,4,5]結果=マップ(ラムダ

Java 関数型プログラミングの利点には、単純さ、構成可能性、同時実行性、テストのしやすさ、パフォーマンスなどがあります。欠点としては、学習に時間がかかること、デバッグが難しいこと、柔軟性が限られていること、パフォーマンスのオーバーヘッドが挙げられます。その主な機能には、副作用のない純粋な関数、データ処理パイプライン、ステートレス コード、効率的なストリーミング API が含まれます。
