成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

面試題目別有洞天 -> 從es6優(yōu)雅解法,到降級(jí)polyfill,再到redux reduce

econi / 3589人閱讀

摘要:每次被執(zhí)行時(shí),和被傳入,這個(gè)根據(jù)進(jìn)行累加或者是自身消減,英文原意,進(jìn)而返回最新的。

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

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

題目背景

完成一個(gè)"flatten"的函數(shù),實(shí)現(xiàn)“拍平”一個(gè)多維數(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), []);

如果你看不明白,不要放棄。我會(huì)用ES5的思路“翻譯”一下,相信你很快就能看懂。

如果你一眼能看明白,也建議繼續(xù)往下讀。因?yàn)闀?huì)有“不一樣”的知識(shí)點(diǎn)。

深入解讀

第一個(gè)想到的念頭肯定是遞歸,遞歸自然就想到遞歸的“盡頭”,那就要判斷數(shù)組某項(xiàng)元素是否還是數(shù)組類型。
好吧,我們開始動(dòng)手實(shí)現(xiàn)一個(gè)方案,其實(shí)是上面解法的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);
    }, []);
};

可能這樣寫,對(duì)于很多人來說,并不能完全理解。因?yàn)槲覀兪褂昧溯^多JS高級(jí)用法。關(guān)鍵核心還用到了類似“函數(shù)式”思想的reduce方法。
千萬不要灰心,繼續(xù)往下看。

return的到底是什么?

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

function t() {
    var a = 1;
    return (a++, a);
}
t(); // 2
Object.prototype.toString.call是什么?

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

var a = [];
typeof a; // "object"
a instanceof Array; // true;
Object.prototype.toString.call(a); // "[object Array]"
reduce方法到底做了什么?

現(xiàn)在到了最關(guān)鍵的地方。reduce方法是ES5引入,很多人使用它的場(chǎng)景并不多。但是了解他的特性卻是必須的。遺憾的是,社區(qū)上對(duì)于它的內(nèi)容似乎都不是“太重視”?!昂瘮?shù)式“思想也讓一些初學(xué)者望而卻步。這里我簡(jiǎn)要進(jìn)行“科普”,因?yàn)橄旅嫖乙獓@它進(jìn)行延伸:

reduce在英文中譯為“減少; 縮小; 使還原; 使變?nèi)酢?,MDN對(duì)方法直述為:“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.”
我并不打算對(duì)他直接翻譯,因?yàn)檫@樣會(huì)變的更加晦澀難懂。

我們看他的使用語法:

array1.reduce(callbackfn[, initialValue])

參數(shù)分析:

1)array1:必需。
一個(gè)數(shù)組對(duì)象。即調(diào)用reduce方法的必須是一個(gè)數(shù)組類型。

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

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

3)initialValue可選項(xiàng)。
其值用于第一次調(diào)用callback的第一個(gè)參數(shù)。如果此參數(shù)為空,則拿數(shù)組第一項(xiàng)來作為第一次調(diào)用callback的第一個(gè)參數(shù)。

比如,我們分析一個(gè)常用用法:

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

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

所以,上述代碼便是一個(gè)累加器的實(shí)現(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對(duì)數(shù)組類型進(jìn)行判斷。這是開篇提到的解法,也是MDN最新版的實(shí)現(xiàn)。

如何實(shí)現(xiàn)一個(gè)reduce的pollyfill

現(xiàn)在明白了reduce的秘密,接下來我們需要充分發(fā)揮對(duì)JS的理解,來手動(dòng)實(shí)現(xiàn)一個(gè)reduce函數(shù)。畢竟,reduce是ES5帶來的數(shù)組新特性,在不使用ES5-shim的情況下,需要手動(dòng)兼容。另外,其實(shí)reduce方法可以實(shí)現(xiàn)的邏輯,大多都能夠使用循環(huán)來實(shí)現(xiàn)。但是了解這樣一個(gè)優(yōu)雅的方法,不管是在程序的可讀性上,還是在設(shè)計(jì)理解層面上,還是很有必要的。

同樣,在MDN上也有實(shí)現(xiàn),但是我覺得下面的代碼實(shí)現(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;
};

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

ES5-shim的pollyfill

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

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

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

Redux中的reducer

明白了reduce函數(shù),我們?cè)賮砜匆幌翿edux中的reducer和這個(gè)reduce有什么命名上的關(guān)聯(lián)。

熟悉Redux數(shù)據(jù)流架構(gòu)的同學(xué)理解reducer做了什么,關(guān)于這個(gè)純函數(shù)的命名,在redux源碼github倉庫上也有一個(gè)官方解釋:“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其實(shí)是根據(jù)之前的狀態(tài)(previous state)和現(xiàn)有的action(current action)更新state(這個(gè)state可以理解為上文累加器的結(jié)果(accumulation))。每次redux reducer被執(zhí)行時(shí),state和action被傳入,這個(gè)state根據(jù)action進(jìn)行累加或者是“自身消減”(reduce,英文原意),進(jìn)而返回最新的state。這符合一個(gè)典型reduce函數(shù)的用法:state -> action -> state.

總結(jié)

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

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

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

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/86850.html

相關(guān)文章

  • 你不知道的h5

    摘要:目前,常用的模塊規(guī)范主要有兩種和。攔截全局請(qǐng)求一直接引入腳本攔截需要的回調(diào)或函數(shù)。深刻知道一個(gè)良好的命名規(guī)范的重要性,同時(shí)在項(xiàng)目中也會(huì)遇到一些命名的瓶頸。 基于 Three.js 的超快的 3D 開發(fā)框架:Whitestorm.js Whitestorm.js 是一款基于 Three.js 超快的 Web 應(yīng)用 3D 開發(fā)框架。它為普通的 Three.js 任務(wù)提供封裝、使搭建環(huán)境、...

    IntMain 評(píng)論0 收藏0
  • 我們不背誦 API,只實(shí)現(xiàn) API

    摘要:接下來,我們換一種思路,用一個(gè)相對(duì)較新的來實(shí)現(xiàn)方法。從這道題目看出,相比考察死記硬背,這樣的實(shí)現(xiàn)更有意義。對(duì)數(shù)組的操作我們不能陌生,其中方法更要做到駕輕就熟。最后,我們?cè)倏聪律鐓^(qū)上著名的和的實(shí)現(xiàn)。 有不少剛?cè)胄械耐瑢W(xué)跟我說:JavaScript 很多 API 記不清楚怎么辦?數(shù)組的這方法、那方法總是傻傻分不清楚,該如何是好?操作 DOM 的方式今天記,明天忘,真讓人奔潰! 甚至有的開發(fā)...

    wudengzan 評(píng)論0 收藏0
  • 我們不背誦 API,只實(shí)現(xiàn) API

    摘要:接下來,我們換一種思路,用一個(gè)相對(duì)較新的來實(shí)現(xiàn)方法。從這道題目看出,相比考察死記硬背,這樣的實(shí)現(xiàn)更有意義。對(duì)數(shù)組的操作我們不能陌生,其中方法更要做到駕輕就熟。最后,我們?cè)倏聪律鐓^(qū)上著名的和的實(shí)現(xiàn)。 有不少剛?cè)胄械耐瑢W(xué)跟我說:JavaScript 很多 API 記不清楚怎么辦?數(shù)組的這方法、那方法總是傻傻分不清楚,該如何是好?操作 DOM 的方式今天記,明天忘,真讓人奔潰! 甚至有的開發(fā)...

    wayneli 評(píng)論0 收藏0
  • Zepto 源碼分析 2 - Polyfill 設(shè)計(jì)

    摘要:此模塊包含的設(shè)計(jì)思路即為預(yù)以匹配降級(jí)方案。沒有默認(rèn)編譯該模塊,以及利用該模塊判斷后提供平臺(tái)相關(guān)邏輯的主要原因在于其設(shè)計(jì)原則的代碼完成核心的功能。此處,也引出了代碼實(shí)現(xiàn)的另一個(gè)基本原則面向功能標(biāo)準(zhǔn),先功能覆蓋再優(yōu)雅降級(jí)。 在進(jìn)入 Zepto Core 模塊代碼之前,本節(jié)簡(jiǎn)略列舉 Zepto 及其他開源庫中一些 Polyfill 的設(shè)計(jì)思路與實(shí)現(xiàn)技巧。 涉及模塊:IE/IOS 3/Dete...

    chuyao 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

econi

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<