发布于 2026-01-06 3 阅读
0

JavaScript:使用函数式编程编写更简洁的代码

JavaScript:使用函数式编程编写更简洁的代码

作为一名全栈 Web 开发人员,我花了很多时间编写和阅读 JavaScript 代码,这些代码经常写得很差,以至于我需要花费比预期更多的时间才能理解。

确实,当我们需要在基于 JS 的项目中重构一些遗留的、无人维护的代码片段时,会感到非常沮丧,因为它们缺乏JSDocs,变量声明模式混杂const, let, var,函数声明从 `<std::function>` 到function f() {}` <std::function>`var f = function() {}或 ` <std::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”标签的文章;请求成功了。那么,问题出在哪里呢?嗯,随着“完成”的定义不断变化,如今衡量我们完成工作的能力,不仅在于能否让工作正常运行,还在于工作内容是否易读、有意义且易于维护。

虽然我们可以使用代码注释或 JSDoc 来解释代码中每一行的作用,但我们更应该考虑利用函数式编程语言的强大功能。由于我们可以抽象所使用的函数,因此我们也可以使用一种自描述性的词汇来命名它们。这样,我们就可以只为需要导出的函数编写文档。

让我们按照以下步骤重构我们的 API 调用:

  • 优化代码通常需要使用最新的语言特性。虽然我们可能并不了解所有特性,但到目前为止,所有 JavaScript 开发者都应该了解 ES6 中引入的特性。因此,作为第一步,我认为我们应该删除var代码中的所有声明,例如,这些声明可以与 `.` 互换const
const fetch = require('node-fetch'); // <-

function articles () {
  const arr = []; // <-
  ...
}

articles().then(function(d) {
  console.log(d);
});
  • 有些人会同意,有些人不会,但我认为编程初期最难的事情之一就是给函数命名。然而,这却是我们工作的重要组成部分。比如,我们的主函数被命名为 `main` articles,这是什么意思呢?这个名字毫无意义,因为它没有表达任何动作(动词),无法告诉我们它具体做什么。我认为我们应该能为这个函数找到一个更好的名字,因为我们已经知道它应该做什么。
...

function fetchDevArticles () {
...
}

fetchDevArticles().then(function(d) {
  console.log(d);
});

新名称看似合适,但并不准确。如果我们想根据这个函数的实际功能来命名,代码会变得非常冗长,难以阅读。例如,这样的代码fetchDevArticlesAndFilterThemByJavascriptAndNodejsTags肯定难以理解。

  • 由于主函数负责同步执行多项任务,因此函数和变量的命名会成为一个问题。在函数式编程中,我们可以为函数取一个与其具体行为相关的名称。这样,我们就可以将主函数拆分成多个描述自身功能的子函数。
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);
});

太棒了!我们的代码看起来更简洁了,而且没有添加代码注释或 JSDoc。不过,代码仍然存在一些问题。正如你所看到的,我使用了一个模块数组变量,仅仅是为了过滤另一个数组并返回结果。

  • 虽然目前这种方法可行,但如果我们能找到更好的数组方法来帮助我们,代码可以变得更加简洁。
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 :)
  • 代码注释并非提高代码可读性的必要条件。
  • 力争做到最好。
文章来源:https://dev.to/r0r71z/javascript-write-cleaner-code-with-functional-programming-279a