【underscore 源碼解讀】Array Functions 相關源碼拾遺 & 小結

Why underscore

最近開始看 underscore.js 源碼,並將 underscore.js 源碼解讀 放在了個人 2016 計劃中。javascript

閱讀一些著名框架類庫的源碼,就好像和一個個大師對話,你會學到不少。爲何是 underscore?最主要的緣由是 underscore 簡短精悍(約 1.5k 行),封裝了 100 多個有用的方法,耦合度低,很是適合逐個方法閱讀,適合樓主這樣的 JavaScript 初學者。從中,你不只能夠學到用 void 0 代替 undefined 避免 undefined 被重寫等一些小技巧 ,也能夠學到變量類型判斷、函數節流&函數去抖等經常使用的方法,還能夠學到不少瀏覽器兼容的 hack,更能夠學到做者的總體設計思路以及 API 設計的原理(向後兼容)。php

以後樓主會寫一系列的文章跟你們分享在源碼閱讀中學習到的知識。java

歡迎圍觀~ (若是有興趣,歡迎 star & watch~)您的關注是樓主繼續寫做的動力瀏覽器

Main

很快,Array Functions 部分到了尾聲,今天來作個了(xiao)結。框架

underscore 給數組(以及 arguments,這裏特別說明下,underscore 的數組擴展方法,一樣適用於 arguments)增長了 20 個擴展方法,值得一提的是,不少有意思的方法,好比 map,shuffle 等,都被放在了 Collection Functions 中。本文來看看 Array Functions 中還有哪些有意思的方法(以前沒有被說起)。ide

_.compact

這個方法頗有意思,它的做用是剔除數組中的假值,返回數組副本。函數

實現很是的簡單:

_.compact = function(array) {
  return _.filter(array, _.identity);
};

_.filter 咱們在之後會講到,這裏你能夠把它理解爲 Array.prototype.filter 的一個 polyfill,來看看 _.identity 是個什麼東東。

_.identity = function(value) {
  return value;
};

乍一看,_.identity 彷佛沒什麼卵用,傳入一個參數,原封不動返回這個參數,什麼鬼?而再看 _.compact 的實現,就會發現很是巧妙!細細品味下,直接過濾了數組的假值,而 _.identity 在源碼中能在多個地方複用。

從這個方法能夠想到 PHP 的 array_filter 函數。array_filter 的基本用法和 Array.prototype.filter 類似,都是爲了過濾數組中的元素。

function isOdd($num) {
  return $num & 1;
}

$a = Array(1, 2, 3);

$a = array_filter($a, 'isOdd');

var_dump($a);

// array
//   0 => int 1
//   2 => int 3

可是,值得注意的是:

If no callback is supplied, all entries of array equal to FALSE (see converting to boolean) will be removed.

這就有點 6 了,直接把 _.filter 和 _.compact 兩個方法合二爲一了。

$a = Array(0, 1, 2, 3, null, false, 4);

$a = array_filter($a);

var_dump($a);

// array
//   1 => int 1
//   2 => int 2
//   3 => int 3
//   6 => int 4

Array.prototype.filter 爲什麼不設計成這樣呢?沒有 callback 傳入的時候,直接過濾假值...

_.difference & _.without

先來看 _.without,它的做用是從數組中剔除指定的元素。

var a = [1, 2, 3, 4, 5];
var ans = _.without(a, 1, 2, 3);
console.log(ans); // [4, 5]

恩,沒錯,剔除數組 a 中的 value 爲 1, 2, 3 的元素,這個過程當中用 === 來進行比較。該方法傳入的第一個參數是數組,後面的參數爲單個元素。

而 _.difference 呢?和 _.without 的惟一區別是,第二個參數開始傳入的是數組。(分別和數組中的元素比較)

var a = [1, 2, 3, 4, 5];
var ans = _.difference(a, [1, 2, 3], [5, 6]);
console.log(ans); // [4]

從 a 數組中剔除 1,2,3,5,6。

仔細一想,若是已經實現了 _.difference,咱們把 _.without 的參數放入數組,而後傳入 _.difference 就 ok 了!倒過來就不行了(思考下爲何)。

來看 _.difference 的實現,很是簡單:

// _.difference(array, *others)
_.difference = function(array) {
  // 將 others 數組展開一層
  // rest[] 保存展開後的元素組成的數組
  // strict 參數爲 true
  // 不能夠這樣用 _.difference([1, 2, 3, 4, 5], [5, 2], 10);
  // 10 就會取不到
  var rest = flatten(arguments, true, true, 1);
  
  // 遍歷 array,過濾
  return _.filter(array, function(value){
    // 若是 value 存在在 rest 中,則過濾掉
    return !_.contains(rest, value);
  });
};

不熟悉 flatten 的能夠看看 前文,當 shallow 和 strict 均爲 true 時,展開一層,而且過濾非數組元素,便可以起到將多個數組合並的做用。以後利用 ._filter 進行過濾便可。

而 _.without 方法則創建在 _.difference 基礎上。

_.without = function(array) {
  // slice.call(arguments, 1)
  // 將 arguments 轉爲數組(同時去掉第一個元素)
  // 以後即可以調用 _.difference 方法
  return _.difference(array, slice.call(arguments, 1));
};

總結

數組的擴展方法就解讀到這裏了,相關源碼能夠參考 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L450-L693 這部分。接下去要解讀的是 Collection Functions 部分,所謂 Collection,正是 Object & Array,也就是說這部分方法既能夠用於 Object 也能用於 Array,好比咱們熟悉的 map,filter,shuffle 等等,都在這部份內。

放個預告,下一篇會暫緩下 Collection Functions,講下 array-like 相關的東西,敬請期待。

PS:堅持一件事真的挺難,一個月來,天天堅持看點源碼,幾乎把全部業餘時間花在了上面,寫了 10 篇隨筆,每篇文章寫的時間不短,關鍵還須要構思,如何提煉出一個主題,如何寫讓人看了會有所收穫,恩,繼續堅持。請關注個人 Repo https://github.com/hanzichi/underscore-analysis 支持我~

相關文章
相關標籤/搜索