您好,登錄后才能下訂單哦!
作者:Sequoia McDowell
2016年01月16日
ES6來啦!隨著越來越多的代碼庫和思潮引領者開始在他們的代碼中使用ES6,以往被認為是“僅需了解”的ES6特性變成了必需的代碼常識。這不僅僅是新的語法學習 - 在許多范例中, ES6中新的語言特性可以讓在ES5中寫起來非常麻煩的表達變得更加簡單,進而鼓勵了新表達方式的使用。下面我們將關注一個這樣簡潔表達的使用范例:ES6中的箭頭函數如何使高階函數的書寫更加簡便。
高階函數是至少具有以下兩種功能之一的函數:
使用一個或多個函數作為實參
返回一個函數作為結果
本文的目的并不是說服你立即采用這種新方式,盡管筆者非常鼓勵你嘗試使用!本文旨在讓你熟悉這種表達方式,這樣當你在遇到其他人基于ES6寫的代碼庫時,不會如筆者當初一樣看著這些陌生代碼撓頭不解。如果你在此之前需要先復習一下箭頭語法的知識,請先查閱這篇文章。
希望你熟悉有返回值的箭頭函數:
const square = x => x * x;
但是下面這兩行代碼是什么意思?
const has = p => o => o.hasOwnProperty(p); const sortBy = p => (a, b) => a[p] > b[p];
“p 返回 o 返回 o.hasOwnProperty…”這句代碼是什么意思?我們能這樣用嗎?
為了說明帶箭頭的高階函數的書寫規則,讓我們一起來看一個經典示例:加法函數。在ES5中會這樣寫:
function add(x){ return function(y){ return y + x; }; } var addTwo = add(2); addTwo(3); // => 5 add(10)(11); // => 21
我們的加法函數輸入x,返回了一個輸入y返回值是y + x的函數。那我們應該如何用箭頭函數表達這個函數呢?已知...
箭頭函數定義是一個表達式,并且
箭頭函數隱式返回單一表達式結果
那么我們所要做的就是讓另一個箭頭函數作為我們箭頭函數的函數體,因此表達方式如下:
const add = x => y => y + x; // outer function: x => [inner function, uses x] // inner function: y => y + x;
現在我們就可以通過一個與變量x相關的返回值創建內部函數:
const add2 = add(2);// returns [inner function] where x = 2 add2(4); // returns 6: exec inner with y = 4, x = 2 add(8)(7); // 15
我們所寫的加法函數并不是超級有用,但是它可以說明一個外函數如何輸入一個以x為變量的參數函數,并在它返回的函數中引用此函數。
假如你在github上看到了一個ES6代碼庫,并且遇到了下面這樣的代碼:
const has = p => o => o.hasOwnProperty(p); const sortBy = p => (a, b) => a[p] > b[p]; let result; let users = [ { name: 'Qian', age: 27, pets : ['Bao'], title : 'Consultant' }, { name: 'Zeynep', age: 19, pets : ['Civelek', 'Muazzam'] }, { name: 'Yael', age: 52, title : 'VP of Engineering'} ]; result = users .filter(has('pets')) .sort(sortBy('age'));
這些代碼又是什么意思?我們叫它箭頭原型的排列和濾波方法,每一種方法都使用一個單功能參數,但是我們會調用返回函數的函數來篩選和排序,而不是編寫表達式去做這些。
讓我們來看一看,在下列每種情況下返回函數的表達方式。
result = users .filter(x => x.hasOwnProperty('pets')) //pass Function to filter .sort((a, b) => a.age > b.age); //pass Function to sort
result = users .filter(has('pets')) //pass Function to filter .sort(sortBy('age')); //pass Function to sort
在每個實例中,過濾過程都是通過檢查對象是否含有名為“pets”的屬性值的函數執行的。
它的有效性出于以下幾個原因:
它減少了重復代碼
它簡化了代碼的可重用性
它提升了代碼含義的清晰度
想象一下我們只想過濾出有寵物和有職位的用戶,我們可以在其中添加另一個函數:
result = users .filter(x => x.hasOwnProperty('pets')) .filter(x => x.hasOwnProperty('title')) ...
這里的重復只是徒增雜亂:它的簡明度沒有提升,只是寫了更多代碼妨礙視線。下面是可實現同樣功能的使用了has函數的代碼:
result = users .filter(has('pets')) .filter(has('title')) ...
這段代碼更加短小精悍且易于書寫,還可以減少打錯字的情況。筆者同時認為這段代碼大大增加了閱讀清晰度,一眼就能讀出代碼的含義。
至于函數重用性,如果需要在很多地方過濾出有寵物的用戶或者有職位的用戶,你可以創建如下函數,并按需重用:
const hasPets = has('pets'); const isEmployed = has('title'); const byAge = sortBy('age'); let workers = users.filter(isEmployed); let petOwningWorkers = workers.filter(hasPets); let workersByAge = workers.sort(byAge);
我們也可以使用已給出的函數來獲得單返回值,并不僅僅是用于過濾數組:
let user = {name: 'Assata', age: 68, title: 'VP of Operations'}; if(isEmployed(user)){ // true //do employee action } hasPets(user); // false has('age')(user); //true
讓我們來寫一個能檢查對象擁有一個有固定值的主鍵的過濾函數。has函數可檢查主鍵,然而我們需要知道兩項值(主鍵和鍵值),而不是一項,來同時檢查值。下面請看一種方法:
//[p]roperty, [v]alue, [o]bject: const is = p => v => o => o.hasOwnProperty(p) && o[p] == v; // broken down: // outer: p => [inner1 function, uses p] // inner1: v => [inner2 function, uses p and v] // inner2: o => o.hasOwnProperty(p) && o[p] = v;
此處,叫做“is”的函數可執行以下三件事:
取一個屬性名并且返回一個函數,該函數……
取一個函數值返回一個函數,該函數……
取一個對象,并檢測該對象是否有特定函數值的特定屬性,最終返回一個布爾值。
下面是一個使用is函數來過濾用戶的示例:
const titleIs = is('title'); // titleIs == v => o => o.hasOwnProperty('title') && o['title'] == v; const isContractor = titleIs('Contractor'); // isContractor == o => o.hasOwnProperty('contractor') && o['title'] == 'Contractor'; let contractors = users.filter(isContractor); let developers = users.filter(titleIs('Developer')); let user = {name: 'Viola', age: 50, title: 'Actress', pets: ['Zak']}; isEmployed(user); // true isContractor(user); // false
看一眼下面這個函數,記下你弄清楚函數意義的時間:
const i = x => y => z => h(x)(y) && y[x] == z;
現在請再看一下同功能僅在書寫風格中略微不同的函數:
const is = prop => val => obj => has(prop)(obj) && obj[prop] == val;
可見,書寫一行越簡潔越好的函數的編碼趨勢是以犧牲可讀性為代價的。請克制自己這樣做的沖動!簡短卻無意義的變量名的確看起來很漂亮,但是讓人難以理解函數的功能。起一個包含多個詞的有意義的變量名和函數名,其實是在幫你自己和同事理解函數功能。
如果你想以年齡降序排序而不是升序排列,該怎么做呢?或者說查找不是雇員的用戶該怎么做呢?我們需要去寫一個新的functionssortByDesc 或者notHas程序嗎?當然不用!我們可以將已有的返回布爾值的函數封裝起來,用一個反轉其布爾值的函數來實現上述功能,反之亦然。
//take args, pass them thru to function x, invert the result of x const invert = x => (...args) => !x(...args); const noPets = invert(hasPets); let petlessUsersOldestFirst = users .filter(noPets) .sort(invert(sortBy('age')));
函數式編程在編程界的勢頭越來越強勁,而ES6使得在JavaScript中采用這種編程思想更加容易。如果你在JavaScript編程過程中還沒遇到過函數式編程風格的代碼,你很可能在接下來的幾個月里就能遇到。這意味著即便你不喜歡這種風格,理解它的基礎知識也是非常重要的,有的知識在這里已經提到過了。希望這篇文章提出的概念能夠幫你在實際遇到ES6代碼時準備好前提知識,更希望它能鼓勵你嘗試這種編程風格!
OneAPM 助您輕松鎖定 .NET 應用性能瓶頸,通過強大的 Trace 記錄逐層分析,直至鎖定行級問題代碼。以用戶角度展示系統響應速度,以地域和瀏覽器維度統計用戶使用情況。想閱讀更多技術文章,請訪問 OneAPM 官方博客。
本文轉自 OneAPM 官方博客
原文鏈接:https://strongloop.com/strongblog/higher-order-functions-in-es6easy-as-a-b-c/
+
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。