9 个让你像专业人士一样使用 Javascript 数组方法的实用技巧!
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
作为一名开发者,我总是会反思自己编写和阅读的代码。在这个过程中,我积累了不少实用技巧。
在这篇文章中,我将分享一些与数组方法相关的技巧。
虽然我这里讨论的是数组方法,但这些技巧也适用于其他情况。编写代码时请牢记这些技巧。
提示
有些是更好的命名规范和应用规则。
有些是让代码更简洁的小技巧。
大多数都带有很强的个人观点😄。
我尝试将它们分为一般性建议和具体建议,以便更容易理解和用作参考。
📚目录
一般提示
给你的函数命名
未来的代码阅读者不应该需要费心去思考某个函数的作用。请做个好人,使用有意义的函数名。
const numbers = [1,2,3,4];
// BAD - I need to think about what this function is doing
numbers.filter(num => num % 2 === 0);
// GOOD - I can read this line and immediately tell what's going on.
const isEven = num => num % 2 === 0);
numbers.filter(isEven);
不要将参数从一个函数传递给另一个函数。
数组方法会调用接收到特定参数的函数。无需显式地将这些参数传递给另一个函数。
const numbers = [1,2,3,4];
const multiplyByTwo = num => num * 2;
// BAD - There is no need to explicitly pass num.
numbers.map(num => multiplyByTwo(num));
// GOOD
numbers.map(multiplyByTwo);
使用部分应用程序
你需要的不仅仅是数组方法传递的数据吗?使用部分应用。
不知道什么是部分申请?看看这个。
const numbers = [1,2,3,4];
// BAD
const multiplyBy = (num, multiplier) => num * multiplier;
numbers.map(num => multiplyBy(num, 2));
const multiplyBy = multiplier => num => num * multiplier;
// GOOD
numbers.map(multiplyBy(2));
// GOOD - more verbose
const multiplyByTwo = multiplyBy(2);
numbers.map(multiplyByTwo);
断开长链或将其分配给变量/函数
当我看到三四层数组方法串联在一起,却没有任何迹象表明结果时,我会问自己:为什么?为什么我必须逐行检查,才能弄清楚结果是什么?
有两种方法可以解决这个问题。
- 打破链条——将每一行的结果赋值给一个变量,并对该变量进行操作。
- 将结果赋值给一个具有有意义名称的函数或变量。
假设我们要找到所有 18 岁以上的员工,给他们随机发放奖金,然后计算他们的工资总和。
const employees = [{name:"bruce banner", age:21, salary: 1500}, ...];
const isAboveEighteen = (employ) => employ.age > 18;
const addRandomBonus = (employ) => ({...employ, salary: employ.salary*(Math.random() + 1)});
const toSumOfSalaries = (sum, employ) => sum + employ.salary;
// BAD - I need to think how each line effect the next one and what will be the outcome
employees
.filter(isAboveEighteen)
.map(addRandomBonus)
.reduce(toSumOfSalaries);
// Breaking the chain
const aboveEighteenEmployees = employees.filter(isAboveEighteen);
const salariesWithBonus = aboveEighteenEmployees.map(addRandomBonus);
const sumOfSalaries = salariesWithBonus.reduce(toSumOfSalaries);
// Assign the result
// *If we need to reuse this then we would use a function
const aboveEighteenAfterBonusSumOfSalaries = employees
.filter(isAboveEighteen)
.map(addRandomBonus)
.reduce(toSumOfSalaries);
地图提示
将类型 A 转换为类型 B 时,请使用“toB”作为函数名,并使用“A”或“fromA”作为函数参数。
例如,假设我们想将错误代码转换为人类可读的错误信息。
在这个例子中,A是
errorCode,B是errorMessage。
const errorCodes = [1, 2, 3];
const errorCodesMessages = {1: "your code is great!", 2: "your code is awesome!".... };
const toErrorMessage = (fromErrorCode) => errorCodesMessages[fromErrorCode];
errorCodes.map(toErrorMessage);
在这个例子中,从代码中可以清楚地看出我们的意图。toErrorMessage函数表明我们正在将 A 转换为B。我们的数组应该告诉我们正在处理错误代码。但是,如果我们搞砸了数组的命名,那么从函数参数中
就会看出我们正在处理A。
执行操作
我们可以使用与上例相同的约定,但这感觉有点别扭,也过于冗长。
所以,对于动作,我们只需直接陈述我们正在执行的动作即可。
假设我们要给一个用户数组添加一个唯一 ID。
const users = [{name: "john doe", email: "johnDoe@evilcorp.com"}, ....];
const addId = obj => ({...obj, id: uuid()});
users.map(addId);
滤嘴
只有在它是完成这项工作的合适工具时才使用它。
什么时候才是filter合适的工具?当你想根据某个条件获取数组的子集时。
在其他情况下,例如你想获取特定成员、断言某个条件对至少一个成员或所有成员都成立时,可以使用 `__getitem__`、`__getitem__`find或some` every__getitem__`——了解你的工具以及何时使用它们。
const peoples = [{ name: "Bruce Wayne", country: "USA", city: "Gotham",... }, ...];
// find
const isBruceWayne = person => person.name === "Bruce Wayne";
peoples.filter(isBruceWayne)[0]; // BAD
peoples.find(isBruceWayne); // GOOD
// some
const isFromTheUSA = person => person.country === "USA";
// has peoples from USA?
!!peoples.filter(isFromTheUSA)[0]; // BAD
peoples.some(isFromTheUSA); // GOOD
// every
const isNotFromTheUSA = person => person.country !== "USA";
// everyone from the USA?
!peoples.filter(isNotFromTheUSA)[0] // BAD
peoples.every(isFromTheUSA) // GOOD
把它说得像个问题
这条规则适用于所有条件语句。
如果函数的返回值是 ` booleannull`true或false`undefined`,那么我们应该把函数写得像一个问句。
const numbers = [1,2,3,4]
// BAD - Reads like a statment
const even = num => num % 2 === 0);
numbers.filter(even);
// GOOD - Reads like a question
const isEven = num => num % 2 === 0);
numbers.filter(isEven);
一些常见的前缀有is,,has……should
一次检查多个条件
如果要一次性检查多个条件,可以使用ramda anyPass和allPass函数;如果要结合多个条件并遵循SRP ,则可以使用这些函数。
例如,假设我们想要得到所有大于 10 的偶数,或者所有小于 10 的奇数。
import R from 'ramda';
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
// This is a simple example but you can imagine how it can get out of control.
const isAValidNumber = num => {
if (num % 2 === 0 && num > 10) {
return true;
}
if (num % 2 && num < 10) {
return true;
}
};
// Good - We've split our logic into small, reusable functions, that do one thing.
const isEven = num => num % 2 === 0;
const isOdd = num => !isEven(num);
const isBiggerThanTen = num => num > 10;
const isSmallerThanTen = num => num < 10;
const isValidNumber = R.anyPass([
R.allPass([isBiggerThanTen, isEven]),
R.allPass([isSmallerThanTen, isOdd]),
]);
numbers.filter(isValidNumber);
我们添加了一些代码,但你看,我们想要实现的目标非常明确!
如果你不喜欢函数式编程方法,或者你的团队不熟悉函数式编程ramda,还有其他方法可以得到相同的结果,例如将我们编写的函数插入到if函数的第一个实现中的语句中isValidNumber。
有什么建议可以分享吗?
以上就是我使用数组方法的一些心得体会。
你有什么自己的技巧吗?这篇文章里有没有你发现的新东西或有趣的内容?
欢迎留言并分享给你的开发者朋友们😄