首页 » 漏洞 » 使用 Flow 写更好的 JavaScript 代码

使用 Flow 写更好的 JavaScript 代码

 
文章目录

你是不是常常发现自己在跟踪代码中的一个 bug,最后发现的错误只是某些本应该可以避免的简单问题呢? 可能你只是按照错误的顺序传递了参数,或者也许是你在尝试传递一个字符串而不是一个数字?JavaScript 的弱类型系统,以及试着将变量在不同类型间强制转换的意图可能就是一整类 bug 的来源,而这些 bug 并不会存在于静态类型语言之中。

Flow 是一个用于 JavaScript 的静态类型检查器,最早由 Facebook 在 2014 年的  Scale 大会 上推出。它的目标是不需要开发人员去频繁的修改实际代码,就能找到 JavaScript 代码中的类型错误,从而大大节省这方面时间与精力的消耗。同时,它也向 JavaScript 增加了额外的语法,以提供给开发者更多的控制能力。

在本文中,我会向你介绍 Flow 以及它的主要特性。我们将介绍如何设置它,如何在代码中添加类型注释,以及如何在运行代码时自动删除这些注释。

安装

Flow 目前可运行于 Mac OS X, Linux (64 位) 以及 Windows (64 位) 系统上。安装它最简单的办法就是通过 npm:

npm install --save-dev flow-bin

然后将它添加到工程的 package.json 文件, 就在 scripts 部分下面:

"scripts": {   "flow": "flow" }

完成了此项工作,我们就做好了继续探索其功能的准备。

入门

一个被命名为 .flowconfig 的配置文件必须被放在工程文件的根目录之下。通过运行如下命令,我们可以创建出一个空的配置文件:

npm run flow init

放好了这个配置文件,你就能在终端上执行如下命令,来在工程文件夹或者任意子目录下的代码上运行 ad-hoc 检查了:

npm run flow check

不过,这并非使用 Flow 的最有效方式,因为这样做会导致 Flow 每次都会重新检查整个工程文件结构。所以我们可以使用 Flow 服务器。

Flow 服务器会对文件进行增量检查,意思就是它只会检查被修改了的部分。服务器可以通过在终端上执行 npm run flow 命令来启动。

在你第一次运行该命令的时候,服务器将会启动并显示初始的测试结果。该操作运行很快并且是增量的工作流。每次在你想要知道测试结果的时候,就在终端上运行 flow。完成了编码环节之后,可以使用 npm run flow stop 来终止服务器的运行。

Flow 的类型检查是 opt-in 的。这就意味着你无须一次性完成对所有代码的检查。Flow 只会为你检查那些你想要检查的那些文件,怎么让 Flow 知道你想要检查哪些文件呢?只需要在这些 JavaScript 文件顶部添加 @flow 作为注释就可以了:

/*@flow*/

这有利于将 Flow 集成到一个现有工程中,因为你可以选择你想要检查的文件,然后逐步解决所发现的问题。

类型推断

通常,类型检查有以下两种方法:

  • 通过注解: 我们指定期望的类型并把它作为代码的一部分,类型检查器会基于这些期望来对代码进行评估。

  • 通过代码推断: 工具聪明到可以通过查看代码被使用的上下文来推断出类型,并基于此对代码进行检查。

使用注解的话,我们就得编写一些额外的代码,它们只会在开发期间被用到,最终在浏览器加载的 JavaScript 构建版本中被去掉。这样做会增加一些额外的工作,也就是通过加入额外的类型注解来让代码“听话”。

而在第二个场景中,代码随时“待命”, 故而缩减了程序员的工作量。我们不需要对代码进行修改,因为它会自动地推断出表达式的数据类型。这就是 类型推断 , 它是 Flow 最重要的特性之一。

为了描述该特性,我们来看看如下示例:

/*@flow*/  function foo(x) {   return x.split(' '); }  foo(34);

这段代码会在你运行 npm run flow 命令的时候在终端上报错, 因为函数 foo() 需要的是一个字符串作为参数,而我们传入的却是一个数字。

报错提示大概如下:

index.js:4   4:   return x.split(' ');                 ^^^^^ property `split`. Property not found in   4:   return x.split(' ');               ^ Number

它明确说明了错误的位置和发生的原因。当我们将参数从数字修改为字符串,报错就会消失,如下:

/*@flow*/  function foo(x) {   return x.split(' '); };  foo('Hello World!');

上面的代码就不会报错。在这里我们能发现的就是 Flow 理解了 split() 方法只适用于字符串,所以适合的 x 就必须是一个字符串。

可为空类型

Flow 不像其它的类型系统一样对待 null。它不会忽略掉 null,因此它可以防范那些作为其他类型传入的 null 可能会导致应用程序崩溃的问题。

看看如下代码:

/*@flow*/  function stringLength (str) {   return str.length; }  var length = stringLength(null);

在上述场景中,Flow 会抛出一个错误, 我们就不得不像下面这样对 null 进行单独的处理:

/*@flow*/  function stringLength (str) {   if (str !== null) {     return str.length;   }    return 0; }  var length = stringLength(null);

为了保证代码在所有的情况下都能正常运行,我们专门针对 null 进行检查。Flow 就会将最后的那行代码当做是有效的代码。

类型注解

如我在上面所提到过的,类型推断是 Flow 最好的特性之一,因为无需编写类型注解我们就可以得到有效的反馈。不过,在某些情况下,有必要向代码添加注解以更好地检查和消除不确定性。

看看下面的代码:

/*@flow*/  function foo(x, y){   return x + y; }  foo('Hello', 42);

Flow 在上面的代码中不会发现任何错误,因为 + (加) 操作可以被用在字符串和数字之上, 而我们并没有将add()的参数指定为数字。

在这种情况下,我们可以使用类型注解来指定想要的行为。Type 注解前缀一个 : (冒号),可以被放在函数参数、返回类型以及变量声明之上。

如果我们向上面的代码添加了类型注解,那么它就将如下所示:

/*@flow*/  function foo(x : number, y : number) : number {   return x + y; }  foo('Hello', 42);

这段代码会显示一个错误,因为函数希望得到的是一个数字作为参数,而我们提供的却是一个字符串。

显示在终端上的错误信息如下所示:

index.js:7   7: foo('Hello', 42);          ^^^^^^^ string. This type is incompatible with the expected param type of   3: function foo(x : number, y : number) : number{                       ^^^^^^ number

如果我们传入的是一个数字而非“Hello”,就不会报错了。类型注解对在庞大且复杂的 JavaScript 文件中指定想要的行为也同样有用。

基于前面的例子,让我们来看看 Flow 支持的一些其它的类型注解。

函数

/*@flow*/  /*--------- Type annotating a function --------*/ function add(x : number, y : number) : number {   return x + y; }  add(3, 4);

上面的代码展示了一个变量和一个函数的注解。add()函数的参数以及返回值都被期望是数字的。如果传入了任何其它的数据类型, Flow 就会抛出一个错误。

数组

/*-------- Type annotating an array ----------*/ var foo : Array<number> = [1,2,3];

数组注解采用 Array<T> 的形式,其中的 T 表示数组中单个元素的数据类型。在上述代码中,foo应该是一个元素为数字的数组。

如下是类和对象的一个示例模式。唯一需要牢记的一点是我们可以使用 | 符号在两个类型间执行 OR 操作。变量 bar1 被加上了注解,期望的是 Bar 类的模式。

/*-------- Type annotating a Class ---------*/ class Bar{   x:string;           // x should be string          y:string | number;  // y can be either a string or a number   constructor(x,y){     this.x=x;     this.y=y;   } }  var bar1 : Bar = new Bar("hello",4);

原文链接:使用 Flow 写更好的 JavaScript 代码,转载请注明来源!

0