30 天精通 RxJS (03): Functional Programming 通用函數

瞭解 Functional Programming 的通用函數,能讓咱們寫出更簡潔的代碼,也能幫助咱們學習 RxJS。javascript

這是【30天精通 RxJS】的 03 篇,若是還沒看過 02 篇能夠往這邊走:
[30 天精通 RxJS (02): Functional Programming 基本觀念]
github.com/ShaofeiZi/3…java

讀者可能會很好奇,咱們的主題是 RxJS 爲何要特別講 Functional Programming 的通用函數呢? 實際上,RxJS 核心的 Observable 操做觀念跟 FP 的數組操做是極爲相近的,只學會如下幾個基本的方法跟觀念後,會讓咱們以後上手 Observable 簡單不少!react

今天的代碼比較多,你們能夠直接看視頻!webpack

ForEach

forEach 是 JavaScript 在 ES5 後,原生就有支援的方法。git

本來咱們可能要透過 for loop 取出數組中的每個元素github

var arr = ['Jerry', 'Anna'];

for(var i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}複製代碼

如今能夠直接透過數組的 forEach 取出每個元素。web

var arr = ['Jerry', 'Anna'];

arr.forEach(item => console.log(item));複製代碼

forEach 是 FP 操做數組的基本方法,咱們能夠用這個方法來實例下面三個咱們今天要講的重點分別爲 map, filter, concatAll。gulp

Map

試着把 newCourseList 每一個元素的 { id, title } 塞到新的數組 idAndTitlePairs數組

var newCourseList = [
    {
        "id": 511021,
        "title": "React for Beginners",
        "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
        "rating": 5
    },
    {
        "id": 511022,
        "title": "Vue2 for Beginners",
        "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
        "rating": 5
    },
    {
        "id": 511023,
        "title": "Angular2 for Beginners",
        "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
        "rating": 5
    },
    {
        "id": 511024,
        "title": "Webpack for Beginners",
        "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
        "rating": 4
    }
], idAndTitle = [];

newCourseList.forEach((course) => {
    idAndTitle.push({ id: course.id, title: course.title });
});複製代碼

雖然咱們成功的把 newCourseList 轉成 idAndTitlePairs,但這樣的寫法仍是顯得有點太複雜了,咱們能夠用更抽象化的方式來完成。app

上面咱們練習到 newCourseList 轉換成一個新的數組 idAndTitlePairs,這個轉換的過程其實就是兩件事

  • 遍歷 newCourseList 全部的元素
  • 把每一個元素的預期值給到新的數組

把這個過程抽象化成一個方法 map,如下是簡化的基本思路:

  1. 咱們會讓每一個 數組 都有一個 map 方法
  2. 這個方法會讓使用者自訂傳入一個 callback function
  3. 這個 callback function 會回傳使用者預期的元素

雖然 ES5 以後原生的 JavaScript 數組有 map 方法了,但但願讀者本身寫一遍,能幫助理解。

// 咱們但願每個數組都有 map 這個方法,因此咱們在 Array.prototype 擴充 map function
Array.prototype.map = function(callback) {
  var result = []; // map 最後必定會返回一個新數組,因此咱們先建立一個新數組

  this.forEach(function(element, index) {
      // this 就是呼叫 map 的數組
      result.push(callback(element, index));
      // 執行使用者定義的 callback, callback 會回傳使用者預期的元素,因此咱們把它 push 進新數組
  })

  return result;
}複製代碼

這裏用到了 JavaScript 的 prototype chain 以及 this 等觀念,能夠看此視頻瞭解!

到這裏咱們就實例完成 map 的方法了,讓咱們來試試這個方法吧!

var idAndTitle = newCourseList
                 .map((course) => {
                     return { id: course.id, title: course.title };
                 });複製代碼

能夠看到咱們的代碼更加的簡潔!

Filter

若是咱們但願過濾一個數組,留下數組中咱們想要的元素,併產生一個新的數組,要怎麼作呢?
先讓咱們用 forEach 完成!

讓咱們過濾出 rating 值是 5 的元素

var ratingIsFive = [];

newCourseList.forEach((course) => {
    if(course.rating === 5) {
        ratingIsFive.push(course);
    }
});複製代碼

一樣的咱們試着來簡化這個過程,首先在這個轉換的過程當中,咱們作了兩件事:

  1. 遍歷 newCourseList 中的全部元素
  2. 判斷元素是否符合條件,符合則加到新的數組中
Array.prototype.filter = function(callback) {
    var result = [];
    this.forEach((item, index) => {
        if(callback(item, index))
            result.push(item);
    });
    return result;
}複製代碼

試試這個方法

var ratingIsFive = newCourseList
                   .filter((course) => course.rating === 5);複製代碼

會發現咱們的代碼又變簡單了,接着咱們試着把 filter, map 串起來。

若是我想要取出全部 rating 是 5 的全部 course title

var ratingIsFive = newCourseList
                   .filter((course) => course.rating === 5)
                   .map(course => course.title);複製代碼

ConcatAll

有時候咱們會遇到組出一個二維數組,但咱們但願數組是一維的,問題以下:

假如咱們要取出 courseLists 中全部 rating 爲 5 的課程,這時可能就會用到兩個 forEach

var user = {
  id: 888,
  name: 'JerryHong',
  courseLists: [{
    "name": "My Courses",
    "courses": [{
      "id": 511019,
      "title": "React for Beginners",
      "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
      "tags": [{ id: 1, name: "JavaScript" }],
      "rating": 5
    }, {
      "id": 511020,
      "title": "Front-End automat workflow",
      "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
      "tags": [{ "id": 2, "name": "gulp" }, { "id": 3, "name": "webpack" }],
      "rating": 4
    }]
  }, {
    "name": "New Release",
    "courses": [{
      "id": 511022,
      "title": "Vue2 for Beginners",
      "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
      "tags": [{ id: 1, name: "JavaScript" }],
      "rating": 5
    }, {
      "id": 511023,
      "title": "Angular2 for Beginners",
      "coverPng": "https://res.cloudinary.com/dohtkyi84/image/upload/v1481226146/react-cover.png",
      "tags": [{ id: 1, name: "JavaScript" }],
      "rating": 4
    }]
  }]
};

var allCourseIds = [];

user.courseLists.forEach(list => {
  list.courses
    .filter(item => item.rating === 5)
    .forEach(item => {
      allCourseIds.push(item)
    })
})複製代碼

能夠看到上面的代碼,咱們用了較爲低階的操做來解決這個問題,咱們剛剛已經試着用抽象化的方式實例了 map 跟 filter,那咱們一樣也可以定義一個方法用來 攤平二維數組。

讓咱們來加入一個 concatAll 方法來簡化這段代碼吧!
concatAll 要作的事情很簡單,就是把一個二維數組轉成一維。

Array.prototype.concatAll = function() {
  var result = [];

  // 用 apply 完成
  this.forEach((array) => {
    result.push.apply(result, array);
  });

  // 用兩個 forEach 完成
  // this.forEach((array) => {
  // array.forEach(item => {
  // result.push(item)
  // })
  // });

  // 用 ES6 spread 完成
  // this.forEach((array) => {
  // result.push(...array);
  // })

  return result;
};複製代碼

一樣的咱們用前面定要好的 courseLists 來試試 concatAll 吧!

var allCourseIds = user.courseLists.map(list => {
    return list.courses.filter(course => course.rating === 5)
}).concatAll()複製代碼

這邊出一個比較難的題目,你們能夠想一想看要怎麼解

var courseLists = [{
  "name": "My Courses",
  "courses": [{
    "id": 511019,
    "title": "React for Beginners",
    "covers": [{
      width: 150,
      height: 200,
      url: "http://placeimg.com/150/200/tech"
    }, {
      width: 200,
      height: 200,
      url: "http://placeimg.com/200/200/tech"
    }, {
      width: 300,
      height: 200,
      url: "http://placeimg.com/300/200/tech"
    }],
    "tags": [{
      id: 1,
      name: "JavaScript"
    }],
    "rating": 5
  }, {
    "id": 511020,
    "title": "Front-End automat workflow",
    "covers": [{
      width: 150,
      height: 200,
      url: "http://placeimg.com/150/200/arch"
    }, {
      width: 200,
      height: 200,
      url: "http://placeimg.com/200/200/arch"
    }, {
      width: 300,
      height: 200,
      url: "http://placeimg.com/300/200/arch"
    }],
    "tags": [{
      "id": 2,
      "name": "gulp"
    }, {
      "id": 3,
      "name": "webpack"
    }],
    "rating": 5
  }]
}, {
  "name": "New Release",
  "courses": [{
    "id": 511022,
    "title": "Vue2 for Beginners",
    "covers": [{
      width: 150,
      height: 200,
      url: "http://placeimg.com/150/200/nature"
    }, {
      width: 200,
      height: 200,
      url: "http://placeimg.com/200/200/nature"
    }, {
      width: 300,
      height: 200,
      url: "http://placeimg.com/300/200/nature"
    }],
    "tags": [{
      id: 1,
      name: "JavaScript"
    }],
    "rating": 5
  }, {
    "id": 511023,
    "title": "Angular2 for Beginners",
    "covers": [{
      width: 150,
      height: 200,
      url: "http://placeimg.com/150/200/people"
    }, {
      width: 200,
      height: 200,
      url: "http://placeimg.com/200/200/people"
    }, {
      width: 300,
      height: 200,
      url: "http://placeimg.com/300/200/people"
    }],
    "tags": [{
      id: 1,
      name: "JavaScript"
    }],
    "rating": 5
  }]
}];

/* var result = courseList 不得直接使用索引 covers[0],請用 concatAll, map, filter, forEach 完成 result 結果爲 [ { id: 511019, title: "React for Beginners", cover: "http://placeimg.com/150/200/tech" }, { id: 511020, title: "Front-End automat workflow", cover: "http://placeimg.com/150/200/arch" }, { id: 511022, title: "Vue2 for Beginners", cover: "http://placeimg.com/150/200/nature" }, { id: 511023, title: "Angular2 for Beginners", cover: "http://placeimg.com/150/200/people" }, ] */複製代碼

練習連結: JSBin | JSFiddle

這題有點難,你們能夠想一想看,我把答案寫在這裏了!

若是你們還想作更多的練習能夠到這個連結:reactivex.io/learnrx/

這個連結是 Jafar 大神爲他的 RxJS workshop 所作的練習網站!

今日小結

今天講了 FP 操做數組的三個通用函數 forEach, map, filter,以及咱們本身定義的一個方法叫 concatAll。這幾天咱們把學習 RxJS 的前置觀念跟知識基本上都講完了,明天咱們就開始進入 RxJS 的重點核心 Observable 嘍!

相關文章
相關標籤/搜索