面試題目別有洞天 -> 從es6優(yōu)雅解法,到降級polyfill镇眷,再到redux reducer迷之命名

之前的一篇文章:從一道面試題咬最,到“我可能看了假源碼”討論了bind方法的各種進(jìn)階Pollyfill,今天再分享一個有意思的題目欠动。

從解這道題目出發(fā)永乌,我會談到數(shù)組的Reduce方法,ES6特性和Redux數(shù)據(jù)流框架中Reducer的命名等等具伍。一道典型的題目翅雏,卻如唐代詩人章碣《對月》詩中所云:“別有洞天三十六,水晶臺殿冷層層人芽⊥福”

題目背景

完成一個'flatten'的函數(shù),實現(xiàn)“拍平”一個多維數(shù)組為一維萤厅。示例如下:

var testArr1 = [[0, 1], [2, 3], [4, 5]];
var testArr2 = [0, [1, [2, [3, [4, [5]]]]]];
flatten(testArr1) // [0, 1, 2, 3, 4, 5]
flatten(testArr2) // [0, 1, 2, 3, 4, 5]

解法先睹為快

先看一眼比較優(yōu)雅的ES6解法:

const flatten = arr => arr.reduce((pre, val) => pre.concat(Array.isArray(val) ? flatten(val) : val), []);

如果你看不明白橄抹,不要放棄。我會用ES5的思路“翻譯”一下惕味,相信你很快就能看懂楼誓。

如果你一眼能看明白,也建議繼續(xù)往下讀名挥。因為會有“不一樣”的知識點疟羹。

深入解讀

第一個想到的念頭肯定是遞歸,遞歸自然就想到遞歸的“盡頭”躺同,那就要判斷數(shù)組某項元素是否還是數(shù)組類型阁猜。
好吧,我們開始動手實現(xiàn)一個方案蹋艺,其實是上面解法的ES5版本:

var flatten = function(array) {
    return array.reduce(function(previous, val) {
        if (Object.prototype.toString.call(val) !== '[object Array]') {
            return (previous.push(val), previous);
        }
        return (Array.prototype.push.apply(previous, flatten(val)), previous);
    }, []);
};

可能這樣寫剃袍,對于很多人來說,并不能完全理解捎谨。因為我們使用了較多JS高級用法民效。關(guān)鍵核心還用到了類似“函數(shù)式”思想的reduce方法憔维。
千萬不要灰心,繼續(xù)往下看畏邢。

return的到底是什么业扒?

我們注意到上面的寫法return使用了()表達(dá)式。括號內(nèi)容前半句是為了執(zhí)行舒萎。這樣寫也許稍微晦澀難懂一些程储。請看下面的代碼示例,你就會明白:

function t() {
    var a = 1;
    return (a++, a);
}
t(); // 2

Object.prototype.toString.call是什么臂寝?

Object.prototype.toString.call可以暫且認(rèn)為是“功能最強大”的類型判斷語句章鲤。在對數(shù)組類型進(jìn)行判斷時,需要格外小心咆贬,比如這樣幾個“陷阱”:

var a = [];
typeof a; // "object"
a instanceof Array; // true;
Object.prototype.toString.call(a); // "[object Array]"

reduce方法到底做了什么败徊?

現(xiàn)在到了最關(guān)鍵的地方。reduce方法是ES5引入掏缎,很多人使用它的場景并不多皱蹦。但是了解他的特性卻是必須的。遺憾的是眷蜈,社區(qū)上對于它的內(nèi)容似乎都不是“太重視”沪哺。“函數(shù)式“思想也讓一些初學(xué)者望而卻步端蛆。這里我簡要進(jìn)行“科普”凤粗,因為下面我要圍繞它進(jìn)行延伸:

reduce在英文中譯為“減少; 縮小; 使還原; 使變?nèi)酢保琈DN對方法直述為:“The reduce method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.”
我并不打算對他直接翻譯今豆,因為這樣會變的更加晦澀難懂嫌拣。

我們看他的使用語法:

array1.reduce(callbackfn[, initialValue])

參數(shù)分析:

1)array1:必需。
一個數(shù)組對象呆躲。即調(diào)用reduce方法的必須是一個數(shù)組類型异逐。

2)callbackfn:必需。
一個接受最多四個參數(shù)的函數(shù)插掂。對于數(shù)組中的每個元素灰瞻,reduce方法都會調(diào)用 callbackfn 函數(shù)一次。
這個callback的4個參數(shù)為:

accumulator // 上一次調(diào)用回調(diào)返回的值辅甥,或者是提供的初始值(initialValue)
currentValue // 數(shù)組中正在處理的元素
currentIndex // 數(shù)據(jù)中正在處理的元素索引酝润,如果提供了initialValue ,從0開始璃弄;否則從1開始
array // 調(diào)用reduce的數(shù)組

3)initialValue可選項要销。
其值用于第一次調(diào)用callback的第一個參數(shù)。如果此參數(shù)為空夏块,則拿數(shù)組第一項來作為第一次調(diào)用callback的第一個參數(shù)疏咐。

比如纤掸,我們分析一個常用用法:

[0,1,2,3,4].reduce(function(previous, item, currentIndex, array){
  return previous + item;
});
// 10

這里并未提供reduce的第二個參數(shù)initialValue,所以從數(shù)組第一項開始進(jìn)行回調(diào)函數(shù)的執(zhí)行浑塞。并且每次回調(diào)函數(shù)執(zhí)行完之后的結(jié)果借跪,作為下一次的previous執(zhí)行回調(diào)。

所以酌壕,上述代碼便是一個累加器的實現(xiàn)掏愁。

ES6寫法

現(xiàn)在理解了Reduce函數(shù),再結(jié)合ES6特性仅孩,使解法更加優(yōu)雅:

const flatten = arr => arr.reduce((pre, val) => pre.concat(Array.isArray(val) ? flatten(val) : val), []);

這樣寫是不是太“函數(shù)式”了托猩,但是思路跟之前解法完全一樣。我只不過充分使用了箭頭函數(shù)帶來的便利辽慕。并且使用了更便捷的isArray對數(shù)組類型進(jìn)行判斷。這是開篇提到的解法赦肃,也是MDN最新版的實現(xiàn)溅蛉。

如何實現(xiàn)一個reduce的pollyfill

現(xiàn)在明白了reduce的秘密,接下來我們需要充分發(fā)揮對JS的理解他宛,來手動實現(xiàn)一個reduce函數(shù)船侧。畢竟,reduce是ES5帶來的數(shù)組新特性厅各,在不使用ES5-shim的情況下镜撩,需要手動兼容。另外队塘,其實reduce方法可以實現(xiàn)的邏輯袁梗,大多都能夠使用循環(huán)來實現(xiàn)。但是了解這樣一個優(yōu)雅的方法憔古,不管是在程序的可讀性上遮怜,還是在設(shè)計理解層面上,還是很有必要的鸿市。

同樣锯梁,在MDN上也有實現(xiàn),但是我覺得下面的代碼實現(xiàn)更加優(yōu)雅和清晰:

var reduce = function(arr, func, initialValue) {
    var base = typeof initialValue === 'undefined' ? arr[0] : initialValue;
    var startPoint = typeof initialValue === 'undefined' ? 1 : 0;
    arr.slice(startPoint)
        .forEach(function(val, index) {
            base = func(base, val, index + startPoint, arr);
        });
    return base;
};

如果讀者有不同實現(xiàn)思路焰情,也歡迎與我討論陌凳。

ES5-shim的pollyfill

我也同樣看了下ES5-shim里的pollyfill,跟我的思路基本完全一致内舟。唯一有一點區(qū)別的地方在于我用了forEach迭代而ES5-shim使用的是簡單for循環(huán)合敦。

當(dāng)然,數(shù)組的forEach方法也是ES5新增的谒获。但我這里是為了用簡單明了的思路蛤肌,實現(xiàn)reduce方法壁却,根本目的還是希望對reduce有一個全面透徹的了解。

如果您還不明白裸准,我認(rèn)為還是對于reduce方法沒有掌握透徹展东。建議再梳理一遍。

Redux中的reducer

明白了reduce函數(shù)炒俱,我們再來看一下Redux中的reducer和這個reduce有什么命名上的關(guān)聯(lián)盐肃。

熟悉Redux數(shù)據(jù)流架構(gòu)的同學(xué)理解reducer做了什么,關(guān)于這個純函數(shù)的命名权悟,在redux源碼github倉庫上也有一個官方解釋:“It's called a reducer because it's the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue)”砸王,雖然是一筆帶過,但是總結(jié)的恰到好處峦阁。

我詳細(xì)說一下:Redux數(shù)據(jù)流里谦铃,reducers其實是根據(jù)之前的狀態(tài)(previous state)和現(xiàn)有的action(current action)更新state(這個state可以理解為上文累加器的結(jié)果(accumulation))。每次redux reducer被執(zhí)行時榔昔,state和action被傳入驹闰,這個state根據(jù)action進(jìn)行累加或者是“自身消減”(reduce,英文原意)撒会,進(jìn)而返回最新的state嘹朗。這符合一個典型reduce函數(shù)的用法:state -> action -> state.

總結(jié)

這篇文章對于如何優(yōu)雅地“扁平化”一個多維數(shù)組進(jìn)行了解法分析。并且對于秉承函數(shù)式編程思想的reduce方法進(jìn)行了深入討論诵肛,我們還實現(xiàn)了reduce的pollyfill屹培。在充分理解的基礎(chǔ)上,又簡要延伸到redux數(shù)據(jù)架構(gòu)里面reducer的命名怔檩。熟悉Redux的同學(xué)一定會有所感觸褪秀。

最后希望對讀者有所啟發(fā),也歡迎同我討論珠洗。

PS:百度知識搜索部大前端繼續(xù)招兵買馬溜歪,高級工程師、實習(xí)生職位均有许蓖,有意向者火速聯(lián)系蝴猪。。膊爪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末自阱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子米酬,更是在濱河造成了極大的恐慌沛豌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異加派,居然都是意外死亡叫确,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門芍锦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來竹勉,“玉大人,你說我怎么就攤上這事娄琉〈闻遥” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵孽水,是天一觀的道長票腰。 經(jīng)常有香客問我,道長女气,這世上最難降的妖魔是什么杏慰? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮炼鞠,結(jié)果婚禮上逃默,老公的妹妹穿的比我還像新娘。我一直安慰自己簇搅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布软吐。 她就那樣靜靜地躺著瘩将,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凹耙。 梳的紋絲不亂的頭發(fā)上姿现,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音肖抱,去河邊找鬼备典。 笑死,一個胖子當(dāng)著我的面吹牛意述,可吹牛的內(nèi)容都是我干的提佣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼荤崇,長吁一口氣:“原來是場噩夢啊……” “哼拌屏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起术荤,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤倚喂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瓣戚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體端圈,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡焦读,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了舱权。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矗晃。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖刑巧,靈堂內(nèi)的尸體忽然破棺而出喧兄,到底是詐尸還是另有隱情,我是刑警寧澤啊楚,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布吠冤,位于F島的核電站,受9級特大地震影響恭理,放射性物質(zhì)發(fā)生泄漏拯辙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一颜价、第九天 我趴在偏房一處隱蔽的房頂上張望涯保。 院中可真熱鬧,春花似錦周伦、人聲如沸夕春。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽及志。三九已至,卻和暖如春寨腔,著一層夾襖步出監(jiān)牢的瞬間速侈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工迫卢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留倚搬,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓乾蛤,卻偏偏與公主長得像每界,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子幻捏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容