以前电面有问到:“你知道一个函数的length是什么吗?”前端
由于没看过,也没碰到过使用场景,我没答出来。后来查了下发现是指函数的参数个数,因而也就做罢了。不过今天恰巧碰到了使用场景,且发现以前的理解也有误,因而就写一篇短文分享一下。数组
今天在尝试 render props 的各类扩展玩法,写了个简单的表格数据 CRUD 操做 Demo:CodeSandboxbabel
写Demo通常从全部操做都同步开始,最后再全改为异步的状况。因此须要写一个将全部同步函数(数据加载以及增删改)都转换成异步函数的工具函数。我一开始写得以下:dom
const delay = ms => new Promise(_ => setTimeout(_, ms));
// 打算从今开始尽可能使用 async/await
const withRequest = func => async args => {
await delay(1000);
if (Math.random() > 0.3) {
func(...args);
} else {
message.info("操做失败!");
}
};
// 等一秒以后 30% 失败,70% 执行操做
复制代码
这是个所谓的 Curried(库里?柯里?咖喱?)函数,用于批量改造函数的函数,接受func为参数,返回改造好的func。明眼人应该已经发现错在哪里了,不过我没有,因而走了一堆弯路,却收获很多。异步
用此函数包裹了个人一堆测试方法:async
add = (a,b) => a + b
square = a => a * a
loadData = () => this.setState({ ... })
loadData = withRequest(this.loadData);
add = withRequest(this.add);
square = withRequest(this.square);
复制代码
立马报错跪了,因而我知道在没有参数的 loadData 函数那里跪了,并开始了个人求知之旅。函数
如何将任意个参数从上级函数传递给下级函数?工具
分状况讨论的关键是:如何知道函数有几个参数呢?毫无疑问我想到了fn.length
, 因而写下:post
const len = func.length
if(len === 0){
func()
} else if(len === 1) {
func(args)
} else {
func(...args)
}
复制代码
这对了吗?答案是不对。 fn.length
的定义是:函数的形参个数。也就是函数定义时的参数个数,而不是函数实际接受的参数个数。好比测试
const add = (a,b) => a + b
add(1,2,3,4,5) // 3
add.length // 2
复制代码
而问题的状况,咱们须要判断的是函数接受的参数个数。这时候有一个方便的内置变量:arguments
function func1(a, b, c) {
console.log(arguments[0]); // 1
console.log(arguments[1]); // 2
console.log(arguments[2]); // 3
}
func1(1, 2, 3);
复制代码
arguments 即为函数接收到的全部参数组成的(类)数组。那么用 arguments.length
替换全部 func.length
是否就对了呢?仍是不对,arguments 有它的局限性:
const func1 = (a, b, c) => {
console.log(arguments[0]); // 1
console.log(arguments[1]); // 2
console.log(arguments[2]); // 3
}
func1(1, 2, 3);
// error: arguments is not defined
复制代码
箭头函数没有arguments。 同时注意到如今前端代码的箭头函数会通过 babel 转译,产生的结果是 arguments 虽然不会 undefined,但会有各类怪异赋值。总之在箭头函数里别使用。
剩余参数 (Rest parameters)
const withRequest = func => async (...args) => {
await delay(1000);
if (Math.random() > 0.3) {
func(...args);
} else {
message.info("操做失败!");
}
};
复制代码
这段代码里出现了两个 ...args
, 前者是剩余参数,后者是数组展开。二者一个是收束,一个是展开。功能相反。
function fun1(...args) {
console.log(args.length);
}
fun1(); // 0
fun1(5); // 1
fun1(5, 6, 7); // 2
复制代码
剩余参数语法将“剩余”的参数收束到一个数组中, 注意和 arguments 不同, 剩余参数是一个真正的数组。绕了一大圈,实际上是忘记写了三个点。不过也算真正理解了:
最后,让咱们用剩余参数挑战一个实用函数吧:
写一个callAll
函数,它接收任意数量的函数和任意数量的参数,若是做为参数的函数存在就用全部的参数调用那个函数。
const add = (a,b) => {console.log(a + b)}
const minus = (a,b) => {console.log(a - b)}
callAll(add, minus)(2,1)
// 3
// 1
复制代码
答案以下:
// 剩余参数是一个真正的数组,可使用任何数组方法
const callAll = (...fns) => (...args) => fns.forEach( fn => fn && fn(...args))
复制代码