Issue 地址:github.com/aleen42/Per…git
其實咱們不少時候在設計一個 JavaScript 函數時,均可能會遇到這樣的一種狀況:參數數量不定,且通常會傳遞有多個。此時,咱們就會想該如何更優雅地設計函數來接收這些不定的參數?此 Issue 的提出就是爲了談談不才及劣者所知道的一些看法。github
對於這樣的狀況,咱們確定會想到一對函數:數組
一樣是爲方法調用指定 this
,二者的區別在於傳遞不定參數的方式。app
爲了方便記憶,我一般會採用一種巧妙的方法去區分二者。
call()
首字母爲 c,於是可看做以逗號(Comma)的方式來區分參數,而apply()
首字母爲 a,於是也可看做是以數組(Array)的方式來區分參數。函數
這樣一看,官方彷佛已爲咱們預先提供了兩種通用的方式:ui
然而,若不去親身實現還不知道該怎麼設計函數才能更爲優雅?對此,不才與劣者認爲應該先實現數組組合的方式,然後再實現逗號分隔的方式。爲了能更好地說明,我將舉例 underscore 中關於 _.without()
與 _.difference()
的實現。在討論實現以前,咱們先了解這兩個函數到底有何做用?其實,它們主要用於過濾數組中的部分紅員。所以,經過下面的代碼片斷咱們就能清晰地看到:this
var arr = [1, 2, 3, 4, 5];
var result = _.without(arr, 1, 2, 3);
console.log(result); /** => [4, 5] */複製代碼
var arr = [1, 2, 3, 4, 5];
var result = _.difference(arr, [1, 2, 3], [5, 6]);
console.log(result); /** => [4] */複製代碼
過濾成員須要經過不定的參數來告知函數,而二者惟一的區別與前述例子相似,也就是傳遞不定參數的方式不一樣。那麼,回到原來的問題,爲什麼咱們要先設計並實現以數組組合方式的函數呢?其實很簡單,緣由在於反過來實現會形成許多沒必要要的麻煩。spa
例如咱們先實現數組方式傳遞的 _.difference()
,咱們就能夠經過簡單的數組組合來實現 _.without()
:prototype
_.difference = function (array) {
/** * 把後續的參數嚴格鋪平成一個數組,即忽略不是包含在數組內的參數 * 如 _.difference(array, [1, 2], 3); 語句中的 3 */
var rest = flatten(arguments, true, true, 1);
return _.filter(array, function (value) {
return !_.contains(rest, value);
});
};
_.without = function (array) {
return _.difference(array, Array.prototype.slice.call(arguments, 1));
};複製代碼
但假若反過來,實現方式則變得更爲複雜:設計
_.without = function (array) {
/** 若通過 `_.difference()` 的調用,則還須要把參數進行一層鋪平 */
var rest = (this == 'difference') ?
_.flatten(arguments, false, false, 1) :
Array.prototype.slice.call(arguments, 1);
return _.filter(array, function(value) {
return !_.contains(rest, value);
});
};
_.difference = function(array) {
var args = _.flatten(arguments, true, true, 1);
return _.without.apply('difference', args.unshift(array));
};複製代碼
綜上所述,以 _.without()
和 _.difference()
爲例其複雜點顯然在於 _.without()
該如何區分究竟是否通過 _.difference()
來調用本身?由於針對這樣的兩種狀況,_.without()
都須要對參數進行不一樣的處理。簡單來講,_.without()
對於來自 _.difference()
的調用須要再進行一次鋪平(注:不才與劣者此處是經過指定 this
來提供一種區分的方式)。爲什麼?細緻想一想就會發現,產生如此的複雜在於舊版的 JavaScript 語法只能經過數組組合來傳遞若干個不定的參數,而沒法鋪開成逗號分隔的形式來傳遞。
那麼,既然語法存在缺陷,ES6 是否提供了新的方式去解決該問題呢?不才認爲,這偏偏體現出展開操做符(...
,Spread Operator)的魅力所在。有了它,你就能夠直接展開若干個不定參數呢!
_.without = function (array) {
var rest = Array.prototype.slice.call(arguments, 1);
return _.filter(array, function(value) {
return !_.contains(rest, value);
});
};
_.difference = function(array) {
var args = _.flatten(arguments, true, true, 1);
args.unshift(array);
return _.without.call(null, ...args);
};複製代碼