ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScriptを使った関数型プログラミング(1) 翻訳_javascriptスキル
プログラミング パラダイム
プログラミング パラダイムは、問題について考え、問題に対するビジョンを実現するためのツールで構成されるフレームワークです。現代の言語の多くはポリパラダイム (またはマルチパラダイム) です。オブジェクト指向、メタプログラミング、関数型、手続き型など、さまざまなプログラミング パラダイムをサポートしています。
関数型プログラミングのパラダイム
関数型プログラミングは、水素で動く自動車のようなものであり、先進的な未来ですが、あまり普及されていません。 。命令型プログラミングとは対照的に、実行時にグローバル状態を更新する一連のステートメントで構成されます。関数型プログラミングでは、計算が式の評価に変わります。これらの式はすべて純粋な数学関数で構成されており、第一級であり (通常の値として使用および処理できます)、副作用はありません。
関数型プログラミングの値は次の値です:
関数が最優先です
処理関数 プログラミング言語の他のクラス オブジェクトと同様に処理されます。つまり、関数を変数に格納したり、関数を動的に作成したり、関数を返したり、関数を他の関数に渡したりすることができます。例を見てみましょう...
文字列は変数として保存でき、関数も同様に保存できます。たとえば、
var sayHello = function() { return “Hello” };
文字列はオブジェクト フィールドとして保存でき、関数も保存できます。例:
var person = {message: “Hello”, sayHello: function() { return “Hello” }};
文字列は再度必要になった場合にのみ作成できます。関数を使用できます。例:
“Hello ” + (function() { return “World” })(); //=> Hello World
文字列を入力パラメータとして関数に渡すことができ、関数でもそれを行うことができます:
function hellloWorld(hello, world) { return hello + world() }
文字列は次のように指定できます。関数の戻り値として使用され、関数でもそれを行うことができます。例:
return “Hello”; return function() { return “Hello”};
関数が他の関数を入力パラメータまたは戻り値として受け取る場合、その関数は高階関数と呼ばれます。高階関数の例を見てきました。次に、より複雑な状況を見てみましょう。
例 1:
[1, 2, 3].forEach(alert); // alert 弹窗显示“1" // alert 弹窗显示 "2" // alert 弹窗显示 "3”
例 2:
function splat(fun) { return function(array) { return fun.apply(null, array); }; } var addArrayElements = splat(function(x, y) { return x + y }); addArrayElements([1, 2]); //=> 3
お気に入り純粋な機能
Pure関数には他の副作用はありません。いわゆる副作用とは、関数によって引き起こされる関数の外部状態の変更を指します。例:
変数の変更
データ構造の変更
変数の変更外の世界 フィールドを設定します
例外をスローするか、エラー メッセージをポップアップします
最も単純な例は数学関数です。 Math.sqrt(4) 関数は常に 2 を返します。ステータスや設定パラメータなどの他の冷却情報は使用されません。数学関数が副作用を引き起こすことはありません。
関数型プログラミングはデータを変更できない純粋関数をサポートしているため、主に不変データを作成するために使用されます。この方法では、既存のデータ構造を変更する必要がなく、新しいデータ構造を効率的に作成できます。
一部のローカル データを変更することで純粋関数が不変の戻り値を生成することが許可されているかどうかを知りたい場合があります。 ? の?答えは「はい」です。
JavaScript ではデフォルトで不変のデータ型はほとんどありません。文字列は変更できないデータ型の例です:
var s = "HelloWorld"; s.toUpperCase(); //=> "HELLOWORLD" s; //=> "HelloWorld"
• 混乱を避け、プログラムの精度を向上させます: 複雑なシステムでは、ほとんどの不明瞭なバグが発生します。プログラム内の外部クライアント コードによって状態が変更されることが原因で発生します。
• 「高速かつ簡潔な」マルチスレッド プログラミングを確立します。複数のスレッドが同じ共有値を変更できる場合は、その値を同期的に取得する必要があります。これは専門家にとって、退屈でエラーが発生しやすいプログラミングの課題です。
ソフトウェア トランザクション メモリとアクター モデルは、スレッドセーフな方法で変更を直接処理します。
再帰は最も有名な関数型プログラミング手法です。まだご存じない方のために説明すると、再帰関数とはそれ自体を呼び出す関数です。
替代反复循环的最经典方式就是使用递归,即每次完成函数体操作之后,再继续执行集合里的下一项,直到满足结束条件。递归还天生符合某些算法实现,比如遍历树形结构(每个树枝都是一颗小树)。
在任何语言里,递归都是一项重要的函数式编程方式。很多函数语言甚至要求的更加严格:只支持递归遍历,而不支持显式的循环遍历。这需要语言必须保证消除了尾端调用,这是 JavasSrip 不支持的。
数学定义了很多无穷集合,比如自然数(所有的正整数)。他们都是符号表示。任意特定有限的子集都在需要时求值。我们将其称之为惰性求值(也叫做非严格求值,或者按需调用,延迟执行)。及早求值会强迫我们表示出所有无穷数据,而这显然是不可能的。
很多语言都默认是惰性的,有些也提供了惰性数据结构以表达无穷集合,并在需要时对自己进行精确计算。
很明显一行代码 result = compute() 所表达的是将 compute() 的返回结果赋值给 result。但是 result 的值究竟是多少只有其被用到的时候才有意义。
可见策略的选择会在很大程度上提高性能,特别是当用在链式处理或者数组处理的时候。这些都是函数式程序员所喜爱的编程技术。
这就开创可很多可能性,包括并发执行,并行技术以及合成。
但是,有一个问题,JavaScrip 并不对自身进行惰性求值。话虽如此,Javascript 里的函数库可以有效地模拟惰性求值。
所有的函数式语言都有闭包,然而这个语言特性经常被讨论得很神秘。闭包是一个函数,这个函数有着对内部引用的所有变量的隐式绑定。换句话说,该函数对它引用的变量封闭了一个上下文。JavaScript 中的闭包是能够访问父级作用域的函数,即使父级函数已经调用完毕。
function multiplier(factor) { return function(number) { return number * factor; }; } var twiceOf = multiplier(2); console.log(twiceOf(6)); //=> 12
函数式编程是声明式的,就像数学运算,属性和关系是定义好的。运行时知道怎么计算最终结果。阶乘函数的定义提供了一个例子:
factorial(n) = 1 if n = 1
n * factorial(n-1) if n > 1
该定义将 factorial(n) 的值关联到 factorial(n-1),是递归定义。特殊情况下的 factorial(1) 终止了递归。
var imperativeFactorial = function(n) { if(n == 1) { return 1 } else { product = 1; for(i = 1; i <= n; i++) { product *= i; } return product; } } var declarativeFactorial = function(n) { if(n == 1) { return 1 } else { return n * factorial(n - 1); } }
从它实现阶乘计算来看,声明式的阶乘可能看起来像“命令式”的,但它的结构更像声明式的。
命令式阶乘使用可变值、循环计数器和结果来累加计算后的结果。这个方法显式地实现了特定的算法。不像声明式版本,这种方法有许多可变步骤,导致它更难理解,也更难避免 bug 。
有很多函数式库:underscore.js, lodash,Fantasy Land, Functional.js, Bilby.js, fn.js, Wu.js, Lazy.js, Bacon.js, sloth.js, stream.js, Sugar, Folktale, RxJs 等等。
map(), filter(), 和 reduce()函数 构成了函数式程序员工具包的核心。 纯高阶函数成了函数式方法的主力。事实上,它们是纯函数和高阶函数应该仿效的典型。它们用一个函数作为输入,返回没有副作用的输出。
这些 JavaScript 函数对每一个函数式程序来说都是至关重要的。他们可以去除循环和语句,使得代码更加整洁。这些都是实现 ECMAScript5.1 的浏览器的标准,他们只处理数组。每次调用都会创建创建并返回一个新的数组。已存在的数组不会被修改。但是稍等,事情很不止于此。。。他们还将函数作为输入参数,通常是作为回调的匿名函数。他们会遍历将整个数组并且将该回调函数应用与每一项!
myArray = [1,2,3,4];
newArray = myArray.map(function(x) {return x*2});
console.log(myArray); // Output: [1,2,3,4]
console.log(newArray); // Output: [2,4,6,8]
除了这三个函数,还有很多函数可以扎入到几乎每一个函数式应用里:
forEach(),concat(), reverse(), sort(), every() 以及some().
JavaScript は、もちろん厳密な意味での関数型プログラミング言語ではありません。その他のパラダイム 使用法:
命令型プログラミング: 詳細な操作記述に基づくプログラミング
プロトタイプベースのオブジェクト指向プログラミング: プロトタイプ オブジェクトとその例のプログラミング
メタプログラミング: JavasScript 実行モデルを操作するプログラミング手法。メタプログラミングの適切な定義は、「プログラミングは何かを実行するコードを記述するときに発生しますが、メタプログラミングは、何かの解釈方法に変更を引き起こすコードを記述するときに発生します。