面试题目(头条):
设计一个 sum 函数, 使其满足以下要求
- sum(1, 2).sumOf() // 返回 3
- sum(1, 2)(3).sumOf() // 返回 6
- sum(1)(2, 3, 4).sumOf() // 返回 10
- sum(1, 2)(3, 4)(5).sumOf() // 返回 15
答案解析:
这道题目主要考察以下两点:
函数参数 arguments 的应用;
函数的链式调用实现;
arguments
arguments 对象是函数 (非箭头) 内部都可以访问到的局部变量, 该对象是一个类数组对象, 具有数组的 length, 索引特性.
- function print () {
- console.log(arguments[0]);
- console.log(arguments[1]);
- console.log(arguments[2]);
- console.log(arguments.length); // 3
- }
- print(1, 2, 3);
- // 利用 arguments 的索引特性, call,apply 函数, 可以使用数组的方法
- Array.prototype.shift.call(arguments);
- // 将 arguments 转为数组, Array 参数为一个, 并且是整数时, 表示数组长度, 超过一个时表示数组元素
- var args = arguments.length == 1 ? [arguments[0]] : Array.apply(null, arguments);
链式编码原理
链式调用原理就是作用域链, 实现需要做如下的工作:
因为需要链式调用, 所以需要返回对象本身的引用;
利用闭包, 存储累计值, 以便最后返回;
示例:
- function add (num) {
- var count = num;
- var _b = function(l){
- count += l;
- return _b
- }
- _b.valueOf = function(){
- return count
- }
- return _b
- }
- var c = add(1)(2)(3);
- console.log(c) //6
下面我们来详细分析一下代码:
首先, 在 add 方法内部, 我们是通过私有的_b 方法实现的加法, 而不是在 add 方法自身实现的, 这里涉及到了函数式编程, 这个概念我们就不在此做展开了, 有兴趣的童鞋可以自己研究一下, 可以说这是一种很不错的开发模式; add 第一次执行后, 返回了_b 方法;
在返回的_b 方法中我们形成了对 count 的闭包, 这样我们可以实现累计加和; 还有一点需要注意, 就是_b 方法每次执行时都返回了它自身, 这就实现了链式;
最后, 也是比较关键的, 就是在输出 add 的结果, 即 add(1)(2)(3)的结果时, 如何让它输出 count, 这里涉及了 valueOf 和 toString 方法的知识, 还是那句话, 感兴趣的童鞋可以自己研究一下; 在这里最后能够正确输出 6 的原理是:_b 是 Function, 是 Object 的一种特殊形式, 当我们做类似打印 console 等操作时, 会自动调用其 valueOf 方法(其实底层实现没有我说的这么简单, 哈哈, 但是大概是这么个意思), 所以我们重写了 valueOf 方法来达到返回 count 的目的;
综上, 我们的面试题目, 就可以做如下实现:
- function sum () {
- var ary = [].slice.apply(arguments);
- var fn = function () {
- ary = ary.concat([].slice.apply(arguments));
- return fn;
- }
- fn.sumOf = function () {
- return ary.reduce((pre, cur) => {
- return pre + cur;
- })
- }
- return fn;
- }
利用闭包, 设置变量 ary 存储函数链式调用期间的所有参数;
自定义内部函数, 调用时返回自身;
定义 fn 的静态方法 sumOf, 用于计算数组 ary 的参数和;
下面是我实现的另一种方式, 但需要设置一个函数静态变量值存储数据:
- function sum() {
- let s = 0;
- for(let i = 0; i < arguments.length; i++) {
- s += arguments[i];
- }
- sum.result += s;
- return sum;
- }
- sum.result = 0;
- sum.sumOf = function () {
- let temp = sum.result;
- sum.result = 0;
- return temp;
- }
以上两种实现方式, 涉及的知识点基本一样, 欢迎读者留言其他实现方式.
来源: http://www.jianshu.com/p/c3f9c3f5d05b