函数式编程

函数式编程

介绍

函数式编程是一种基于函数计算的软件开发方法。像数学一样,函数在编程中通过输入产生输出。你可以通过多种方式组合基本功能来构建越来越复杂的程序。

函数式编程遵循几个核心原则:

  • 独立于程序状态或全局变量,只依赖于传递给它们的参数进行计算
  • 限制更改程序状态,避免更改保存数据的全局对象
  • 对程序的副作用尽量小

函数式编程式将程序分成小的、可测试的部分,这里介绍 JavaScript 中函数式编程的基本原则。

术语

首先,我们将介绍一些术语:

Callbacks是被传递到另一个函数中调用的函数。你应该已经在其他函数中看过这个写法,例如在filter中,回调函数告诉 JavaScript 以什么规则过滤数组。

函数就像其他正常值一样,可以赋值给变量、传递给另一个函数,或从其它函数返回,这种函数叫做first class函数。在 JavaScript 中,所有函数都是first class函数。

将函数为参数或返回值的函数叫做higher order函数。

当函数传递给另一个函数或从另一个函数返回时,那些传入或返回的函数可以叫做lambda

Array.splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

函数式编程注意

  • 函数不要改变外部变量(避免避免突变和副作用)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 全局变量
    var fixedValue = 4;

    function incrementer () {
    return fixedValue + 1; //这行
    }

    var newValue = incrementer(); // 应等于 5
    console.log(fixedValue); // 应打印 4
  • 传递参数,避免外部依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 全局变量
    var fixedValue = 4;

    function incrementer (value) { // < ==
    return value + 1;
    }

    var newValue = incrementer(fixedValue); // 应等于 5
    console.log(fixedValue); // 应打印 4

    目前为止,我们已经看到了函数式编程的两个原则:

    1) 不要更改变量或对象——创建新变量和对象,并在需要时从函数返回它们。

    2) 声明函数参数——函数内的任何计算仅取决于参数,而不取决于任何全局对象或变量。

添加一个元素到数组最后

1
2
3
4
5
function add (arr,bookName) {
let newArr = [...arr];
newArr.push(bookName);
return newArr;
}

注意:不能直接返回return newArr.push(bookName);

因为这样函数返回值是添加元素后数组的长度。

1
2
3
4
//添加末尾元素精简版:
function add (arr,bookName) {
return [...arr,bookName];
}
1
2
3
4
5
//删除特定元素:
function remove (arr,bookName) {
if(arr.indexOf(bookName)!=-1)
return arr.filter((e)=>e !== bookName);
}

常用函数

  • Array.push() 添加元素到数组末尾,返回添加后数组长度
  • Array.splice() 添加、删除数组元素
  • Array.filter() 返回满足回调函数的数组元素组成的新数组(即筛除不符合的元素)
  • Array.map() 返回每个数组元素调用回调函数后的结果组成的新数组,只要回调函数不改变原数组则原数组不变。
  • Array.slice() 返回一个新数组,不会修改原数组 用slice代替splice可以避免改变原数组

使用map方法代替for循环遍历数组是个好方法。

  • Array.concat() 返回拼接后的数组

  • Array.sort() 对数组进行排序,如果该方法时没有参数,将按字母顺序也就是按照字符编码的顺序进行排序,数字排序加参数,a,b 返回a-b则升序,返回a<b则降序。

    • sort方法会产生改变原始数组中元素顺序的副作用。换句话说,它会改变数组的位置。避免这种情况的一种方法是先将空数组连接到正在排序的数组上(记住concat返回一个新数组),再用sort方法。

      1
      2
      3
      4
      5
      6
      7
      8
      var globalArray = [5, 6, 3, 2, 9];
      function nonMutatingSort(arr) {
      var newArr=[];
      return newArr.concat(arr).sort(function(a,b){
      return a-b;
      });
      }
      nonMutatingSort(globalArray);
  • split() 分割字符串成为数组 ,下面是以字符串的符号作为分割点进行分割

    1
    2
    3
    4
    5
    function splitify(str) {
    return str.split(/[\ |\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\||\\|\[|\]|\{|\}|\;|\:|\"|\'|\,|\<|\.|\>|\/|\?]/g) //过于复杂
    //应该简化 return str.split(/\W/)
    }
    console.log(splitify("Hello World,I-am code")); //["Hello","world","I","am","code"]
  • join() 将数组组合成字符串 把数组中的所有元素放入一个字符串,并通过指定的分隔符参数进行分隔。

  • every() 检查每个数组元素是否符合条件。

    1
    2
    3
    4
    5
    6
    function checkPositive(arr) {
    return arr.every(function(val){
    return val>0;
    });
    }
    checkPositive([1, 2, 3, -4, 5]);//返回true
  • some() 检查数组中是否有元素符合给定的条件

    1
    2
    3
    4
    5
    var numbers = [10, 50, 8, 220, 110, 11];
    numbers.some(function(currentValue) {
    return currentValue < 10;
    });
    // 返回 true

字符串转换为URL片段

许多内容管理站点(CMS)为了让添加书签更简单,会将帖子的标题添加到 URL 上。举个例子,如果你写了一篇标题为 “Stop Using Reduce” 的帖子,URL很可能会包含标题字符串的某种形式 (如:”…/stop-using-reduce”)

转换字符串title带有连字符号的 URL 版本。

要求:

输入包含空格和标题大小写单词的字符串

输出字符串,单词之间的空格用连字符(-)替换

输出应该是小写字母

输出不应有任何空格

1
2
3
4
5
6
var globalTitle = "Winter Is Coming";

function urlSlug(title) {
return title.trim().split(/\W+/g).join("-").toLowerCase();
}
var winterComing = urlSlug(globalTitle); // 为 "winter-is-coming"

函数柯里化

arity是函数所需的形参的数量。函数Currying意思是把接受多个arity的函数变换成接受单一arity的函数。

换句话说,就是重构函数让它接收一个参数,然后返回接收下一个参数的函数,依此类推。

1
2
3
4
5
6
7
8
9
10
11
12
//Un-curried function
function unCurried(x, y) {
return x + y;
}

// 柯里化函数
function curried(x) {
return function(y) {
return x + y;
}
}
curried(1)(2) // 返回 3
1
2
3
4
5
6
7
8
9
10
function add(x) {
return function(y)
{
return function(z)
{
return x+y+z;
}
}
}
add(10)(20)(30);//60