盲猜一个:若是你有看过前端
这两篇文章,你必定会对 JS 的【函数】有更多兴趣!编程
若是你没兴趣?那我走?设计模式
皮一下,很舒服~ 没错!JS 就是轻量级的函数式编程!数组
拆解一下这句话,品味一下~markdown
本瓜将借助《JavaScript 轻量级函数式编程》一书带领你先透析它的落脚点函数式编程,而后再看看 JS 为何被称为是 “轻量的”!闭包
此篇是《JS如何函数式编程?看这就够了!》系列的第一篇,点赞👍关注👀持续追踪!app
函数式编程(FP),不是一个新的概念,它几乎贯穿了整个编程史。直到最近几年,函数式编程才成为整个开发界的主流观念。编程语言
函数式编程有完善且清晰的原则,一旦咱们知道这些原则,咱们将能更加快速地读懂代码,定位问题。这是为何函数式编程重要的缘由!
好比:你可能写过一些命令式的代码,像 if 语句和 for 循环这样的语句。这些语句旨在精确地指导计算机如何完成一件事情。而声明式代码,以及咱们努力遵循函数式编程原则所写出的代码,更专一于描述最终的结果。
函数式编程以另外一种方式来思考代码应该如何组织才能使数据流更加明显,并能让读者很快理解你的思想。
记住,你编写的每一行代码以后都要有人来维护,这我的多是你的团队成员,也多是将来的你。
函数式编程不是仅仅用 function 这个关键词来编程,就像面向对象编程不只仅是用了对象就算是。
函数的真正意义是什么?
回到最初的起点,咱们心中的函数必定是这样的:
f(x) = 2x2 + 3
,这是数学上真正的函数。那这和函数式编程有什么关系呢?
函数的本质是【映射】。以一个优雅的方式来描述一组值和另外一组值的映射关系,即函数的输入值与输出值之间的关联关系。
在编程中,它或许有许多个输入值,或许没有。它或许有一个输出值( return 值),或许没有。
若是你计划使用函数式编程,你应该尽量多地使用函数,而不是程序。你全部编写的 function 应该接收输入值,而且返回输出值。(这么作的缘由是多方面的,后续会一一介绍)
这里,输入值就是函数传参,输出值就是return
的东西。(若是你没有 return 值,或者你使用 return;,那么则会隐式地返回 undefined 值。)
function foo(x) {
if (x > 10) return x + 1;
var y = x / 2;
if (y > 3) {
if (x % 2 == 0) return x;
}
if (y > 1) return y;
return x;
}
复制代码
foo(2) 返回什么? foo(4) 返回什么? foo(8), foo(12) 呢?
function foo(x) {
var retValue;
if (retValue == undefined && x > 10) {
retValue = x + 1;
}
var y = x / 2;
if (y > 3) {
if (retValue == undefined && x % 2 == 0) {
retValue = x;
}
}
if (retValue == undefined && y > 1) {
retValue = y;
}
if (retValue == undefined) {
retValue = x;
}
return retValue;
}
复制代码
这样写会更容易理解吗?
在每一个 retValue 能够被设置的分支, 这里都有个守护者以确保 retValue 没有被设置过才执行。(?)
相比在函数中提前使用 return,咱们更应该用经常使用的流控制( if 逻辑 )来控制 retValue 的赋值。到最后,咱们 return retValue。
用 return 来实现流控制,会创造更多的隐含意义。
// 经过一个函数修改变量 y 的值
var y;
function foo(x) {
y = (2 * Math.pow( x, 2 )) + 3;
}
foo( 2 );
y;
复制代码
可是咱们也能够这样写:
function foo(x) {
return (2 * Math.pow( x, 2 )) + 3;
}
var y = foo( 2 );
y;
复制代码
后一个版本更有优点吗?
答案是确定的:有!
后一个版本中的 return 表示一个显式输出,而前者的 y 赋值是一个隐式输出。
一般,开发人员喜欢显式模式而不是隐式模式。
为何说后者 return 出来的就是显式的?而前者的 y 赋值是隐式的?
这个例子能够给你答案:
function sum(list) {
var total = 0;
for (let i = 0; i < list.length; i++) {
if (!list[i]) list[i] = 0; // list 使用了 nums 的引用,不是对 [1,3,9,..] 的值复制,而是引用复制。
total = total + list[i];
}
return total;
}
var nums = [ 1, 3, 9, 27, , 84 ];
sum( nums ); // 124
复制代码
这段代码,除了 return 的输出,还有没有其它输出可能改变到函数外部参数 nums 的值?
是有的!就是在注释的一行,咱们无心中改变了 nums 。
console.log(nums) // [1, 3, 9, 27, 0, 84]
复制代码
JS 对数组、对象和函数都使用引用和引用复制,咱们能够很容易地从函数中建立输出,即便是无意的。
这个隐式函数输出在函数式编程中有一个特殊的名称:反作用。
没有反作用的函数也有一个特殊的名称:纯函数,这个概念十分重要,后面对有更多讨论!
一个函数若是能够接受或返回一个甚至多个函数,它被叫作高阶函数。
其中最强大的就是:【闭包】。
咱们将在的后续举例中大量使用闭包。它多是全部函数式编程中最重要的基础。
此处举一小例:
假设你须要将两个值相加,一个你已经知道,另外一个还须要后面才能知道,你可使用闭包来记录第一个输入值:
function makeAdder(x) {
return function sum(y){
return x + y;
};
}
//咱们已经分别知道做为第一个输入的 10 和 37
var addTo10 = makeAdder( 10 );
var addTo37 = makeAdder( 37 );
// 紧接着,咱们指定第二个参数
addTo10( 3 ); // 13
addTo10( 90 ); // 100
addTo37( 13 ); // 50
复制代码
这种在连续函数调用中指定输入,是函数式编程中很是广泛的形式。
它可分为两类:偏函数应用和柯里化。后续会展开。
咱们提倡要用具名函数,而不是匿名函数,这更有利于咱们语义化代码,好比getPreferredName(..)
,操做意图很明确,而且能够很好的回溯问题,防止出现 (anonymous function)
。
可是 => 箭头函数除外,箭头函数仍是得有效利用。
=> 箭头函数使人兴奋的地方在于它几乎彻底遵循函数的数学符号,特别是像 Haskell 这样的函数式编程语言。它能简化、优化代码片断中的空间。
JavaScript 中的 this 绑定规则是真的难记,好消息是咱们将把 this 丢弃掉,不去理会它。
这样作的内核缘由是:this 是函数的一个隐式的输入参数。前面咱们提到一般,开发人员喜欢显式模式而不是隐式模式。,这样的隐式输入违背了咱们的原则。
函数是强大的!
咱们学习函数式编程的所有理由是为了书写更具可读性的代码。
程序中,函数不只仅是一个语句或者操做的集合,而是须要一个或多个输入(理想状况下只需一个!)和一个输出。开发人员喜欢显式输入输出而不是隐式输入输出。
函数内部的函数能够取到闭包外部变量,并记住它们以备往后使用。这是全部程序设计中最重要的概念之一,也是函数式编程的基础。
要警戒匿名函数,特别是 => 箭头函数。虽然在编程时用起来很方便,可是会对增长代码阅读的负担。
别用 this 敏感的函数。这不须要理由。
我是掘金安东尼,公众号【掘金安东尼】,关注前端,也关注生活,持续输出ing......