口试问题别有洞天 -> 从es6文雅解法,到降级polyfill,再到redux reducer迷之定名

之前的一篇文章:从一道面试题,到“我能够看了假源码”议论了bind要领的种种进阶Pollyfill,本日再分享一个有意思的问题。

从解这道问题动身,我会谈到数组的Reduce要领,ES6特征和Redux数据流框架中Reducer的定名等等。一道典范的问题,却如唐朝墨客章碣《对月》诗中所云:“别有洞天三十六,水晶台殿冷层层。”

问题背景

完成一个’flatten’的函数,完成“拍平”一个多维数组为一维。示例以下:

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]

解法先睹为快

先看一眼比较文雅的ES6解法:

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

假如你看不邃晓,不要摒弃。我会用ES5的思绪“翻译”一下,相信你很快就能够看懂。

假如你一眼能看邃晓,也发起继承往下读。由于会有“不一样”的学问点。

深切解读

第一个想到的动机肯定是递归,递归天然就想到递归的“终点”,那就要推断数组某项元素是不是照样数组范例。
好吧,我们最先着手完成一个计划,实际上是上面解法的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高等用法。症结中心还用到了相似“函数式”头脑的reduce要领。
万万不要泄气,继承往下看。

return的究竟是什么?

我们注重到上面的写法return运用了()表达式。括号内容前半句是为了实行。如许写或许轻微艰涩难明一些。请看下面的代码示例,你就会邃晓:

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

Object.prototype.toString.call是什么?

Object.prototype.toString.call能够临时以为是“功用最壮大”的范例推断语句。在对数组范例举行推断时,须要分外警惕,比方如许几个“圈套”:

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

reduce要领究竟做了什么?

如今到了最症结的处所。reduce要领是ES5引入,很多人运用它的场景并不多。然则相识他的特征倒是必须的。遗憾的是,社区上关于它的内容好像都不是“太注重”。“函数式“头脑也让一些初学者望而生畏。这里我扼要举行“科普”,由于下面我要缭绕它举行延长:

reduce在英文中译为“削减; 减少; 使复原; 使变弱”,MDN对要领直述为:“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])

参数剖析:

1)array1:必须。
一个数组对象。即挪用reduce要领的必须是一个数组范例。

2)callbackfn:必须。
一个接收最多四个参数的函数。关于数组中的每一个元素,reduce要领都邑挪用 callbackfn 函数一次。
这个callback的4个参数为:

accumulator // 上一次挪用回调返回的值,或者是供应的初始值(initialValue)
currentValue // 数组中正在处置惩罚的元素
currentIndex // 数据中正在处置惩罚的元素索引,假如供应了initialValue ,从0最先;否则从1最先
array // 挪用reduce的数组

3)initialValue可选项。
其值用于第一次挪用callback的第一个参数。假如此参数为空,则拿数组第一项来作为第一次挪用callback的第一个参数。

比方,我们剖析一个经常使用用法:

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

这里并未供应reduce的第二个参数initialValue,所以从数组第一项最先举行回调函数的实行。而且每次回调函数实行完以后的效果,作为下一次的previous实行回调。

所以,上述代码就是一个累加器的完成。

ES6写法

如今明白了Reduce函数,再连系ES6特征,使解法越发文雅:

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

如许写是不是是太“函数式”了,然则思绪跟之前解法完整一样。我只不过充足运用了箭头函数带来的方便。而且运用了更便利的isArray对数组范例举行推断。这是开篇提到的解法,也是MDN最新版的完成。

怎样完成一个reduce的pollyfill

如今邃晓了reduce的隐秘,接下来我们须要充足发挥对JS的明白,来手动完成一个reduce函数。毕竟,reduce是ES5带来的数组新特征,在不运用ES5-shim的情况下,须要手动兼容。别的,实在reduce要领能够完成的逻辑,大多都能够运用轮回来完成。然则相识如许一个文雅的要领,不管是在顺序的可读性上,照样在设想明白层面上,照样很有必要的。

一样,在MDN上也有完成,然则我以为下面的代码完成越发文雅和清楚:

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;
};

假如读者有差别完成思绪,也迎接与我议论。

ES5-shim的pollyfill

我也一样看了下ES5-shim里的pollyfill,跟我的思绪基础完整一致。唯一有一点区别的处所在于我用了forEach迭代而ES5-shim运用的是简朴for轮回。

固然,数组的forEach要领也是ES5新增的。但我这里是为了用简朴明了的思绪,完成reduce要领,基础目标照样愿望对reduce有一个周全透辟的相识。

假如您还不邃晓,我以为照样关于reduce要领没有控制透辟。发起再梳理一遍。

Redux中的reducer

邃晓了reduce函数,我们再来看一下Redux中的reducer和这个reduce有什么定名上的关联。

熟习Redux数据流架构的同砚明白reducer做了什么,关于这个纯函数的定名,在redux源码github堆栈上也有一个官方诠释:“It’s called a reducer because it’s the type of function you would pass to Array.prototype.reduce(reducer, ?initialValue)”,虽然是一笔带过,然则总结的适可而止。

我详细说一下:Redux数据流里,reducers实际上是依据之前的状况(previous state)和现有的action(current action)更新state(这个state能够明白为上文累加器的效果(accumulation))。每次redux reducer被实行时,state和action被传入,这个state依据action举行累加或者是“本身消减”(reduce,英文原意),进而返回最新的state。这相符一个典范reduce函数的用法:state -> action -> state.

总结

这篇文章关于怎样文雅地“扁平化”一个多维数组举行相识法剖析。而且关于秉持函数式编程头脑的reduce要领举行了深切议论,我们还完成了reduce的pollyfill。在充足明白的基础上,又扼要延长到redux数据架构内里reducer的定名。熟习Redux的同砚一定会有所感想。

末了愿望对读者有所启示,也迎接同我议论。

PS:百度学问搜刮部大前端继承招兵买马,高等工程师、实习生职位均有,有意向者敏捷联络。。。

    原文作者:lucas_580e331d326b4
    原文地址: https://segmentfault.com/a/1190000008632845
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞