送你一道字节前端原题(Add sumOf)|文末送红宝书
前言
最近学弟去面了字节跳动,但是由于面试经验少,面试的时候紧张了,一时之间没有写出来,之后来我交流了一下。那我就来分析分析这道题目。
image-20200906001022824
正文
这题的规则是这样的
给定有一个 Add 函数,要支持以下形式的调用
Add(1)(2)(3).sumOf(); // 输出 6
Add(1,2)(3)(4).sumOf(); // 输出 10
Add(1,2,...)(3)(4)(...).sumOf(); // ...
拿到这种题目,我先来说说我自己的做题流程,一般会去找它最简单的形态。我们一步一步来拆解。
先去掉 sumOf()
变成了以下形态
Add(1,2,...)(3)(4)(...)
嗯....有点熟悉...但是还是有点复杂,那我们再去掉无限调用这个限制。
Add(1,2,...)(3)(4)
唔,还是有点难呀...没关系,再砍, 不要传入多个参数。
Add(1)(2)(3)
有....有....有那味了....这....这不就是柯里化吗....
有些小朋友可能没有听过,对于大朋友而言耳熟能详,融会贯通。
我们还是来介绍一下。
在《javascript高级程序设计》这本书中有如下介绍:
与函数绑定紧密相关的主题是函数柯里化,它用于创建已经设置好的一个或者多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。
我们来写写看:
function Add(x) {
return function (y) {
return return functio (z) {
return x + y + z;
}
}
}
// 简洁写法
const Add = x => y => z => x+y+z;
执行一下
Add(1)(2)(3) // 6
是我们要的那味~
那么我们既然已经写出了这个形态,我们就一步一步反推。
这个时候千万别紧张,我们从最低级的形态出发,写出一个最基本的形态,能够有效地帮助我们建立自信心,吃下定心丸,按照这种方式,哪怕我们最终没有写出完美的结果,让面试官看到你思考解题的过程,也是一种加分。
好,接着说~
那我们接下来需要实现这个样子。
Add(1,2,...)(3)(4)
传入参数不止一个
我们知道,对于不确定参数个数,我们可以使用 arguments
这个对象来获取到所有的入参,但是 arguments
不是一个 Array
,但是我们可以使用 ES6 中的 Spread syntax (展开语法)去将他变成一个数组。表演继续。
function Add() {
const nums = [...arguments];
return function() {
nums.push(...arguments);
return function() {
nums.push(...arguments);
return nums.reduce((a, b) => a + b);
}
}
}
nice!已经离我们最终的形态越来越近了。接下来是这个函数能够无限的进行调用。
Add(1,2,...)(3)(4)(...)
那么怎么样才能无限调用呢?没错,用递归。
function Add() {
const nums = [...arguments];
function AddPro() {
nums.push(...arguments);
return AddPro;
}
return AddPro;
}
嗯,其实我们写到这里发现了... 由于是无限递归,我们没办法确定最后一次函数调用,因此我们需要最后显式调用一个结束的方法来打印出最后的数据。
很自然地,我们可以在 AddPro
添加一个方法 sumOf
来解决这个问题。
学弟就是卡在这里地方,被函数添加上一个方法搞懵了。你是否知道呢?
function Add() {
const nums = [...arguments];
function AddPro() {
nums.push(...arguments);
return AddPro;
}
AddPro.sumOf = () => {
return nums.reduce((a, b) => a + b);
}
return AddPro;
}
好啦好啦,结束啦。
等等
在最后,我再来补充一种方案,function
不仅可以继续挂载 function
~ 还可以挂载变量哦~
function Add() {
if (!Add.nums) {
Add.nums = [];
}
Add.nums.push(...arguments);
return Add;
}
Add.sumOf = () => {
return Add.nums.reduce((a, b) => a + b);
}
如果上述回答有更优解,请公众号后台回复,留下你的微信,红包相送。
我们总结一下,小小的面试题涉及到的基础知识。
闭包、递归、作用域、函数与对象
基础就是基础,永远是你爸爸,掌握好基础,以不变应万变。
一个彩蛋
function Add() {
const nums = [...arguments];
return () => {
nums.push(...arguments);
return () => {
nums.push(...arguments);
return nums.reduce((a, b) => a + b);
}
}
}
// 如果我上述代码中间换成箭头函数又会怎么样呢~,公众号号后台回复自己的理解,抽取2位优秀回答者送红包(6.6元)哦~大朋友就别来了~
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- kafka高可用集群搭建
- ELK7.x日志系统搭建 4. 结合kafka集群完成日志系统
- ELK7.x日志系统搭建 3. 采用轻量级日志收集Filebeat
- ELK7.x日志系统搭建 2. Nginx、Cluster等日志收集
- Java8 Stream
- 性能测试必备知识(4)- 使用 stress 和 sysstat 分析平均负载过高的场景
- 高并发利器-guava分流与缓存
- 树莓派3b+组装+烧录retropie系统
- 使用Azure DevOps Pipeline实现.Net Core程序的CI
- 带你遨游USB世界
- C# 泛型中的数据类型判定与转换
- 评测Loki日志工具
- .Net微服务实战之负载均衡(上)
- ES 7.8 速成笔记(上)
- 利用Xtrabackup进行mysql增量备份和全量备份