英文 | https://dev.to/r0r71z/javascript-write-cleaner-code-with-functional-programming-279a
翻译 | web前端开发 (ID:web_qdkf)
作为一个Web全栈开发人员,我花了很多时间来学习和写JavaScript代码,但这些代码依然写得很差,所以就需要花更多的时间去理解它。
确实,当我们需要在基于JS的项目中重构一些遗留或者未维护的代码片段时,常会感到沮丧,因为它们缺少JSDocs,它具有的混合变量声明模式const, let, var,函数声明从function f() {}到var f = function() {}或const f = () => {},更重要的是,所有代码一个模块中的一个功能包含在单个功能主体中。
让我们看下面的代码:
var fetch = require('node-fetch'); // if using NodeJS
function articles () {
var arr = [];
return fetch('https://dev.to/api/articles').then(function(a) {
if (a) {
return a.json().then(function(b) {
if (b) {
b.forEach((c) => {
if (c.tag_list.indexOf('javascript') !== -1 && c.tag_list.indexOf('node') !== -1) {
arr.push(c);
}
});
return arr;
}
});
}
});
}
articles().then(function(d) {
console.log(d);
});
在上面的示例中,我们尝试使用DEV API请求带有'javascript'和'node'标签的有用文章。随着“完成”的定义,随着时间的推移而发生巨大变化,如今,我们不仅只是为了完成工作,而使代码具有可读性和可维护性。
尽管我们可以使用代码注释或JSDocs来解释此代码中的每一行在做什么,但是我们应该考虑利用功能性编程语言的功能。由于我们可以抽象出所使用的功能,因此我们也可以使用一些词来命名它们,从而使代码具有自我描述性。这样,我们就可以将文档保存为要导出的功能。
让我们尝试按照以下步骤重构API调用:
- 优化代码通常涉及使用最新的语言可用功能。虽然我们可能并不全部了解它们,但是到了此时,所有JavaScript开发人员都应该知道ES6中引入的功能。因此,作为第一步,我想我们应该禁用
var
代码中的所有声明,对于本示例,这些声明可以与互换const
。
const fetch = require('node-fetch'); // <-
function articles () {
const arr = []; // <-
...
}
articles().then(function(d) {
console.log(d);
});
- 你们中的一些人可能会同意,也有一些人可能会不同意这样的做法,但是我认为,最初在编程中导致真正困难的是正确地命名。这是我们工作的重要组成部分。我们的主要功能名为
articles
,这是什么意思?这没有任何意义,因为此函数名称未表达任何告诉我们它在做什么的动作。我认为我们应该能够为此功能找到一个更好的名称,因为我们已经知道该功能的目的。
function fetchDevArticles () {
...
}
fetchDevArticles().then(function(d) {
console.log(d);
});
新名称似乎合适,但不准确。如果我们想为该函数进行准确的功能命名时,它又会变得非常冗长,以至于阅读起来会很烦人。例如,fetchDevArticlesAndFilterThemByJavascriptAndNodejsTags绝对很难阅读。
- 我们的函数和变量命名成为一个问题,因为主函数负责同步执行多项操作。在函数式编程中,我们可以为函数指定与其确切行为功能相关的名称。因此,我们可以将main函数拆分为多个描述自身的较小函数。
const fetch = require('node-fetch'); // if using NodeJS
const arr = [];
function pushFilteredArticlesToAuxArray (c) {
if (
c.tag_list.indexOf('javascript') !== -1
&& c.tag_list.indexOf('node') !== -1
) {
arr.push(c);
}
}
function filterAndReturnValues (b) {
if (b) {
b.forEach(pushFilteredArticlesToAuxArray);
return arr;
}
}
function fetchJSDevArticles () {
return fetch('https://dev.to/api/articles').then(function(a) {
if (a) {
return a.json().then(filterAndReturnValues);
}
});
}
fetchJSDevArticles().then(function(d) {
console.log(d);
});
在不添加代码注释或JSDocs的情况下,我们的代码看起来会更好识别。但是,代码仍然存在一些问题。如你所见,我使用模块数组变量只是为了过滤另一个数组并返回输出。
- 尽管目前可行,但是如果我们能找到更好的数组方法来帮助我们的话,代码可以变得更加简单。
const fetch = require('node-fetch');
const tagsToFilter = ['javascript', 'node'];
const isIncludedIn = (arr) => tag => arr.includes(tag);
const byTags = (tags) => (article) => tags.every(isIncludedIn(article.tag_list));
const filterAndReturnValues = (articles) => articles.filter(byTags(tagsToFilter));
function fetchJSDevArticles () {
return fetch('https://dev.to/api/articles').then(function(a) {
if (a) {
return a.json().then(filterAndReturnValues);
}
});
}
fetchJSDevArticles().then(function(d) {
console.log(d);
});
这就是巨大的差异!我使用了几种简单的数组方法来减少代码中的行数。而且,我使用箭头函数是因为它允许我们编写单行帮助函数。
现在,我们的代码在字面上更具可读性,因为我为每个函数确切地命名了它的功能。但是还有更多工作要做。
const fetch = require('node-fetch');
const tagsToFilter = ['javascript', 'node'];
const devArticlesApiURL = 'https://dev.to/api/articles';
const isIncludedIn = (arr) => tag => arr.includes(tag);
const byTags = (tags) => (article) => tags.every(isIncludedIn(article.tag_list));
const filterAndReturnValues = (articles) => articles.filter(byTags(tagsToFilter));
const fetchJSDevArticles = () =>
fetch(devArticlesApiURL)
.then(response => response.json())
.then(filterAndReturnValues)
.catch(console.log);
fetchJSDevArticles().then(console.log);
这次,我通过将所有回调都转换为单行箭头函数来减少代码,避免使用花括号和return语句。对我来说,这看起来已经不错了,但是基于这些技巧,你将有动力尝试进一步减少代码,至少我希望如此。
结论
为了编写简洁的代码,函数式编程是我们作为JavaScript开发人员需要意识到的一个范例。不要想着自己能够编写出完美的代码,尤其是初学者,这样你可以有机会从错误中成长。但是你应该尽力做到最好,要牢记总有一些可以改进的地方。
总结说明:
- ES6很重要。
- 你尝试执行的操作可能有一个数组方法。
- 如果没有,请尝试lodash :)
- 代码注释并不总是需要,让代码更具可读性这个比注释更加重要。
- 力争最好。