ホームページ >ウェブフロントエンド >jsチュートリアル >Javascript 関数型プログラミングの簡単な紹介_JavaScript スキル
関数型プログラミングは、数十年にわたりコンピューター サイエンスの愛好家の間で人気があり、その数学的な純粋さと謎めいた性質により、データ サイエンティストや博士号取得希望者によってコンピューター研究室に埋もれていました。しかし今、Python、Julia、Ruby、Clojure、そして最後ではありませんが Javascript などの最新言語のおかげで、この言語はルネッサンスを迎えています。
JavaScript のことですか?このWEBスクリプト言語?それは正しい!
JavaScript は、長い間消滅していない重要なテクノロジーであることが証明されています。これは主に、backbone.js、jQuery、Dojo、underscore.js など、拡張されたフレームワークとライブラリの一部によって生まれ変わる機能によるものです。これは、関数型プログラミング言語としての Javascript の正体に直接関係しています。 Javascript の関数型プログラミングを理解することは重要であり、今後はあらゆるレベルのプログラマーにとって役立ちます。
なぜですか?関数型プログラミングは非常に強力で、堅牢で、エレガントです。これは大規模なデータ構造にとって非常に便利で効率的です。 JavaScript は、クライアント側のスクリプト言語として、DOM の機能的な操作、API 応答の整理、およびますます複雑化する Web サイトを扱う際のその他のタスクの完了に非常に役立ちます。
この本では、JavaScript の関数型プログラミングについて知っておくべきことすべてを学びます。関数型プログラミングを使用して Javascript Web アプリケーションを構築する方法、JavaScript の隠された力を解き放つ方法、より強力なコードを記述する方法、そしてプログラムが小さいため、コードの保守が容易になり、より速くダウンロードでき、コストも低くなります。また、関数型プログラミングの中心となる概念とそれを Javascript に適用する方法、JavaScript を関数型言語として使用する場合の問題を回避する方法、JavaScript で関数型プログラミングとオブジェクト指向プログラミングを混合する方法についても学習します。
しかし、始める前に、実験をしてみましょう。
例
おそらく、JavaScript で関数型プログラミングを導入する最良の方法は、簡単な例を使用することです。 JavaScript でいくつかのタスクを実行します。1 つは従来のネイティブ アプローチを使用し、もう 1 つは関数型プログラミングを使用します。次に、これら 2 つの方法を比較します。
アプリケーション - 電子商取引 Web サイト
リアルさを追求するため、ECサイト、コーヒー豆の通販会社を作ります。このウェブサイトでは、品質が異なり、もちろん価格も異なる数種類のコーヒーを販売しています。
命令型メソッド
まず、プログラムを書き始めます。この例を実用的にするには、データを保持するいくつかのオブジェクトを作成する必要があります。必要に応じてデータベースから値を取得できます。 しかし、ここではそれらが静的に定義されていると仮定します:
// create some objects to store the data. var columbian = {  name: 'columbian', basePrice: 5 }; var frenchRoast = { name: 'french roast', basePrice: 8 }; var decaf = { name: 'decaf', basePrice: 6 }; // 我们将使用辅助函数计算价格 // 根据size打印到一个HTML的列表中 function printPrice(coffee, size) { if (size == 'small') { var price = coffee.basePrice + 2; } else if (size == 'medium') { var price = coffee.basePrice + 4; } else { var price = coffee.basePrice + 6; } // create the new html list item var node = document.createElement("li"); var label = coffee.name + ' ' + size; var textnode = document.createTextNode(label+' price: $'+price); node.appendChild(textnode); document.getElementById('products').appendChild(node); } // 现在我们只需根据咖啡的各种价格和size的组合调用printPrice函数 printPrice(columbian, 'small'); printPrice(columbian, 'medium'); printPrice(columbian, 'large'); printPrice(frenchRoast, 'small'); printPrice(frenchRoast, 'medium'); printPrice(frenchRoast, 'large'); printPrice(decaf, 'small'); printPrice(decaf, 'medium'); printPrice(decaf, 'large');
ご覧のとおり、このコードは非常に基本的なものです。もし、この 3 種類以外にもコーヒーの種類がもっと増えたらどうなるでしょうか? 20 個、あるいは 50 個あった場合はどうなるでしょうか? もっとサイズがある場合はどうなりますか?有機物と無機物がある場合はどうなるでしょうか?これにより、すぐに膨大な量のコードが必要になります。
この方法を使用して、マシンにあらゆるコーヒーの種類とサイズを印刷させます。これが、この命令的アプローチをとる場合の基本的な問題です。
関数型プログラミング
命令型コードは、問題を解決するために何をする必要があるかを段階的にコンピューターに指示します。これとは対照的に、関数型プログラミングは問題を数学的に記述し、残りはコンピューターに実行させます。
より機能的な方法では、同じアプリケーションを次のように作成できます。
// 从接口中分解数据和逻辑 var printPrice = function(price, label) { var node = document.createElement("li"); var textnode = document.createTextNode(label+' price: $'+price); node.appendChild(textnode); document.getElementById('products 2').appendChild(node); } // 为每种咖啡创建函数对象 var columbian = function(){ this.name = 'columbian'; this.basePrice = 5; }; var frenchRoast = function(){ this.name = 'french roast'; this.basePrice = 8; }; var decaf = function(){ this.name = 'decaf'; this.basePrice = 6; }; // 为每种size通过字面量创建对象 var small = { getPrice: function(){return this.basePrice + 2}, getLabel: function(){return this.name + ' small'} }; var medium = { getPrice: function(){return this.basePrice + 4}, getLabel: function(){return this.name + ' medium'} }; var large = { getPrice: function(){return this.basePrice + 6}, getLabel: function(){return this.name + ' large'} }; // 将所有咖啡的种类和size放到数组里 var coffeeTypes = [columbian, frenchRoast, decaf]; var coffeeSizes = [small, medium, large]; // 创建由上面内容组成的新对象,并把它们放到一个新数组里 var coffees = coffeeTypes.reduce(function(previous, current) { var newCoffee = coffeeSizes.map(function(mixin) { // `plusmix`是函数式的minxin, 见第7章 var newCoffeeObj = plusMixin(current, mixin); return new newCoffeeObj(); }); return previous.concat(newCoffee); },[]); // 现在我们已经定义了如何获得所有咖啡种类和size组合方式的价格,现在可以直接打印它们了 coffees.forEach(function(coffee){ printPrice(coffee.getPrice(),coffee.getLabel()); });
最初に明確にしておく必要があるのは、このコードはよりモジュール化されているということです。サイズの追加やコーヒーの種類の追加は、次のコードと同じくらい簡単になりました:
var peruvian = function(){ this.name = 'peruvian'; this.basePrice = 11; }; var extraLarge = { getPrice: function(){return this.basePrice + 10}, getLabel: function(){return this.name + ' extra large'} }; coffeeTypes.push(Peruvian); coffeeSizes.push(extraLarge);
咖啡对象的数组和size对象的数组混合(mix)到了一起,也就是他们的方法和成员变量被组合到了一块儿 ——通过一个叫“plusMinxin”的自定义函数(详见第七章)。这些咖啡类型的类(columbian, frenchRoast, decaf)包含了成员变量, 而这些size对象(small, medium, large)包含了获取名称和计算价格的方法。 ”混合”(minxing)这个动作通过一个map操作来起作用,也就是对数组中的每一个成员执行一个纯函数并返回一个新的函数, 然后这些返回的函数被放到了一个reduce函数中被操作,reduce也是一个高阶函数,和map有些像, 只是reduce把数组里的所有元素处理后组合到了一个东西里面。最终,新的数组包含了所有可能的种类和size的组合, 这个数组通过forEach方法遍历,forEach也是一个高阶函数,它会让数组里面每一个对象作为参数执行一遍回调函数。 在这个例子里,这个回调函数是一个匿名函数,它获取这些对象后,以对象的getPrice()和getLabel() 两个方法的返回值作为参数调用printPrice函数。
实际上,我们可以让这个例子更加函数式:去掉coffees变量,并将函数串到一起链式调用,这也是函数式编程的一个小技巧。
coffeeTypes.reduce(function(previous, current) { var newCoffee = coffeeSizes.map(function(mixin) { // `plusMixin` function for functional mixins, see Ch.7 var newCoffeeObj = plusMixin(current, mixin); return new newCoffeeObj(); }); return previous.concat(newCoffee); },[]).forEach(function(coffee) { printPrice(coffee.getPrice(),coffee.getLabel()); });
这样,控制流没有像命令式代码那样从头到尾的顺序进行。在函数式编程里,map函数和其它高阶函数代替了for和while循环, 只有少量关键的代码是在顺序执行。 这使得新接触的人在阅读这样范式的代码有些困难,但是一旦你能够欣赏它,你就会发现这根本没啥难的, 而且这样写看起来更好。
这个例子仅仅是刚开始展露Javascript中函数式编程能做什么。通过这本书,你将会看到更多函数式实现的强悍的例子。
总结
首先,采用函数式风格的优点已经明确了。 其次,不要害怕函数式编程。的确,它往往被认为是编程语言的纯逻辑形式,但是我们不需要理解lambda演算也能够在日常任务中应用它。 实际上,通过把我们的程序拆分成小的片段,它们变得更容易被理解、维护,也更加可靠。 map和reduce函数是Javascript中不太被知道的内建函数,然而我们将要关注它们。
Javascript是一个脚本语言,可交互,易使用,不需要编译。我们甚至不需要下载任何开发软件, 你最喜欢的浏览器就可以作为开发环境的解释器。
感兴趣吗?好,我们开始!