清洁 JavaScript - 10 个技巧
我们都经历过这种情况。看着一周前、一个月前、一年前的 JavaScript,我们不禁会想,自己最初编写它时喝的是什么咖啡。🤷♂️
很多时候,这取决于三件事:完成工作的可用时间、旧的最佳实践,或者出现了编写代码的新模式和原则。
然而,我们可以做一些经得起时间考验的事情,并且能够帮助任何接触我们代码库的人,无论是未来的我们,还是刚刚入职的初级开发人员。我整理了以下 10 条我在编写 JavaScript 时喜欢使用的技巧,以保持其简洁易读。
复杂条件句?array.some()
来拯救你
好的,我们有一个 if 语句,它相当冗长。我们是否应该执行一段代码取决于很多因素。或者,这些条件是由我们应用程序中的其他逻辑动态生成的。像这样的 if 语句并不罕见:
if(condition1
|| condition2
|| condition3 === 'myEquality'
|| ...
|| conditionN.includes('truthy')) {
// do something
}
这可真够棘手的!🤢
怎么解决啊!简单!数组!
const myConditions: boolean = [];
myConditions.push(condition1);
myConditions.push(condition2);
myConditions.push(condition3 === 'myEquality');
myConditions.push(conditionN.includes('truthy'));
if (myConditions.some((c) => c)) {
// do something
}
通过创建一个条件数组,我们可以检查其中任何一个条件是否为真,如果为真,则执行 if 语句。这也意味着,如果我们需要动态或通过循环生成条件,只需将其推送到条件数组即可。我们也可以轻松地删除条件,只需将其注释掉myCondition.push()
或完全删除即可。
注意:这将创建一个数组并根据条件运行循环,因此预计会产生很小的、通常不易察觉的性能影响
数组用于“或”,但“与”又如何呢?array.every()
快来试试吧!
与上面的提示几乎相同,只是不只是检查任何一个条件,而是array.every()
检查每个条件是否真实!
const myConditions: boolean = [];
myConditions.push(condition1);
myConditions.push(condition2);
myConditions.push(condition3 === 'myEquality');
myConditions.push(conditionN.includes('truthy'));
if (myConditions.every((c) => c)) {
// do something
}
就这么简单!
没有魔法弦
不确定什么是魔法字符串?它本质上是期望输入等于任意字符串值,该字符串值可能代表也可能不代表实现,并且可能在其他地方使用,因此重构起来很困难,并且容易导致代码出错。
以下是魔法字符串的实际示例:
function myFunc(input) {
if (input === 'myString') {
// do something
}
}
myFunc('myString'); // works
myFunc('myStrung'); // doesn't work
从上面的例子中可以看出,使用myString
魔法字符串很容易导致 bug。这不仅是因为开发人员的拼写错误,而且,如果你myFunc
通过更改它所期望的魔法字符串来更改,那么所有调用它的函数myFunc
也都需要更改,否则程序会完全崩溃:
function myFunc(input) {
if (input === 'bar') {
// do something
}
}
myFunc('myString'); // no longer works
myFunc('myStrung'); // still doesn't work
我们可以很容易地解决这个问题,但创建一个共享对象,该对象使用相应的键值设置来定义这些魔法字符串:
const MY_FUNC_ARGS = {
DoSomething: 'bar',
};
function myFunc(input) {
if (input === MY_FUNC_ARGS.DoSomething) {
// do something
}
}
myFunc(MY_FUNC_ARGS.DoSomething); // works and is refactor proof!
在对象中定义魔法字符串不仅可以为代码提供实现上下文,还可以防止因拼写错误和重构而导致的错误!💪
数组解构返回
我不确定你的情况,但我确实有时候希望能够从一个函数返回多个值,我要么选择返回一个数组,要么选择返回一个包含这些信息的对象。有一段时间,我倾向于避免返回数组,因为我讨厌看到这样的语法:
const myResult = myFunc();
if (myResult[0] === 'yes' && myResult[1] === 2) {
// Do something
}
数组索引代表什么,完全没有上下文myResult
,理解这里发生的事情有点困难。不过,使用数组解构,我们可以让它更具可读性🤓。看看这个:
const [userAnswer, numberOfItems] = myFunc();
if (userAnswer === 'yes' && numberOfItems === 2) {
// Do something
// Refactor that magic string to use an Object 🤫
}
这难道不让工作变得容易得多吗?
对象解构返回
好的,数组解构很棒,我们可以从中了解发生了什么,但如果我们只关心函数返回的一些内容,而我们关心的内容与返回的数组的顺序不同,该怎么办?
返回一个对象可能是一个更好的解决方案,以便我们可以对其执行对象解构:
function myFunc() {
return {
userAnswer: 'yes',
numberOfItems: 2,
someKey: 10,
};
}
const { numberOfItems, someKey } = myFunc();
if (numberOfItems === 2 || someKey === 10) {
// Do Something
}
现在,我们不需要关心返回数组中项目的存在顺序,并且可以安全地忽略我们关心的值之前的任何值🔥
许多文件与通用文件
也就是单一职责原则……
好吧,听我说完。有了打包工具,创建只做一件事的 JS 文件比创建几个做很多事的通用文件要容易得多,而且值得。
如果你有一个名为 的文件models.js
,其中包含定义应用中所有模型结构的对象,请考虑将它们拆分成单独的文件!
请看以下示例:
一位初级开发人员正在尝试处理与添加 TODO 项相关的 API 请求。他们必须深入models.js
研究 1000 行代码才能找到该AddTodoRequest
对象。
初级开发人员打开data-access/todo-requests.js
并查看AddTodoRequest
文件顶部的内容。
我知道我更喜欢哪一个!好好想想吧。看看你的文件,看看它们是不是做了太多事情。如果是,就把这些代码拆分成一个更合适的文件。
命名你的 hack
好吧,你正在尝试做一些奇怪的事情,但没有合适的方法来实现它。也许你需要为特定的浏览器(比如IE)添加一个变通方案。你可能清楚
地知道你用一段专门用于此变通方案的代码做了什么,但在你之后的人可能完全不知道,甚至一个月后你也不知道。
帮自己,也帮大家一个忙,把这个解决方法说出来!做法很简单,要么把它单独拉到一个函数里,要么创建一个局部变量,并给它起个合适的名字:
function myIE11FlexWorkaround() {
/// Workaround code
}
function main() {
myIE11FlexWorkaround();
const ie11CssVarsPonyFill = (() => {
/* some pony fill code */
})();
}
现在,任何在你之后来的人都会确切地知道你在尝试什么!🚀
较小的方法
这毋庸置疑。我知道我们都希望方法简短,但实际上,由于时间限制,这说起来容易做起来难。但是,如果我们反过来想,如果我们正在编写单元测试,我知道我更愿意为小方法而不是大方法编写单元测试。
我更希望看到这个:
function myLargeComplexMethod() {
const resultA = doSomePiece();
const resultB = transformResult(resultA);
const apiData = mapToApiData(resultB);
const response = doApiRequest(apiData);
return response;
}
比起试图一次性完成所有这些独立单元的方法,我们还可以为每个较小的单元编写一些单元测试,并编写一个非常简单的测试来myLargeComplexMethod
确保这些较小的单元被正确调用。我们不需要关心它们是否正常工作,因为与这些较小单元相关的单元测试已经为我们确保了这一点。
for ... of
对比forEach
我想这不言而喻,但我们都经历过回调地狱,它.forEach()
让我想起太多回调地狱,甚至不想去想它。而且,我们现在有了一个非常简洁的方法可以循环遍历所有类型的 Iterable,所以为什么不使用它呢?
让我们forEach()
比较一下 a 和 a for ... of
,然后你就可以自己做了。
const myArrayOfObjects = [{ id: 1 }, { id: 2 }, { id: 3 }];
const myMapOfObjects = new Map([
[1, { id: 1 }],
[2, { id: 2 }],
[3, { id: 3 }],
]);
// forEach()
myArrayOfObjects.forEach((obj, index) => {
// do some code
});
Array.from(myMapOfObjects.values()).forEach((obj, index) => {
// do some code
});
// For ... of
for (const obj of myArrayOfObjects) {
// do some code
}
for (const obj of myMapOfObjects.values()) {
// do some code
}
就我个人而言,我更喜欢for...of
有两个原因:
- 你可以立即看到,其目的是循环遍历数组中的所有项目
- 它对于代码库中的任何可迭代对象都是一致的,无论是数组还是映射
forEach
确实具有在回调中提供索引的好处,因此如果这对您有用,那么最好采用该方法。
移除try-catch
方块
最后,说说我个人的一点小抱怨。try-catch
块。我个人觉得它们被过度使用了,用错了地方,它们做了太多事情,或者捕获了它们原本不应该捕获的错误。这都归咎于它们的结构和外观。
我在这里对我不喜欢它们的原因有更长的描述,但简要地说,这里有一个有问题的 try-catch:
try {
const myResult = myThrowableMethod(); // I expect this one to potentially throw
const response = transformResult(myResult);
const answer = doRequestThatThrowsButIWasntAware(response); // I didn't realise this could have thrown
} catch (error) {
console.error(error); // Wait... Which method threw!?
// do something specifc to handle error coming from myThrowableMethod
// without expecting the error to be from a different method
}
// Ok, let me refactor so I know for certain that I'm only catching the error I'm expecting
let myResult;
try {
myResult = myThrowableMethod();
} catch (error) {
// do something specifc to handle error coming from myThrowableMethod
}
const response = transformResult(myResult);
const answer = doRequestThatThrowsButIWasntAware(response);
告诉我你不认为这两者都有问题...如果你的错误处理逻辑很复杂,它只会分散读者对你的方法想要实现的目标的注意力。
我创建了一个小型库来解决这个问题:no-try。有了它,我们可以将上述代码转换为:
function handleError(error) {
console.log(error);
}
const [myResult] = noTry(() => myThrowableMethod(), handleError);
const response = transformResult(myResult);
const answer = doRequestThatThrowsButIWasntAware(response);
我个人觉得这样干净多了。不过这都是个人意见!
我希望您从本文中获得一些有用的提示,帮助您编写 JavaScript!
如果您有任何疑问,请随时在下面提问或在 Twitter 上联系我:@FerryColum。
鏂囩珷鏉ユ簮锛�https://dev.to/coly010/clean-javascript-10-tips-37f4