しかし、言っている、非常に多くの場合、関数型プログラミングは、本当に良い香りです!本論文ではまた、最も簡潔な書面で目指していた機能のコアコンセプトを説明しました。
JS自体が知られており、ますます人気のオブジェクト指向プログラミング機能を比較し、複数のプログラミングパラダイムをサポートしています。自然は新しい知識ではなく、プログラミングのコンセプト。これらのパラダイムそれぞれ独自の特徴を持って、我々は支配を使用することを学ぶのではなく、相互に排他的でなければなりません。正しいことを行うために適切な技術を使用して、適切な場所では、よりよい解決策です。
次に、機能的な原則のいくつかの基本的な概念を見て:
原則、特性、概念
- 第一原理の機能は、第2の原理が小さい、小さいです。
- 関数型プログラミングの世界では、外部環境に全く依存し、無の状態、無変異はありません。
- 同じ入力の機能は、出力が同じでなければなりません。これは、参照透明性として知られています。
- むしろ不可欠よりも、宣言型と抽象コードを書くための関数型プログラミングのアイデア。命令型は宣言に焦点を当て、「方法」をコンパイラに伝えることであることは、「何をするか。」コンパイラーに指示します
ピュア機能
純粋関数は、同じ出力を得るために、入力と同じ機能です。
- 純粋関数は、任意の外部変数を変更するべきではない、任意の外部の変数に依存しないでください。
- 最初の利点は、純粋な機能をテストすることは容易です。
- 純粋関数は、意味のある名前を持っている必要があります。
- コアは、関数型プログラミングパラダイムの組み合わせであります
高次機能
- 関数は、パラメータとして渡すことができ、(HOCをいう)高次関数と称される
- 機能は他の機能に戻すことができます。
// 函数可以作为参数传递
var fn = func => typeof func === 'function' && func();
var log = () => console.log('hello HOC');
fn(log)
// 函数可以被其他函数返回
var fn2 = () => log;
var func2 = fn2();
func2()
复制代码
- 高階関数によって抽象
// unless函数只在断言为false的时候执行函数
const unless = (predicate, fn) => {
if (!predicate) fn();
}
// 所以我们很方便的求出一个数组中的偶数
const isEven = e => e % 2 === 0;
const isOdd = e => !isEven(e);
[1,2,3,4,5,6].forEach(e => unless(isOdd(e), () => console.log(e)))
// 定义times函数,指定函数执行n次
const times = (times, fn) => {
for(var i = 0; i < times; i++) fn(i)
}
times(100, e => unless(isOdd(e), () => console.log(e)))
复制代码
- 本当の高階関数:一部/すべての/マップ/フィルター/ /ソートを減らす、など
// 定义一个sortBy函数作为通用的数组排序的参数
// 根据某个属性返回一个从小到大排序的函数,作为sort的参数
const sortBy = (property) => (a, b) => a[property] > b[property] ? 1 : a[property] === b[property] ? 0 : -1;
const arr = [{name: '小猫', age: 5}, {name: '小狗', age: 1}];
arr.sort(sortBy('age'));
console.log(arr);
复制代码
クロージャ
- 外部変数の関数にアクセスすることができる閉鎖の力は、関数は、その範囲に拡張することができます。
- 単項定義関数、パラメータの受信された複数の変換関数は、パラメータの機能のみを受信します
// unary函数
const unary = fn => fn.length === 1 ? fn : (arg) => fn(arg);
const arrInt = [1,2,3].map(parseInt); // [1, NaN, NaN]
const arrInt2 = [1,2,3].map(unary(parseInt)); // [1, 2, 3]
复制代码
- メモ化キャッシュ機能
// 纯函数的输出只依赖输入,所以可以对其做缓存操作
// 阶乘函数只依赖输入,所以是纯函数
const factorial = n => {
if (n === 1) return 1;
return n * factorial(n - 1);
}
// 定义memoized缓存函数
const memoized = fn => {
const cache = {};
return function (arg) {
if (cache.hasOwnProperty(arg)) return cache[arg];
return cache[arg] = fn(arg);
}
}
// 定义阶乘的缓存函数
const memoFactorial = memoized(factorial);
// 调用
console.time('one');
memoFactorial(1000);
console.timeEnd('one'); // one: 0.115966796875ms
console.time('two');
memoFactorial(1000);
console.timeEnd('two') // two: 0.02490234375ms
复制代码
- 二つの配列のためのジップ機能は、アレイに結合され
const zip = (arrLeft, arrRight, fn) => {
let result = [];
let index = 0;
let maxLength = Math.max(arrLeft.length, arrRight.length);
for (; index < maxLength; index++) {
const res = fn(arrLeft[index], arrRight[index]);
result.push(res);
}
return result;
}
zip([1,23,4], [2,4,5], (a, b) => a + b) // [3, 27, 9]
复制代码
部分適用とカリー化
- 唯一の多変量関数として知られている複数のパラメータを受信し、2要素パラメトリックとして知られる2つのパラメータをとるメンバーシップ関数にパラメータを受信します。受信不確実なパラメータは可変引数機能を言及しました。
- カリー化は、ネストされた関数の一価プロセスに変換する複数のパラメータの関数です。
// 定义柯里化函数
const curry = (fn) => {
return function curryFunc(...arg) {
if (arg.length < fn.length) {
return function () {
return curryFunc.apply(null, [...arg, ...arguments]);
};
}
return fn.apply(null, arg);
}
};
const func = (a, b) => console.log(a - b);
curry(func)(1)(2)
复制代码
- アプリケーション開発者は部分を機能させることができ、部分的アプリケーション
// 定义偏应用
// 只当partial时后续参数为udefined时才使用对应的实参替换
const partial = (fn, ...args) => {
return function (...last) {
let i = 0;
const argsLen = args.length;
const lastLen = last.length;
for (; i < argsLen && i < lastLen; i++) {
args[i] === undefined && (args[i] = last[i]);
}
return fn.apply(null, args);
}
}
const timer3s = partial(setTimeout, undefined, 3000)
timer3s(() => console.log('hello')) // 3s后输出hello
// bug原因在于undefined已经被替换掉了,后面再调用时发现没有undefined便不会再替换
timer3s(() => console.log('hello2')) // 依旧输出hello,而不是hello2
timer3s(() => console.log('hello3'))
复制代码
そして、パイプラインの組み合わせ
- 古いプログラムに新しいプロパティを追加するよりも、より良い再構築するために、新しいタスクを完了したい場合はUnixの哲学は、プログラムは、ただ一つのことを行います。(あなたが新しいタスクを完了したい場合は、単一の原則として理解、再結合し、元のプログラムの変換よりも複数の小さな機能が優れています)
- 組み合わせの概念、他の関数への入力として出力する機能は、プロセスが組み合わせで、右から左へ渡さ。
// 定义组合函数
const compose = (...fns) => (val) => fns.reverse().reduce((acc, fn) => fn(acc), val);
// 定义一系列小的函数
const splitStr = str => str.split(' ');
const getArrLen = arr => arr.length;
// 组合并输出
const getWords = compose(getArrLen, splitStr);
getWords('I am LengChui!') // 3
复制代码
- 組み合わせは、各パラメータの機能のみを受け取ります。あなたは、パラメトリック機能で、よりリアリティが必要な場合は、カレーとpartilを使用することができます。
- パイプと同じ機能の組み合わせが、左から右へ。ただ、個人的な好み。
// 定义管道函数
const pipe = (...fns) => val => fns.reduce((acc, fn) => fn(acc), val);
// 可以达到和compose同样的输出
const getWords2 = pipe(splitStr, getArrLen);
getWords2('I am LengChui!')
复制代码
- パイプ及び誤差の合成機能を配置
// 定义identity函数,将接收到的参数打印输出
const identity = arg => {
console.log(arg);
return arg;
}
// 在需要的地方直接插入即可
const getWords2 = pipe(splitStr, identity, getArrLen);
复制代码
ファンクタ
- ファンクタは、各オブジェクト値のトラバースに新しいオブジェクトを生成するマップ機能を実現する通常のオブジェクトです。要するに、それは容器の保有のファンクタ値です。
// 函子其实就是一个持有值的容器
const Containter = function (value) {
this.value = value;
}
// of静态方法用来生成Container实例,省略new而已
Containter.of = function (value) {
return new Containter(value)
}
Containter.prototype.map = function (fn) {
return Containter.of(fn(this.value));
}
// 可以简化一下(省略of)
const Containter = function (value) {
if (!(this instanceof Containter)) return new Containter(value);
this.value = value;
}
Containter.prototype.map = function (fn) {
return Containter.of(fn(this.value));
}
// es6写法
class Containter {
constructor (value) {
this.value = value;
}
// 静态方法of返回类实例
static of(value) {
return new Containter(value);
}
// map函数允许Container持有的值调用任何函数
map(fn) {
return Containter.of(fn(this.value));
}
}
console.log(Containter.of(123).map(e => 2 * e)
.map(e => e + 1).value
) // 247
复制代码
- たぶんファンクタは、エラー処理のための強力な抽象化です。
// 定义Maybe函子,和普通函子的区别在于map函数
// 会对传入的值进行null和undefined检测
class Maybe {
constructor(value) {
this.value = value;
}
static of(value) {
return new Maybe(value);
}
isNothing() {
return this.value === undefined || this.value === null;
}
// 检测容器持有值是否为null或undefined,如果是则直接返回null
map(fn) {
if (this.isNothing()) return Maybe.of(null);
return Maybe.of(fn(this.value));
}
}
// 可以保证程序在处理值为null或undefinede的时候不至于崩溃
// eg1
const res = Maybe.of(null).map(e => null).map(e => e - 10);
console.log(res);
// eg2
const body = {data: [{type: 1}]};
const typeAdd = e => {
e.type && e.type ++;
return e;
}
const res = Maybe.of(body).map(body => body.data)
.map(data => data.map(typeAdd))
console.log(res)
复制代码
多分ファンクタは簡単にすべてのnullとundefinedエラーを処理することができます
が、エラーがどこから来るかもしれないファンクタは知りません。
- どちらのファンクタは、支店の拡大の問題を解決することができます
// ES6方式实现
class EitherParent {
constructor(value) {
this.value = value;
}
// 子类会继承该方法
static of(value) {
// new this.prototype.constructor使得返回的实例是子类
// 这样子类调用of方法后才可以继续链式调用
return new this.prototype.constructor(value);
}
}
class Nothing extends EitherParent {
constructor(...arg) {
super(...arg)
}
map() {
return this;
}
}
class Some extends EitherParent {
constructor(...arg) {
super(...arg)
}
map(fn) {
return new Some(fn(this.value))
}
}
// 实例使用
function getData() {
try {
throw Error('error'); // 模拟出错
return Some.of({code: 200, data: {a: 13}})
} catch (error) {
return Nothing.of({code: 404, message: 'net error'})
}
}
console.log(getData().map(res => res.data).map(data => data.a))
复制代码
どちらのかもしれないとファンクタを指摘しています
モナドファンクタ
- モナドはファンクタ所有チェーンの方法であり、
- 同様MAYBEファンクタ、
class Monad {
constructor(value) {
this.value = value;
}
static of(value) {
return new Monad(value);
}
isNothing() {
return this.value === undefined || this.value === null;
}
// 用于扁平化MayBe函子,但是只扁平一层
join() {
if (this.isNothing()) return Monad.of(null);
return this.value;
}
// 直接将map后的join扁平操作封装在chain方法中
// 使得更简便的调用
chain(fn) {
return this.map(fn).join();
}
map(fn) {
if (this.isNothing()) return Monad.of(null);
return Monad.of(fn(this.value));
}
}
console.log(Monad.of(123).chain(e => {
e += 1;
return e;
}))
复制代码
コンテンツ参照
- ES6関数型プログラミングポータルクラシック
成功を続けると、さらにその日
私は唖然とハンマー、フロントエンドの愛好家でした。
批判と交流を歓迎。
ます。https://juejin.im/post/5cfcf8f7e51d455a694f94e1で再現