目录

一. 函数式编程范式

1.1 编程范式定义

1.2 函数式编程范式特点

1.3 函数式编程范式基本概念

1.4 章节学习指南

二. 头等函数

2.1 一等公民 和 高阶函数

2.2 函数作为参数

2.3 函数作为返回值

2.4 常用高阶函数模拟

2.4.1 map

2.4.2 every

2.4.3 some

2.4.4 find

2.4.5 findIndex

三. 闭包

3.1 闭包定义及特性

3.2 闭包 Demo

3.3 Chrome 开发者工具 > Sources

四. 纯函数

4.1 何谓纯函数?

4.2 纯函数库 Lodash

4.3 纯函数的好处

4.4 关于 “副作用”

五. 柯里化

5.1 何谓柯里化?

5.2 Lodash.curry(func)

5.3 模拟 Lodash.curry()

六. 函数组合

6.1 何谓函数组合?

6.2 Lodash 组合函数

6.3 模拟 Lodash.flowRight()

6.4 函数组合中的结合律

6.5 函数组合的 Demo

七. 函数式编程的思考总结


一. 函数式编程范式

1.1 编程范式定义

  • 函数式编程(Functional Programming: FP)是一种 编程范式(指计算机编程中的典范模式或方法,就是一种思维方式),属于结构化编程,用于描述数据(函数)之间的映射关系
  • 注意:函数式编程中的函数,不是指程序中的函数(方法),而是数学中的函数(映射关系),如:y = f(x),指 x 和 y 之间的关系
  • 常见的编程范式有:过程化(命令式)编程、面向对象编程、声明式编程等

  1. 过程化编程:最原始的传统编程,将问题抽象为一系列步骤,然后通过编程方式将这些步骤转换为程序指令集,这些指令集按照一定顺序排列;常见的过程化编程语言有机器语言、汇编语言、BASIC、C、FORTRAN 等;过程化语言特别适合解决线性(或者说按部就班)的算法问题。
  2. 面向对象编程:将待解决问题抽象为面向对象的程序中的对象,利用封装使每个对象都拥有个体的身份;程序就是成堆的对象,彼此通过信息的传递,请求其它对象进行工作;面向对象包括三个基本概念:封装性、继承性、多态性。常见的面向对象语言有 Java、C、C++、JavaScript。
  3. 声明式编程:以数据结构的形式来表达程序执行的逻辑。只需要定义好该如何处理数据,不需要指定具体实现;SQL 语句就是最明显的一种声明式编程的例子。

  • 现代编程语言的发展趋势:支持多种范式,如 C#、Java 8+、Kotlin、ES6+

  • 编程范式 vs 设计模式
  1. 编程范式:是指从事软件工程的一类典型的编程风格(此概念好比 “战略” ),体现编写程序的人如何看待程序设计的 “哲学观”
  2. 设计模式:设计模式是软件设计中常见问题的典型解决方案(此概念好比 “战术” ),是解决一系列实际问题的 “方法学”

1.2 函数式编程范式特点

  • 代码简洁,函数式编程使用了大量的函数,减少了代码的重复;
  • 接近自然语言,易于理解;
  • 函数是 “第一等公民”:函数与其他数据类型一样,可以赋值给其它变量,也可以作为参数,也可以作为返回值;
  • 函数式编程会使用较多的 闭包 和 高阶函数;
  • 没有 “副作用”,方便与代码管理和单元测试:副作用指函数内部与外部互动(比如意外修改全局变量),这意味着函数要保持独立,所有功能就是返回一个新的值,没有其他⾏为,尤其是不得修改外部变量的值;
  • 引用透明:函数的运行不依赖于外部变量或 ”状态”,只依赖于输入的参数,任何时候只要参数相同,引用函数的返回值相同
let result = (1 + 2)* 3 - 4; // 普通表达式let result = subtract(multiply(add(1, 2), 3), 4); // 函数式编程

1.3 函数式编程范式基本概念

  • 函数式编程的思维方式?
  • 把现实世界的事物和事物之间的联系(映射关系)抽象到程序世界(对运算过程进行抽象)
  • 函数式编程是用来描述数据(函数)之间的映射
// 比如买单价为0.5元的白菜,买了两斤,需要支付多少块钱(白菜与货币的联系)
let money = multiply(0.5, 2); // 即两斤白菜 -> 1元(money)

  • 程序的本质?
  • 根据输入,通过某种运算获得相应的输出

  • y = f(x) 函数关系?
  • x → f(映射) → y,x 与 y 是一一对应的
  • 映射(函数)关系【一一对应】:
  • 非映射(函数)关系【非一一对应】:

  • 何谓纯函数?
  • 相同输入 始终要得到 相同输出

1.4 章节学习指南

  • 函数式编程范式,是一种对 程序编程思维 的一种概论,函数式编程范式的 具体实现 是通过柯里化(第 5 章)、函数组合(第 6 章)、函子等完成的
  • 在学习如何实现函数式编程范式前,需要先了解三个小知识点:头等函数(第 2 章),闭包(第 3 章),纯函数(第 4 章)

二. 头等函数

2.1 一等公民 和 高阶函数

  • 为什么说 函数是一等公民呢?因为在某些编程语言中,函数不能实现:
  1. 函数可以存储在变量中
  2. 函数可以作为参数(2.2.1)
  3. 函数可以作为返回值(2.2.2)
  • JavaScript 中,函数可以享受以上几种待遇,所以函数是一等公民

  • 何谓高阶函数?
  • 可以操作函数的函数,被称为 高阶函数,有两种情况:
  1. 参数是一个函数
  2. 返回值是一个函数

2.2 函数作为参数

  • 将函数作为参数,最常见的应用是:各种回调函数
// 遍历(模拟数组的 forEach 方法)
function forEach(arr, fn) {for (let i = 0; i < arr.length; i++) {fn(arr[i], i); // 将每一项传入回调 fn 处理}
}// 筛选,返回符合条件的元素组成的新数组
function filter(arr, fn) {const results = [];for (const item of arr) {if (fn(item)) {results.push(item);}}return results;
}// 遍历(模拟数组的 forEach 方法) - 举例
const colors = ["#FF0000", "#00FF00", "blue"];
forEach(colors, (item, index) => {console.log(index + 1, item);
});// 筛选,返回符合条件的元素组成的新数组 - 举例
console.log(filter(colors, (item) => item.length === 7));
  • 体现出的高阶函数意义:高阶函数用来抽象 通用 的问题,这里抽象了遍历的逻辑

2.3 函数作为返回值

  function makeFn() {const msg = "hello function";return function () {console.log(msg);};}// makeFn() 执行后返回一个匿名函数,赋值给 fnconst fn = makeFn();fn(); // 等价于 makeFn()()// 模拟 lodash 中的 once函数,让函数仅执行一次// 举个栗子:支付,不管用户点击多少次按钮,都只执行一次function once(func) {let done = false; // 定义一个状态 done,判断是否已执行支付return function () {if (!done) {done = true; // 更改闭包作用域中的 done 为已支付func.apply(this, arguments);}};}const pay = once((money) => {// 传入一个函数,通过输出模拟支付过程和结果console.log(`支付${money}元`);});pay(20); // 支付20元pay(30);pay(40);
  • 体现出的高阶函数意义:高阶函数用来屏蔽细节,只关注目标;
  • 比如不用在乎怎么遍历,只需要关注遍历后怎么处理数据;
  • 比如不用在乎用户是否会多次点击,只需要关注支付后的操作;

2.4 常用高阶函数模拟

2.4.1 map

  • map() 函数:返回被传入函数处理后的数组
  function map(arr, fn) {const res = [];for (const val of arr) {res.push(fn(val)); // 将回调fn()处理好的元素存入新数组}return res;}let arr = [1, 2, 3, 4, 5];arr = map(arr, (item) => item * item);console.log(arr); // [ 1, 4, 9, 16, 25 ]

2.4.2 every

  • every() 函数:检测数组中的所有元素,是否每个都符合指定条件
  function every(arr, fn) {let res = true; // 定义一个flagfor (const val of arr) {res = fn(val); // fn判断if (!res) {// 只要有一个元素不满足,就结束循环break;}}return res;}const arr1 = [1, 2, 3, 4, 5];const arr2 = [4, 5, 6, 7];const res1 = every(arr1, (item) => item > 3);console.log(res1); // falseconst res2 = every(arr2, (item) => item > 3);console.log(res2); // true

2.4.3 some

  • some() 函数:检测数组中的所有元素,是否至少有一个元素满足条件
  function some(arr, fn) {let res = false; // 定义一个flagfor (const val of arr) {res = fn(val); // fn判断if (res) {// 只要有一个元素满足,就结束循环break;}}return res;}const arr1 = [1, 2, 3, 4, 5];const arr2 = [1, 3, 5, 7];const res1 = some(arr1, (item) => item % 2 === 0);console.log(res1); // trueconst res2 = some(arr2, (item) => item % 2 === 0);console.log(res2); // false

2.4.4 find

  • find() 函数:返回数组中 满足传入函数的 第一个元素的值;如果未找到,则返回 undefined
  function find(arr, fn) {for (const item of arr) {if (fn(item)) {// 找到满足条件的第一个元素return item;}}return undefined; // 未找到返回undefined}const arr1 = [1, 2, 3, 4, 5];const res1 = find(arr1, (item) => item % 2 === 0);console.log(res1); // 2const res2 = find(arr1, (item) => item === 8);console.log(res2); // undefined

2.4.5 findIndex

  • findIndex() 函数:找到满足条件的第一个元素,返回其位置;如果未找到,则返回 -1
  function findIndex(arr, fn) {for (let i = 0; i < arr.length; i++) {if (fn(arr[i])) {// 找到满足条件的第一个元素位置return i;}}return -1; // 未找到返回-1}const arr1 = [1, 2, 3, 4, 5];const res1 = findIndex(arr1, (item) => item % 2 === 0);console.log(res1); // 1const res2 = findIndex(arr1, (item) => item === 8);console.log(res2); // -1

三. 闭包

3.1 闭包定义及特性

  • 闭包的本质:
  • 函数执行时,会被放到执行栈上,函数执行完毕后,会从执行栈上删除,但是堆上作用域成员因为被外部引用而不能被释放,因此内部函数依然可以访问到作用域的成员;

  • 闭包的特性:
  1. 函数嵌套函数(高阶函数)
  2. 函数内部可以引用函数外部的参数和变量
  3. 参数和变量不会被垃圾回收机制回收

3.2 闭包 Demo

function makePower(power) {return function (number) {return number ** power; // number为底数,power为指数};
}// 平方:number**2
const power2 = makePower(2);
// 立方:number**3
const power3 = makePower(3);console.log(power2(5));
console.log(power3(4));===============================================================function makeSalary(base) {return function (performance) {return base + performance;};
}// 底层打工人
const level1 = makeSalary(1000);
// 高级打工人
const level2 = makeSalary(10000);console.log(level1(100)); // 1100
console.log(level2(30000)); // 40000
  • 以 第二个 Demo 为例:
  • level2 是 makeSalary(10000) 返回的一个函数,也就是:function (performance) { return 10000 + performance }
  • level2(30000) 也就相当于:function (30000) { return 10000 + 30000 }

3.3 Chrome 开发者工具 > Sources

  • Call Stack(函数调用栈)
  • Scope(作用域) : Global(var 全局) 、 Local(局部) 、 Closure(闭包) 、 Script(let 作用域)

四. 纯函数

4.1 何谓纯函数?

  • 相同的输入 永远会得到 相同的输出,没有任何副作用

  • 举个栗子:
  • slice 和 splice 分别:纯函数和不纯函数
  • slice 返回数组中的指定部分,不会改变原数组
  • splice 对数组进行操作返回该数组,会改变原数组
// 纯函数 slice(start, end)
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.slice(0, 3)); // [ 1, 2, 3 ]
console.log(numbers.slice(0, 3)); // [ 1, 2, 3 ]
console.log(numbers.slice(0, 3)); // [ 1, 2, 3 ]// 不纯函数 splice(index, howmany, ...items)
console.log(numbers.splice(0, 3)); // [ 1, 2, 3 ]
console.log(numbers.splice(0, 3)); // [ 4, 5 ]
console.log(numbers.splice(0, 3)); // []// 最简单的纯函数示例
function getSum(a, b) {return a + b;
}console.log(getSum(1, 2)); // 3
console.log(getSum(1, 2)); // 3
console.log(getSum(1, 2)); // 3

4.2 纯函数库 Lodash

  • 官方网站:Lodash
  • Lodash 是一个纯函数的功能库,它提供了对数组、数字、对象、字符串、函数等操作的很多方法
const _ = require("lodash");const arr = ["Tom", "Jon", "Kate"];
console.log(_.first(arr));
console.log(_.toUpper(_.last(arr)));
console.log(_.reverse(arr));_.each(arr, (item, index) => {console.log(item, index);
});const value = [];
_.isEmpty(value); // 判断一个value 是否是 empty(null, [], {}....)

4.3 纯函数的好处

  • 可缓存:因为纯函数相同的输入永远会等到相同的输出,所以可以把纯函数结果缓存【比如 Lodash.memoize(func)】
  • 可测试:纯函数让测试更加方便,对单元化测试很友好
  • 并行处理:在多线程环境下,并行操作共享的内存数据,很可能会出现意外情况;纯函数不需要访问共享的内存数据,所以在并行环境下可以任意运行纯函数(Web Worker)

  • 关于纯函数的好处 —— “可缓存” 的栗子:
// lodash 记忆函数
const _ = require("lodash");function getArea(r) {console.log(`执行getArea计算,r = ${r}`);return Math.PI * r * r;
}
// 这里使用lodash中的记忆函数
const getAreaWithMemory = _.memoize(getArea);console.log(getAreaWithMemory(4));
console.log(getAreaWithMemory(4)); // 不会再次计算
console.log(getAreaWithMemory(5));===========================================================// js模拟 memoize 方法的实现
function memoize(f) {const cache = {};return function () {const key = JSON.stringify(arguments);cache[key] = cache[key] || f.apply(f, arguments);return cache[key];};
}const getAreaWithMemory = memoize(getArea);
console.log(getAreaWithMemory(4));
console.log(getAreaWithMemory(4));
console.log(getAreaWithMemory(5));

4.4 关于 “副作用”

  • 纯函数:指 相同的输入永远会得到相同的输出,而且没有可观察的 副作用
  • 由概念得:副作用让函数变的不纯,纯函数根据相同的输入返回相同的输出,如果函数依赖于外部的状态,就无法保证输出相同,也就会带来副作用
  • 举个栗子:
// 不纯的函数
let min = 18;
function checkAge(age) {return age >= min; // 依赖外部的 min 状态
}// 纯函数
function checkAge2(age) {let min = 18; // 硬编码,可通过闭包或者柯里化解决return age >= min;
}

  • 副作用的来源:配置文件、数据库、获取用户的输入
  • 所有的外部交互都有可能带来副作用;

  • 副作用会使方法通用性下降、不适合扩展;
  • 副作用不可能完全禁止,尽可能控制他们在可控范围内发生;
// 有副作用
let result = 0;
function sum() {const a = $(".input-1").val();const b = $(".input-2").val();result = a + b;
}
// <button onclick="sum()">求和</button>==========================================================// 避免副作用
function sum(a, b) {return a + b;
}
$("button").bind("click", () => {const a = $(".input-1").val();const b = $(".input-2").val();result = sum(a, b);
});
// <button>求和</button>

五. 柯里化

5.1 何谓柯里化?

  • 柯里化(curry:咖喱)把 多元函数 转化成 一元函数
  • 解释:当一个函数有多个参数的时候,先传递一部分参数调用它(这部分参数以后永远不变),然后返回一个新的函数接受剩余的参数,直到参数接收完毕,才返回结果
  • 举个栗子:
// 闭包的方式解决(简单的柯里化)
function checkAge(min) {return function (age) {return age >= min;};
}// es6写法
let checkAge = (min) => (age) => age >= min;

  • 柯里化的好处:
  1. 可以实现 给函数传递较少的参数,得到一个已经记住某些固定参数的新函数
  2. 这是一种对函数参数的 “缓存”(闭包)
  3. 让函数变的更灵活,让函数的粒度更小
  4. 可以把 多元函数 转换成 一元函数,可以组合使用函数,产生强大的功能

5.2 Lodash.curry(func)

  • 文档:lodash.curry | Lodash 中文文档 | Lodash 中文网
  • 功能:创建一个函数,该函数接受 func 的参数。如果 fn 所需的参数都被提供了,则执行 func 并返回结果,否则 继续返回该函数,并等待接收剩余的参数【类似于 5.1 中,柯里化的解释】
  • 注意:传参先后顺序不能变
  • 举个栗子:
const _ = require("lodash");function getSum(a, b, c) {return a + b + c;
}
const curried = _.curry(getSum);console.log(curried(2, 3, 4)); // 9
console.log(curried(2)(3)(4)); // 9
console.log(curried(2)(3, 4)); // 9
console.log(curried(2, 3)(4)); // 9
  • 再举个栗子:
const _ = require("lodash");
const match = _.curry((reg, str) => {return str.match(reg);
});// 匹配所有数字
const hasSpace = match(/\s+/g); // 返回已经接受了 reg参数 的函数// 匹配所有空白字符
const hasNumber = match(/\d+/g); // 返回已经接受了 reg参数 的函数console.log(hasSpace("helloword")); // null 返回已经接受了 reg/str参数 的函数console.log(hasNumber("123213 123")); // ["123213", "123"]============================================================// 再扩展:筛选数组中指定条件的元素
const filter = _.curry((func, array) => {return array.filter(func);
});console.log(filter(hasSpace, ["Tea Meow", "Tea_Meow"])); // ["Patrick Jun"]

5.3 模拟 Lodash.curry()

  • 小知识点:func = (a, b, c, d, e) => {},那么 func.length = 5;
function curry(func) {return function curriedFn(...args) {// 判断形参和实参的个数if (args.length < func.length) {return function () {// 将...args与...arguments拼接传递给curriedFnreturn curriedFn(...args, ...arguments);};}return func(...args);};
}function getSum(a, b, c) {return a + b + c;
}const curried = curry(getSum);
console.log(curried(2, 3)(4)); // 9
console.log(curried(2)(3, 4)); // 9


六. 函数组合

6.1 何谓函数组合?

  • 函数组合(compose):如果一个函数 要经过多个函数处理,才能得到最终值,就可以把 中间过程的函数 合并成一个函数
  • 解释:函数就像是数据的管道,函数组合就是把这些管道连接起来,让数据穿过多个管道形成最终结果
  • 函数组合默认是 —— 从右到左执行
  • 函数组合后只接受一个参数
function reverse(array) {return array.reverse();
}function first(array) {return array[0];
}// 将任意两个函数组合 - 模拟组合函数 从右向左 执行
function compose(f, g) {return function (value) {return f(g(value));};
}// 将最上面的两个函数组合起来
const last = compose(first, reverse);console.log(last([1, 2, 3, 4])); // 4

6.2 Lodash 组合函数

  • Lodash 中的组合函数:flow() / flowRight(),他们都可以组合多个函数,可以参考 6.1 中的解释
  • flow():从左到右运行
  • flowRight():从右到左运行,使用的更多
const _ = require("lodash");const reverse = (arr) => arr.reverse();
const first = (arr) => arr[0];
const toUpper = (s) => s.toUpperCase();const f = _.flowRight(toUpper, first, reverse);console.log(f(["one", "two", "three"])); // THREE

6.3 模拟 Lodash.flowRight()

  • 小知识点:数组中的 reduce() 方法 —— 对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值
function compose(...args) {return function (val) {return args.reverse().reduce((acc, fn) => {return fn(acc);}, val);};
}// ES6
// const compose = (...args) => (val) => args.reverse().reduce((acc, fn) => fn(acc), val);


6.4 函数组合中的结合律

  • 函数的组合,需要满足结合律
  • 举个栗子:compose(f,g,h),先把 f、g 组合在一起,或者先把 g、h 组合在一起;他们再和 f 结合在一起,上述两种方法的返回结果应该一样
console.log(compose(compose(f, g), h) == compose(f, compose(g, h))); //true
console.log(compose(f, g, h) == compose(f, compose(g, h))); //trueconst _ = require("lodash");
// 下面三种写法结果运行一样
const f = _.flowRight(_.flowRight(_.toUpper, _.first), _.reverse); // 前两个组合
const f1 = _.flowRight(_.toUpper, _.flowRight(_.first, _.reverse)); // 后两个组合
const f2 = _.flowRight(_.toUpper, _.first, _.reverse); // 不组合console.log(f(["one", "two", "three"]) === f1(["one", "two", "three"])); // true
console.log(f(["one", "two", "three"]) === f2(["one", "two", "three"])); // true
console.log(f1(["one", "two", "three"]) === f2(["one", "two", "three"])); // true

6.5 函数组合的 Demo

  • 将 NEVER SAY DIE 转换为 never-say-die
  • 思路:小写 toLowerCase,分割 split,组合 join
  • 'NEVER SAY DIE'.toLowerCase().split(' ').join('-');
const _ = require("lodash");// 第一步:_.toLower()// 第二步:_.split()
// 因为我们需要传入 str 变量,所以 str 放在最后面传入,以下同理
const split = _.curry((symbol, str) => _.split(str, symbol));// 第三步:._join
const join = _.curry((symbol, array) => _.join(array, symbol));// log 用来检测数据管道中,哪部分值有错误
const log = (v) => {console.log(v);// 继续返回值给下一个fnreturn v;
};const f = _.flowRight(join("-"), log, split(" "), log, _.toLower);
console.log(f("NEVER SAY DIE")); // never-say-die========================================================================// // 考虑到数据管道很长的情况,如果多次log,打印的数据不够直观,于是改造log
// const _ = require('lodash');// const trace = _.curry((tag, v) => {
//   console.log(tag, v);
//   return v;
// });// const split = _.curry((symbol, str) => _.split(str, symbol));
// const join = _.curry((symbol, arr) => _.join(arr, symbol));
// const f = _.flowRight(join('-'), trace('after split:'), split(' '), trace('after toLower:'), _.toLower);
// console.log(f('NEVER SAY DIE'));

七. 函数式编程的思考总结

  1. 函数式编程是一种强调 以函数使用为主 的软件开发风格;
  2. 纯函数指:没有副作用的函数,相同的输入有相同的输出;
  3. 在函数式编程里面,将多个不同函数组合是非常非常非常重要的思想;
  4. 函数式编程将函数视为积木,通过一些高阶函数来提高代码的模块化和可重用性;
  5. 柯里化是”因式分解“,将参数分解开;函数组合是”结合律“,将函数组合使用
  • 扩展:lodash/fp、函子...
  • 参考文章:函数式编程范式 - 思路大前端团队 (ths.js.org)

JavaScript 函数式编程范式相关推荐

  1. JavaScript函数式编程入门经典

    一个持续更新的github笔记,链接地址:Front-End-Basics,可以watch,也可以star. 此篇文章的地址:JavaScript函数式编程入门经典 正文开始 什么是函数式编程?为何它 ...

  2. SegmentFault 技术周刊 Vol.16 - 浅入浅出 JavaScript 函数式编程

    函数式编程(Functional Programming),一看这个词,简直就是学院派的典范. 以至于从 Lisp 的创世,到 Scheme.Haskell.Clean.Erlang.Miranda. ...

  3. 一文带你了解JavaScript 函数式编程

    前言 函数式编程在前端已经成为了一个非常热门的话题.在最近几年里,我们看到非常多的应用程序代码库里大量使用着函数式编程思想. 本文将略去那些晦涩难懂的概念介绍,重点展示在 JavaScript 中到底 ...

  4. 一文带你了解JavaScript函数式编程

    摘要: 函数式编程入门. 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 函数式编程在前端已经成为了一个非常热门的话题.在最近几年里,我们看到非常多的应用程序代码库里大量使用着函 ...

  5. SegmentFault 技术周刊 Vol.16 - 浅入浅出 JavaScript 函数式编程 1

    函数式编程(Functional Programming),一看这个词,简直就是学院派的典范. 以至于从 Lisp 的创世,到 Scheme.Haskell.Clean.Erlang.Miranda. ...

  6. 大前端 - 函数式编程范式

    函数式编程范式 为什么要学习函数式编程 函数式编程是一个非常古老的概念,早于第一台计算机的诞生. 那我们为什么要学函数式编程 ? 函数式编程时随着React的流行受到越来越多的关注 Vue3 也开始拥 ...

  7. JavaScript函数式编程(一)\(二)\(三)

    JavaScript函数式编程(一) Starkwang 前端工程师@腾讯 / Node.js Collaborator 一.引言 说到函数式编程,大家可能第一印象都是学院派的那些晦涩难懂的代码,充满 ...

  8. JavaScript 函数式编程(一)

    零.前言 说到函数式编程,想必各位或多或少都有所耳闻,然而对于函数式的内涵和本质可能又有些说不清楚. 所以本文希望针对工程师,从应用(而非学术)的角度将函数式编程相关思想和实践(以 JavaScrip ...

  9. JavaScript函数式编程(纯函数、柯里化以及组合函数)

    JavaScript函数式编程(纯函数.柯里化以及组合函数) 目录 JavaScript函数式编程(纯函数.柯里化以及组合函数) 前言 1.纯函数 1.1.纯函数的概念 1.2.副作用 1.3.纯函数 ...

最新文章

  1. 树莓派/PC实现实时摄像头数据共享(Python—OpenCV)
  2. 只知道用它打印了Hello World,除此之外你了解多少呢?
  3. 与您相约.NET Conf China 2020! 新进2位重磅嘉宾
  4. 二分法在数组内查找数c语言,C++二分法在数组中查找关键字的方法
  5. 利用异步I/O复制文件及详解
  6. jQuery 1.3 正式版发布
  7. ndk+opencv安装+各种错误分析(新版安装,编译不需要Cygwin 和Sequoyah了)
  8. java中equals方法的用法以及==的用法(转)
  9. TCP 慢启动 拥塞控制
  10. 超定方程的最小二乘解的三维几何解释
  11. Hibernate事务与并发问题处理(乐观锁与悲观锁)
  12. Centos5.5服务器ROOT密码破解
  13. html链接安装包,磁力宅资源链接地址
  14. FCPX字幕插件、转场插件、效果插件、MG动画、调色插件、AE模板、AE插件、PR插件
  15. 求三点外接圆圆心公式
  16. plc应用与c语言编程区别,PLC编程与应用入门基础知识
  17. 特斯拉为什么要“干掉”保险丝和继电器?
  18. Spark大数据平台
  19. 朱晔的互联网架构实践心得S2E1:业务代码究竟难不难写?
  20. 微信重磅功能更新!加好友按人数收费,视频号付费订阅、微信版“知乎”来了...

热门文章

  1. 高中学python有用吗_人生苦短,请用Python!学习Python的四大理由
  2. 外汇天眼:新手炒外汇止损止盈技巧有哪些?如何设置止损止盈?
  3. 夫妻分居期间一方的债务应该如何处理?
  4. 游戏的策略性和技巧性
  5. L2-031 深入虎穴 (25 分)(DFS 代码有详细解析)
  6. 关键词提取-基于python实现tf-idf
  7. 最近火爆了的对话ChatGPT
  8. 小评最近很火的BERT模型
  9. 解决win7报计算机内存不足的问题
  10. 圆弧中点坐标值求解(二维平面三维空间)(3.1增加三维部分)-①