【译】如何使用ES6箭头函数使JavaScript代码更易读

原文:https://medium.freecodecamp.org/arrow-functions-in-javascript-2f8bf7df5077

箭头函数是搭建现代 Web 应用程序中一种新的基础构件。在本文中,你将会学习箭头函数如何使代码更简洁、使“this”关键字更易于管理;还将学习隐式返回、使用箭头函数记录日志以及隐式返回结合对象一起使用的方法。

如果你喜欢通过视频而不是文本学习,这是视频链接:https://youtu.be/dB1KA-yz65s 。

与常规函数相比,使用箭头函数有两个优点。首先,箭头函数使代码更简洁。其次,使用箭头函数使得管理“this”关键字更容易。

我见过那些初次学习箭头函数的开发者,对于他们来说箭头函数本身的概念并不是很难理解。你可能已经熟悉函数、函数特性、函数用例等内容,但是当你第一次接触箭头函数语法时还是比较容易困惑的。因此,我们要慢慢来,首先介绍一下与常用函数相比,箭头函数的语法是怎样的。

下面是一个基本函数声明和函数表达式的范例:

// 函数声明
function add (x,y) {
  return x + y;
}
// 函数表达式
var add = function (x,y) {
  return x + y;
}

现在,如果我们想要将函数表达式改为一个箭头函数,我们可以这样做:

//常用函数表达式
var add = function (x,y) {
  return x + y;
}
//箭头函数
var add = (x,y) => {
  return x + y;
}

使用箭头函数最难的是习惯它的语法形式,一旦你适应了并坚持下去,相信你将会深入的理解并掌握它。

现在,你可能想知道使用箭头函数带来的所有好处。其实,上面的例子并没有充分显示出箭头函数的优势,我发现当使用匿名函数时,箭头函数的优势发挥的最为明显。通过查看另一个使用 .map  的基本例子,可以让我们对箭头函数的语法更为熟悉。

users.map(function () {
})
users.map(() => {
})

好了,熟悉的差不多了,接下来让我们进行深入的学习。

假设我们有一个函数:getTweets ,该函数接收用户 id 作为入参,通过访问一个简单的 API 接口后,返回所有星数和转发次数超过 50 的 Twitter 用户。

使用 promise 构造函数定义,这个函数看起来是这样的:

function getTweets (uid) {
  return fetch('https://api.users.com/' + uid)
    .then(function (response) {
      return response.json()
    })
    .then(function (response) {
      return response.data
    }).then(function (tweets) {
      return tweets.filter(function (tweet) {
        return tweet.stars > 50
      })
    }).then(function (tweets) {
      return tweets.filter(function (tweet) {
        return tweet.rts > 50
      })
    })
}

是的,功能虽然实现了,但是这个函数还不是最完美的。尽管具体的实现步骤是紧凑的,但是想法有点过于平常了。根据上述对箭头函数的理解,我们来看一下可以怎样改进 getTweets 函数。

function getTweets (uid) {
  return fetch('https://api.users.com/' + uid)
    .then((response) => {
      return response.json()
    })
    .then((response) => {
      return response.data
    }).then((tweets) => {
      return tweets.filter((tweet) => {
        return tweet.stars > 50
      })
    }).then((tweets) => {
      return tweets.filter((tweet) => {
        return tweet.rts > 50
      })
    })
}

好的,酷极了。但是除了不需要写 function 之外,和上面的写法几乎是一致的。虽然这种写法是有益的,却没有什么值得炫耀的。还是让我们来看一下使用箭头函数带来的下一个优点:“隐式返回”。

使用箭头函数时,如果你的函数是一个“简单的函数体”(对于单行函数的称呼),那么你可以省略“return”关键字,该值会自动(隐式)返回。

所以,前面的 add 示例修改后如下:

//普通函数表达式
var add = function (x,y) {
  return x + y;
}
//使用箭头函数的隐式返回
var add = (x,y) => x + y;

更重要的是,修改后的 getTweets 函数示例如下:

function getTweets (uid) {
  return fetch('https://api.users.com/' + uid)
    .then((response) => response.json())
    .then((response) => response.data)
    .then((tweets) => tweets.filter((tweet) => tweet.stars > 50))
    .then((tweets) => tweets.filter((tweet) => tweet.rts > 50))
}

该代码不仅容易编写,更重要的是,它更容易阅读。

如果箭头函数只有一个入参,那么我们可以做的进一步的修改是省略该参数周围的 ()。考虑到这一点, getTweets 函数可以这样写:

function getTweets (uid) {
  return fetch('https://api.users.com/' + uid)
    .then(response => response.json())
    .then(response => response.data)
    .then(tweets => tweets.filter(tweet => tweet.stars > 50))
    .then(tweets => tweets.filter(tweet => tweet.rts > 50))
}

总的来说,我认为上面做的每一种改变都是巨大的进步。

箭头函数的另一个优点就是如何管理“this”关键字。如果你对“this ”关键字不熟悉,我推荐看一下 WTF is this 。

让我们看下使用 ES6 语法(译者注:bind为ES5语法)编写的典型 React 代码。

class Popular extends React.Component {
  constructor(props) {
    super();
    this.state = {
      repos: null,
    };
this.updateLanguage = this.updateLanguage.bind(this);
  }
  componentDidMount () {
    this.updateLanguage('javascript')
  }
  updateLanguage(lang) {
    api.fetchPopularRepos(lang)
      .then(function (repos) {
        this.setState(function () {
          return {
            repos: repos
          }
        });
      });
  }
  render() {
    // Stuff
  }
}

当组件加载时,它会向 API ( Github API )发出请求来获取当下 JavaScript 最流行的库。当组件获得库的数据时,它将会接受并更新其本地状态,至少我们希望它能这样做。很遗憾,实际上它并没有这样做。相反,我们会得到一个错误,你能指出上面代码中的 bug 吗?

上面代码将会抛出的错误是“无法读取未定义的 setState ”。讨论为什么出现这个错误已经在本篇文章讨论范围之外了(如果你需要请看 WTF is this )。另外,  ES5 中典型的解决方法是使用了 .bind 来绑定 this ,具体内容如下:

class Popular extends React.Component {
  constructor(props) {
    super();
    this.state = {
      repos: null,
    };
this.updateLanguage = this.updateLanguage.bind(this);
  }
  componentDidMount () {
    this.updateLanguage('javascript')
  }
  updateLanguage(lang) {
    api.fetchPopularRepos(lang)
      .then(function (repos) {
        this.setState(function () {
          return {
            repos: repos
          }
        });
      }.bind(this));
  }
  render() {
    // Stuff
  }
}

至于箭头函数为什么这么受欢迎的第二个主要的优点是它不会创建自己的上下文环境。这意味着“this”关键字在特定函数中使用时无需考虑上下文环境,也不用担心被修改。所以在 updateLanguage 方法中使用箭头函数时我们不需要再担心 this ,意味着不必再使用 .bind 绑定它了。

updateLanguage(lang) {
  api.fetchPopularRepos(lang)
    .then((repos) => {
      this.setState(() => {
        return {
          repos: repos
        }
      });
    });
}

引申阅读

至此,我们的讨论已经覆盖了箭头函数中“必须掌握”的所有知识点。然而,有两点你可能想知道的内容,我认为值得一提。

回过头看一下 updateLanguage 方法,如果我们想在 setState 的回调函数中隐式返回对象,该如何做呢?你最先想到的会是删除 return 语句,然后返回一个对象。

api.fetchPopularRepos(lang)
  .then((repos) => {
    this.setState(() => {
      repos: repos
    });
  });

正如你可能猜到的那样,这样做的问题是该语法与创建函数的语法完全相同。 JavaScript 无法神奇地分辨出你是想创建一个函数还是想返回一个对象,因此它会抛出一个错误。为了解决这个问题,我们可以将对象包裹在 () 中。

api.fetchPopularRepos(lang)
  .then((repos) => {
    this.setState(() => ({
      repos: repos
    }));
  });

使用这个方法,现在我们可以使用箭头函数隐式的返回一个对象。

接下来,比如我们想在 setState 内部通过记录状态值来检查组件之前的状态。如果让你写这个 setState 函数,你将如何来记录 nextState 值呢?

this.setState((nextState) => ({
  repos: repos
}));

最显而易见的做法是将代码中的隐式返回改为显式返回,创建一个函数,然后在这个函数体内记录。

this.setState((nextState) => {
  console.log(nextState)
  return {
    repos: repos
  }
});

这种写法有点糟糕。还有个更好的方法,使用 || 操作符,为了维持你的代码整洁,你可以这样写:

this.setState((nextState) => console.log(nextState) || ({
  repos: repos
}));

最初我将这篇文章放到了 tylermcginnis.com 网站上,作为我“Modern JavaScript”课程的一部分。

你可以通过 Twitter 联系我: @tylermcginnis 。

文章来源:

Author:sunyinfeng
link:https://jdc.jd.com/archives/5142