最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

独家理解:JavaScript函数式编程

来源:懂视网 责编:小采 时间:2020-11-27 20:22:58
文档

独家理解:JavaScript函数式编程

独家理解:JavaScript函数式编程:JavaScript 函数式编程是一个存在了很久的话题,但似乎从 2016 年开始,它变得越来越火热。这可能是因为 ES6 语法对于函数式编程更为友好,也可能是因为诸如 RxJS (ReactiveX) 等函数式框架的流行。 看过许多关于函数式编程的讲解,但是其中大部分是停留在理
推荐度:
导读独家理解:JavaScript函数式编程:JavaScript 函数式编程是一个存在了很久的话题,但似乎从 2016 年开始,它变得越来越火热。这可能是因为 ES6 语法对于函数式编程更为友好,也可能是因为诸如 RxJS (ReactiveX) 等函数式框架的流行。 看过许多关于函数式编程的讲解,但是其中大部分是停留在理

  闭包的概念比较抽象,相信大家都或多或少知道、用到这个特性

  那么闭包到底能给我们带来什么好处?

  先来看一下如何创建一个闭包:

// 创建一个闭包
function makeCounter() {
 let k = 0;

 return function() {
 return ++k;
 };
}

const counter = makeCounter();

console.log(counter()); // 1
console.log(counter()); // 2

  makeCounter 这个函数的代码块,在返回的函数中,对局部变量 k ,进行了引用,导致局部变量无法在函数执行结束后,被系统回收掉,从而产生了闭包。而这个闭包的作用就是,“保留住“ 了局部变量,使内层函数调用时,可以重复使用该变量;而不同于全局变量,该变量只能在函数内部被引用。

  换句话说,闭包其实就是创造出了一些函数私有的 ”持久化变量“。

  所以从这个例子,我们可以总结出,闭包的创造条件是:

  1. 存在内、外两层函数

  2. 内层函数对外层函数的局部变量进行了引用

  闭包的用途

  闭包的主要用途就是可以定义一些作用域局限的持久化变量,这些变量可以用来做缓存或者计算的中间量等等。

// 简单的缓存工具
// 匿名函数创造了一个闭包
const cache = (function() {
 const store = {};
 
 return {
 get(key) {
 return store[key];
 },
 set(key, val) {
 store[key] = val;
 }
 }
}());

cache.set('a', 1);
cache.get('a'); // 1

  上面例子是一个简单的缓存工具的实现,匿名函数创造了一个闭包,使得 store 对象 ,一直可以被引用,不会被回收。

  闭包的弊端

  持久化变量不会被正常释放,持续占用内存空间,很容易造成内存浪费,所以一般需要一些额外手动的清理机制。

  高阶函数

接受或者返回一个函数的函数称为高阶函数

  听上去很高冷的一个词汇,但是其实我们经常用到,只是原来不知道他们的名字而已。JavaScript 语言是原生支持高阶函数的,因为 JavaScript 的函数是一等公民,它既可以作为参数又可以作为另一个函数的返回值使用。

  我们经常可以在 JavaScript 中见到许多原生的高阶函数,例如 Array.map , Array.reduce , Array.filter

  下面以 map 为例,我们看看他是如何使用的

  map (映射)

映射是对集合而言的,即把集合的每一项都做相同的变换,产生一个新的集合

  map 作为一个高阶函数,他接受一个函数参数作为映射的逻辑

// 数组中每一项加一,组成一个新数组


// 一般写法
const arr = [1,2,3];
const rs = [];
for(const n of arr){
 rs.push(++n);
}
console.log(rs)


// map改写
const arr = [1,2,3];
const rs = arr.map(n => ++n);

  上面一般写法,利用 for...of 循环的方式遍历数组会产生额外的操作,而且有改变原数组的风险

  而 map 函数封装了必要的操作,使我们仅需要关心映射逻辑的函数实现即可,减少了代码量,也降低了副作用产生的风险。

  柯里化(Currying)

给定一个函数的部分参数,生成一个接受其他参数的新函数

  可能不常听到这个名词,但是用过 undescore 或 lodash 的人都见过他。

  有一个神奇的 _.partial 函数,它就是柯里化的实现

// 获取目标文件对基础路径的相对路径

// 一般写法
const BASE = '/path/to/base';
const relativePath = path.relative(BASE, '/some/path');


// _.parical 改写
const BASE = '/path/to/base';
const relativeFromBase = _.partial(path.relative, BASE);

const relativePath = relativeFromBase('/some/path');

  通过 _.partial ,我们得到了新的函数 relativeFromBase ,这个函数在调用时就相当于调用 path.relative ,并默认将第一个参数传入 BASE ,后续传入的参数顺序后置。

  本例中,我们真正想完成的操作是每次获得相对于 BASE 的路径,而非相对于任何路径。柯里化可以使我们只关心函数的部分参数,使函数的用途更加清晰,调用更加简单。

  组合(Composing)

将多个函数的能力合并,创造一个新的函数

  同样你第一次见到他可能还是在 lodash 中,compose 方法(现在叫 flow)

// 数组中每个单词大写,做 Base64

// 一般写法 (其中一种)
const arr = ['pen', 'apple', 'applypen'];
const rs = [];
for(const w of arr){
 rs.push(btoa(w.toUpperCase()));
}
console.log(rs);


// _.flow 改写
const arr = ['pen', 'apple', 'applypen'];
const upperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa));
console.log(upperAndBase64(arr));

  _.flow 将转大写和转 Base64 的函数的能力合并,生成一个新的函数。方便作为参数函数或后续复用。

 自己的观点

  我理解的 JavaScript 函数式编程,可能和许多传统概念不同。我并不只认为 高阶函数 算函数式编程,其他的诸如普通函数结合调用、链式结构等,我都认为属于函数式编程的范畴,只要他们是以函数作为主要载体的。

  而我认为函数式编程并不是必须的,它也不应该是一个强制的规定或要求。与面向对象或其他思想一样,它也是其中一种方式。我们更多情况下,应该是几者的结合,而不是局限于概念。

文档

独家理解:JavaScript函数式编程

独家理解:JavaScript函数式编程:JavaScript 函数式编程是一个存在了很久的话题,但似乎从 2016 年开始,它变得越来越火热。这可能是因为 ES6 语法对于函数式编程更为友好,也可能是因为诸如 RxJS (ReactiveX) 等函数式框架的流行。 看过许多关于函数式编程的讲解,但是其中大部分是停留在理
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top