一道很考验基本功的Js题目

文章目录
  1. 题目介绍
  2. 题目分析
  3. 感想

在前端讨论群里曾遇到过一道很难的Js题目,当时被难了很久在网上找了很多资料才弄懂如何理解这道题目,然而之后就把这道题放在一边没管了,今天突然想起来觉得这道题是到很有价值的题,它直接考验了你对Js的基本函数了解的够不够深,所以我就写了这样一篇关于这道的介绍和解析,算是复习一下很久没看过的Js吧。

题目介绍

代码:

1
2
3
4
5
6
7
8
9
10
11
12
function fnull(fun) {
var defaults = [].splice.call(arguments, 1);
return function() {
var args = [].map.call(
arguments,
(item, i) => !!item ? item : defaults[i]
);
return fun(...args);
};
}
var a = fnull((total, n) => total * n, 5, 1);
var b = [1, 2, 3, 4, 5].reduce(a, null);

要求:给出b的结果。

题目分析

该题目代码不多但是参数传递相当复杂,一眼之下很难直接看出b的取值,需要一层一层的来分析。
首先我们知道a的值是函数fnull执行后的返回值,可以看出fnull函数已经形成了一个闭包函数,在执行时defaults值通过splice函数被设置为了[5,1],并返回了它的内部函数对象。所以此时我认为a是这样的:

1
2
3
4
5
6
7
a = function() {
var args = [].map.call(
arguments,
(item, i) => !!item ? item : defaults[i]
);
return ((total,n)=>total * n)(...args);
};

然后b的赋值表达式,将a作为数组[1,2,3,4,5]的reduce函数的第一个参数,这里就是我们必须知道reduce是什么,它有几个参数,每个参数又代表什么。首先reduce在平常使用到的场景并不多,它主要是将数组中每个子项以一定的方式进行累计,最后返回累计结果。它一共支持两个参数,第一个参数为一个callback函数,代表我们累计的具体方式,第二个参数则我们要传入的初始值,如果不传入值,则初始值默认为数组的第一个值。在reduce函数执行时,它会自动向callback传入4个参数,分别是per、cur、index、arr。per为上一次调用函数时的返回值,或者初始值,cur为当前正在处理的元素值,index为当前正在处理的元素下标,arr为调用reduce方法的数组,然后在函数a中会调用map函数对传入的每个参数做一次处理,如果某个参数的值为false就会用defaults中的元素代替它,这里我们发现它是为了保证前两个参数值不为空,所以当我们传入的初始值为null时,将会用defaults[0]的值来代替即为5,然后将改变后的参数args作为累乘函数的参数传入,这里我们发现后两个参数是没有用到的,此函数的作用其实就是对数组的每个参数做一个累乘的计算,但它在其中整合对于包括初始值在内所有元素为空值或0值的处理,当初始值为这两种情况时用我们传入的第一个参数来代替,当其余元素为空值或0时用第二个参数来代替,至此我们得到b的值为51234*5=600。

感想

这道题分析起来真的很考验对于reduce这类比较冷门的js函数的深入了解,而且一定要保证思维的清晰和细致,是一道不可多得的值得详细分析的题目。