字符串

两个常量字符串(不是变量,是放在引号中的字符串),你不须要应用 + 来连贯它们。

举荐的写法

print(    'ERROR: Parts of the spaceship are on fire. Other '    'parts are overrun by martians. Unclear which are which.');

不举荐的写法

print('ERROR: Parts of the spaceship are on fire. Other ' +    'parts are overrun by martians. Unclear which are which.');
不要在字符串中应用不必要的大括号

如果要插入是一个简略的标识符,并且前面没有紧跟着的字母,则应省略 {}

举荐的写法

'Hi, $name!'"Wear your wildest $decade's outfit."//标识符前面有紧跟着的字母了 加上大括号用以辨别'Wear your wildest ${decade}s outfit.'

不举荐的写法

'Hi, ${name}!'"Wear your wildest ${decade}'s outfit."

布尔值

应用? ?将空值转换为布尔值。

当表达式的值能够为真、假或null,并且您须要将后果传递给不承受null的对象时,此规定实用。一个常见的状况是一个判断空值的办法调用被用作条件:

不举荐的写法

if (optionalThing?.isEnabled) {  print("Have enabled thing.");}

如果optionalThing为空,此代码将抛出异样。(if只反对判断bool值,不反对null)要解决这个问题,您须要将null值“转换”为true或false。尽管您能够应用==来实现此操作,但咱们倡议应用?? :

举荐的写法

//如果你想要optionalThing是空值时返回falseoptionalThing?.isEnabled ?? false;//如果你想要optionalThing是空值时返回trueoptionalThing?.isEnabled ?? true;

不举荐的写法

// 如果你想要optionalThing是空值时返回falseoptionalThing?.isEnabled == true;// 如果你想要optionalThing是空值时返回trueoptionalThing?.isEnabled != false;

PS:如果这里你有些不了解,能够先浏览一下作者之前的文章<u>Flutter根底——Dart语法</u>,学习一下'?' '??' '??=' 的应用

两种操作都会产生同样的后果,而且做的都是正确的事件,但首选??操作符,次要有三个起因。

  1. ??运算符分明地表明,代码与空值无关。
  2. 当右边的布尔表达式不会为空时,==true看起来像是多余的。
  3. ??false 和 ??true 分明地表明了当表达式为null时将应用什么值。对于==true,你必须分明右边表达式的布尔逻辑,才会明确想要 右边表达式是null时返回false 为什么要用== true。

汇合

Dart 汇合中原生反对了四种类型:list, map, queue, 和 set。 上面是利用于汇合的最佳实际。

尽可能的应用汇合字面量。

两种形式来结构一个空的可变 list : []List() 。 同样,有三种形式来结构一个空的Map map:{}Map(), 和 LinkedHashMap()

如果想创立一个固定不变的 list 或者其余自定义汇合类型,这种状况下你须要应用构造函数。 否则,应用字面量语法更加优雅。 外围库中裸露这些构造函数易于扩大,然而通常在 Dart 代码中并不应用构造函数。

举荐的写法

var points = [];var addresses = {};

不举荐的写法

var points = List();var addresses = Map();

如果需要的话,你能够提供一个泛型。

举荐的写法

var points = <Point>[];var addresses = <String, Address>{};

不举荐的写法

var points = List<Point>();var addresses = Map<String, Address>();

留神,对于汇合类的 命名 构造函数则不实用下面的规定。 List.from()Map.fromIterable() 都有其应用场景。 如果须要一个固定长度的联合,应用 List() 来创立一个固定长度的 list 也是正当的。

不要应用 .length 来判断一个汇合是否为空。

通过调用 .length 来判断汇合是否蕴含内容是十分低效的。相同,Dart 提供了更加高效率和易用的 getter 函数:.isEmpty.isNotEmpty。 应用这些函数并不需要对后果再次取非(list.length ! =0)

举荐的写法

if (lunchBox.isEmpty) return 'so hungry...';if (words.isNotEmpty) return words.join(' ');

不举荐的写法

if (lunchBox.length == 0) return 'so hungry...';if (!words.isEmpty) return words.join(' ');
不要应用 List.from() 除非想批改后果的类型。

给定一个可迭代的对象,有两种常见形式来生成一个蕴含雷同元素的 list:

var copy1 = iterable.toList();var copy2 = List.from(iterable);

举荐的写法

显著的区别是前一个更短。 更重要的区别在于第一个保留了原始对象的类型参数:

// 创立一个 List<int>:var iterable = [1, 2, 3];// 输入 "List<int>":print(iterable.toList().runtimeType);

不举荐的写法

// 创立一个 List<int>:var iterable = [1, 2, 3];// 输入 "List<dynamic>":print(List.from(iterable).runtimeType);

如果你想要扭转原始对象的类型参数,那么能够调用 List.from()

举荐的写法

var numbers = [1, 2.3, 4]; // List<num>.numbers.removeAt(1); // 当初汇合里只蕴含int型var ints = List<int>.from(numbers);

然而如果你的目标只是复制可迭代对象并且保留元素原始类型, 或者并不在乎类型,那么请应用 toList()

参数

应用 = 来分隔参数名和参数默认值。

因为遗留起因,Dart 同时反对 := 作为参数名和默认值的分隔符。 为了与可选的地位参数保持一致,请应用 =

举荐的写法

void insert(Object item, {int at = 0}) { ... }

不举荐的写法

void insert(Object item, {int at: 0}) { ... }
不要 显式的为参数设置 null 值。

如果你创立了一个可选参数,那么就不要为其赋默认值, Dart 默认应用 null 作为默认值,所以这里不须要为其 null 赋值语句。

举荐的写法

void error([String message]) {  stderr.write(message ?? '\n');}

不举荐的写法

void error([String message = null]) {  stderr.write(message ?? '\n');}

变量

不要 显示的为参数初始化 null 值。

在Dart中,未主动显式初始化的变量或字段将初始化为 null 。 语言保障了赋值的可靠性。在 Dart 中没有“未初始化内存”的概念。 所以应用 = null 是多余的。

举荐的写法

int _nextId;class LazyId {  int _id;  int get id {    if (_nextId == null) _nextId = 0;    if (_id == null) _id = _nextId++;    return _id;  }}

不举荐的写法

int _nextId = null;class LazyId {  int _id = null;  int get id {    if (_nextId == null) _nextId = 0;    if (_id == null) _id = _nextId++;    return _id;  }}
防止 保留可计算的后果

在设计类的时候,你经常心愿裸露底层状态的多个体现属性。 经常你会发现在类的构造函数中计算这些属性,而后保存起来:

不举荐的写法

class Circle {  num radius;  num area;  num circumference;  Circle(num radius)      : radius = radius,        area = pi * radius * radius,        circumference = pi * 2.0 * radius;}

下面的代码有两个不妥之处。首先,这样节约了内存。 严格来说面积和周长是缓存数据。 他们保留的后果能够通过已知的数据计算出来。 他们缩小了 CPU 耗费却减少了内存耗费。 咱们还没有衡量,到底存不存在性能问题?

更坏的状况是,下面的代码是 错的 。下面的缓存是有效的— 你如何晓得什么时候缓存生效了须要从新计算? 在这里,咱们永远不会从新计算,即便 radius 是可变的。 你能够给 radius 设置一个不同的值,然而 areacircumference 还是之前的值。

为了正确处理缓存生效,咱们须要这样做:

不举荐的写法

class Circle {  num _radius;  num get radius => _radius;  set radius(num value) {    _radius = value;    _recalculate();  }  num _area;  num get area => _area;  num _circumference;  num get circumference => _circumference;  Circle(this._radius) {    _recalculate();  }  void _recalculate() {    _area = pi * _radius * _radius;    _circumference = pi * 2.0 * _radius;  }}

这须要编写、保护、调试以及浏览更多的代码。 如果你一开始这样写代码:

举荐的写法

class Circle {  num radius;  Circle(this.radius);  num get area => pi * radius * radius;  num get circumference => pi * 2.0 * radius;}

下面的代码更加简洁、应用更少的内存、缩小出错的可能性。 它尽可能少的保留了示意圆所须要的数据。 这里没有字段须要同步,因为这里只有一个无效数据源。

成员

在 Dart 中,对象成员能够是函数(办法)或数据(实例变量)。 上面是对于对象成员的最佳实际。

不要 为字段创立不必要的 getter 和 setter 办法。

举荐的写法

class Box {  var contents;}

不举荐的写法

class Box {  var _contents;  get contents => _contents;  set contents(value) {    _contents = value;  }}
举荐 应用 final 关键字来创立只读属性

如果你有一个变量,对于内部代买来说只能读取不能批改, 最简略的做法就是应用 final 关键字来标记这个变量。

举荐的写法

class Box {  final contents = [];}

不举荐的写法

class Box {  var _contents;  get contents => _contents;}
不要 应用 this. ,在重定向命名函数和防止抵触的状况下除外。

只有当局部变量和成员变量名字一样的时候,你才须要应用 this. 来拜访成员变量。 只有两种状况须要应用 this. 。其中一种状况是要拜访的局部变量和成员变量命名一样的时候:

举荐的写法

class Box {  var value;  void clear() {    update(null);  }  void update(value) {    this.value = value;  }}

不举荐的写法

class Box {  var value;  void clear() {    this.update(null);  }  void update(value) {    this.value = value;  }}
尽可能的在定义变量的时候初始化变量值。

如果一个字段不依赖于构造函数中的参数, 则应该在定义的时候就初始化字段值。 这样能够缩小须要的代码并能够确保在有多个构造函数的时候你不会遗记初始化该字段。

不举荐的写法

class Folder {  final String name;  final List<Document> contents;  Folder(this.name) : contents = [];  Folder.temp() : name = 'temporary'; // Oops! Forgot contents.}

举荐的写法

class Folder {  final String name;  final List<Document> contents = [];  Folder(this.name);  Folder.temp() : name = 'temporary';}

当然,对于变量取值依赖结构函数参数的状况以及不同的构造函数取值也不一样的状况, 则不适宜本条规定。

构造函数

尽可能的应用初始化模式。

许多字段间接应用结构函数参数来初始化,如:

不举荐的写法

class Point {  num x, y;  Point(num x, num y) {    this.x = x;    this.y = y;  }}

为了初始化一个字段,咱们须要取_四_次 x 。应用上面的形式会更好:

举荐的写法

class Point {  num x, y,z;  Point(this.x, this.y,{this.z});}

这里的位于结构函数参数之前的 this. 语法被称之为初始化模式(initializing formal)。 有些状况下这无奈应用这种模式。特地是,这种模式下在初始化列表中无奈看到变量。 然而如果能应用该形式,就应该尽量应用。(如果应用命名参数)

; 来代替空的构造函数体 {}

在 Dart 中,没有具体函数体的构造函数能够应用分号结尾。 (事实上,这是不可变构造函数的要求。)

举荐的写法

class Point {  int x, y;  Point(this.x, this.y);}

不举荐的写法

class Point {  int x, y;  Point(this.x, this.y) {}}
不要 应用 new

创建对象不要应用new

举荐的写法

Widget build(BuildContext context) {  return Row(    children: [      RaisedButton(        child: Text('Increment'),      ),      Text('Click!'),    ],  );}

不举荐的写法

Widget build(BuildContext context) {  return new Row(    children: [      new RaisedButton(        child: new Text('Increment'),      ),      new Text('Click!'),    ],  );}

异步

举荐 应用 async/await 而不是间接应用底层的个性。

显式的异步代码是十分难以浏览和调试的, 即便应用很好的形象(比方 future)也是如此。 这就是为何 Dart 提供了 async/await。 这样能够显著的进步代码的可读性并且让你能够在异步代码中应用语言提供的所有流程管制语句。

举荐的写法

Future<int> countActivePlayers(String teamName) async {  try {    var team = await downloadTeam(teamName);    if (team == null) return 0;    var players = await team.roster;    return players.where((player) => player.isActive).length;  } catch (e) {    log.error(e);    return 0;  }}

不举荐写法

Future<int> countActivePlayers(String teamName) {  return downloadTeam(teamName).then((team) {    if (team == null) return Future.value(0);    return team.roster.then((players) {      return players.where((player) => player.isActive).length;    });  }).catchError((e) {    log.error(e);    return 0;  });}

标识符

在 Dart 中标识符有三种类型。

  • UpperCamelCase 每个单词的首字母都大写,蕴含第一个单词。
  • lowerCamelCase 每个单词的首字母都大写,除了第一个单词, 第一个单词首字母小写,即便是缩略词。
  • lowercase_with_underscores 只是用小写字母单词,即便是缩略词, 并且单词之间应用 _ 连贯。
应用 UpperCamelCase 格调命名类型。

Classes(类名)、 enums(枚举类型)、 typedefs(类型定义)、 以及 type parameters(类型参数)应该把每个单词的首字母都大写(蕴含第一个单词), 不应用分隔符。

class SliderMenu { ... }class HttpRequest { ... }typedef Predicate = bool Function<T>(T value);
要在文件夹源文件中应用 lowercase_with_underscores 形式命名。
lowercase_with_underscores 格调命名库和源文件名。

举荐的写法

library peg_parser.source_scanner;import 'file_system.dart';import 'slider_menu.dart';

不举荐的写法

library pegparser.SourceScanner;import 'file-system.dart';import 'SliderMenu.dart';
应用 lowercase_with_underscores 格调命名导入的前缀。

举荐的写法

import 'dart:math' as math;import 'package:angular_components/angular_components'    as angular_components;import 'package:js/js.dart' as js;

不举荐的写法

import 'dart:math' as Math;import 'package:angular_components/angular_components'    as angularComponents;import 'package:js/js.dart' as JS;
应用 lowerCamelCase 格调来命名其余的标识符。

类成员、顶级定义、变量、参数以及命名参数等 除了第一个单词,每个单词首字母都应大写,并且不应用分隔符。

举荐的写法

var item;HttpRequest httpRequest;void align(bool clearItems) {  // ...}
把超过两个字母的首字母大写缩略词和缩写词当做一般单词。

首字母大写缩略词比拟难浏览, 特地是多个缩略词连载一起的时候会引起歧义。 例如,一个以 HTTPSFTP 结尾的名字, 没有方法判断它是指 HTTPS FTP 还是 HTTP SFTP 。

为了防止下面的状况,缩略词和缩写词要像一般单词一样首字母大写, 两个字母的单词除外。 (像 ID 和 Mr. 这样的双字母缩写词依然像个别单词一样首字母大写。)

举荐的写法

HttpConnectionInfouiHandlerIOStreamHttpRequestIdDB

不举荐的写法

HTTPConnectionUiHandlerIoStreamHTTPRequestIDDb

译者注:

  • acronyms :首字母缩略词,指取若干单词首字母组成一个新单词,如:HTTP = HyperText Transfer Protocol
  • abbreviations : 缩写词,指取某一单词的局部字母(或其余缩短单词的形式)代表整个单词,如:ID = identification
不要 应用前缀字母

举荐的写法

defaultTimeout

不举荐的写法

kDefaultTimeout

格式化

应用 dartfmt 格式化你的代码。

格式化是一项繁琐的工作,尤其在重构过程中特地耗时。 庆幸的是,你不用放心。 咱们提供了一个名为 dartfmt 的优良的主动代码格式化程序,它能够为你实现格式化工作。 咱们有一些对于它实用的规定的 文档 , Dart 中任何官网的空格解决规定由 dartfmt 生成

其余格局指南用于 dartfmt 无奈修复的一些规定。

对所有流控制结构应用花括号。

这样能够防止 dangling else (else悬挂)的问题。

if (isWeekDay) {  print('Bike to work!');} else {  print('Go dancing or read a book!');}

这里有一个例外:一个没有 elseif 语句, 并且这个 if 语句以及它的执行体适宜在一行中实现。 在这种状况下,如果您违心,能够不必括号

if (arg == null) return defaultValue;

然而,如果执行体蕴含下一行,请应用大括号:

举荐的写法

if (overflowChars != other.overflowChars) {  return overflowChars < other.overflowChars;}

不举荐的写法

if (overflowChars != other.overflowChars)  return overflowChars < other.overflowChars;

欢送关注微信公众号,文章会首发在微信公众号。
能够搜寻公众号名 ‘’杰克的程序人生‘’