用 Map 替代循環的那些好處

1217339405 8年前發布 | 12K 次閱讀 JavaScript開發 JavaScript

來自: http://web.jobbole.com/84916/

如今,在程序員學習過程中基本都會發現一個叫 map 的函數。在發現 map 函數之前,你可能都會使用 for 循環來處理需要多次執行某一行為的場景。一般情況下,在這個循環過程中都會伴隨一些數據變換。

命令式

例如,你團隊的銷售人員交給你一個很長的電郵地址列表。這些郵箱地址獲取的時候并沒有經過很好地校驗,以至于有些是大寫的,有些是小寫的,還有一些是大小寫混合的。使用 for 循環進行數據處理的代碼如下:

JavaScript

var mixedEmails = ['JOHN@ACME.COM', 'Mary@FooBar.com', 'monty@spam.eggs'];

function getEmailsInLowercase(emails) { var lowercaseEmails = [];

for (var i = 0; i < emails.length; i++) { lowercaseEmails.push(emails[i].toLowerCase()); }

return lowercaseEmails; }

var validData = getEmailsInLowercase(mixedEmails);</pre>

var mixedEmails = ['JOHN@ACME.COM', 'Mary@FooBar.com', 'monty@spam.eggs'];
 
function getEmailsInLowercase(emails) {
  var lowercaseEmails = [];
 
  for (var i = 0; i < emails.length; i++) {
    lowercaseEmails.push(emails[i].toLowerCase());
  }
 
  return lowercaseEmails;
}
 
var validData = getEmailsInLowercase(mixedEmails);
</div>

這樣的做法是有效的,但卻把一個實際上簡單常見的操作變得復雜。使用 for 循環的函數牽扯了很多不必要的細節。一些痛點如下:

  • 需要讓程序創建一個臨時列表來存儲復制的郵件地址值。
  • 需要讓程序先計算列表的長度,以此為次數訪問一遍列表。
  • 需要讓程序創建一個計數器用來記錄當前訪問的位置。
  • 需要告訴程序計數的方向,但順序在這里并不重要。

這是命令式的編程方法。我們似乎在口述給電腦該怎么做這件事。

困惑

為了使之前的代碼更加清晰整潔,我們改用 map 函數。在任何 map 函數的說明文檔中,我們都會看到諸如 “array”、“each”、“index”之類的詞。這表明,我們可以把 map 當做不那么“隆重”的 for 循環使用,事實上也是可行的。現在來修改一下之前的代碼:

JavaScript

var mixedEmails = ['JOHN@ACME.COM', 'Mary@FooBar.com', 'monty@spam.eggs'];

function getEmailsInLowercase(emails) { var lowercaseEmails = [];

emails.map(function(email) { lowercaseEmails.push(email.toLowerCase()); });

return lowercaseEmails; }

var validData = getEmailsInLowercase(mixedEmails);</pre>

var mixedEmails = ['JOHN@ACME.COM', 'Mary@FooBar.com', 'monty@spam.eggs'];
 
function getEmailsInLowercase(emails) {
  var lowercaseEmails = [];
 
  emails.map(function(email) {
    lowercaseEmails.push(email.toLowerCase());
  });
 
  return lowercaseEmails;
}
 
var validData = getEmailsInLowercase(mixedEmails);
</div>

這樣寫不僅能用,而且代碼比使用 for 循環更加清楚。除了代碼量更少,我們也不用再告訴程序去記錄索引和遍歷列表的方向了。

然而,這還不夠好。這樣寫還是命令式的編程。我們還是指揮的太多。實際上我們牽涉了很多不必要的細節,似乎都在領著程序的手走每一步。

聲明式

我們需要改變我們關于數據變換的思考方式。我們不需要想著:“電腦啊,我需要你取出列表中第一個元素,然后把它轉換成小寫,再存儲到另一個列表中,最后返回這個列表”。相反,我們應該這樣想:“電腦,我這有一個混合了大小寫的郵件地址列表,而我需要一個全是小寫的郵件地址列表,這是一個能夠進行小寫轉換的函數”。

JavaScript

var mixedEmails = ['JOHN@ACME.COM', 'Mary@FooBar.com', 'monty@spam.eggs'];

function downcase(str) { return str.toLowerCase(); }

var validData = mixedEmails.map(downcase);</pre>

var mixedEmails = ['JOHN@ACME.COM', 'Mary@FooBar.com', 'monty@spam.eggs'];
 
function downcase(str) {
  return str.toLowerCase();
}
 
var validData = mixedEmails.map(downcase);
</div>

毫無疑問,這種寫法更易懂,同時這才是程序 的本質:把你的想法告訴其他人,這個人可能是別的程序員或未來的你。上面的代碼在說“有效的數據是使用小寫轉換函數映射后的郵箱列表”。

使用類似這樣的高級方式傳遞想法是 函數式編程 的核心原則,而我們就在這樣做。將單一功能、易于理解的簡單部分組合起來,由此構建復雜程序。

這樣的寫法還有些額外的好處。下表的排序不分先后:

  • 小寫轉換函數提供了最簡化的接口:單值輸入,單值輸出。
  • 能夠改動的地方不多,所以邏輯更易于理解,也易于測試,而且不易出錯。
  • 邏輯單一,所以易于復用,并且與其他函數能夠組合出更復雜的功能。
  • 沿這樣的聲明式編程路線走的話,代碼量會顯著縮小。

雖然給 map 的第一個參數傳入匿名函數很常見,還是建議把函數提出來并合理命名。這能夠幫你記錄下寫此函數的意圖,這樣別的開發者就能通過函數名了解功能而不用再費勁分析代碼了。

瀏覽器支持情況

ECMAScript 5 標準 定義了原生的 map() 方法,所以 瀏覽器兼容性 較好。如果你想在 IE 9 之前的版本中使用,就需要引入一個 polyfill 或使用 UnderscoreLodash 之類的庫了。

性能表現

極大多數情況下,在實際編碼中 map 函數和 for 循環之間沒有明顯的性能差距。 for 循環 稍快一些 ,但如果你不是在寫圖形或物理引擎的話,這點差距沒必要去考慮,當然即使如此,除非能夠確定這些性能的提升對你有幫助,否則用這種方式去優化意義不大。

總結

將邏輯分成單一功能的方法并應用于數據結 構上,這種編程方法會讓你的代碼更準確、魯棒和易于理解。我們的理念就是盡可能通用,通用能夠幫助代碼重用。學習這種思考方法,不僅能幫助你提高 Javascript 水平,也能體現在其他多數編程語言上,例如 Ruby 和 Haskell。

所以,下一次當你要使用 for 循環時,重新考慮一下。記住,你要處理的數據并不一定是普通的數組,你可以去處理對象,取出它的值,再使用函數去映射,最后整理出結果數組。你甚至可以利用 Underscore 之類的庫去在 保留鍵值的情況下 map 一個對象

你還能想出其他使用 map 函數的創新方式么?去嘗試吧,你將目睹代碼的縮減過程。

</div>

 本文由用戶 1217339405 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!