Dart基礎——Dart語法規範

Dart基礎——Dart語法規範

字符串

兩個常量字符串(不是變量,是放在引號中的字符串),你不須要使用 + 來鏈接它們。

推薦的寫法git

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

不推薦的寫法github

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.'

不推薦的寫法async

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

布爾值

使用? ?將空值轉換爲布爾值。

當表達式的值能夠爲真、假或null,而且您須要將結果傳遞給不接受null的對象時,此規則適用。一個常見的狀況是一個判斷空值的方法調用被用做條件:ide

不推薦的寫法函數

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

若是optionalThing爲空,此代碼將拋出異常。(if只支持判斷bool值,不支持null)要解決這個問題,您須要將null值「轉換」爲true或false。雖然您可使用==來完成此操做,但咱們建議使用?? :性能

推薦的寫法學習

//若是你想要optionalThing是空值時返回false
optionalThing?.isEnabled ?? false;

//若是你想要optionalThing是空值時返回true
optionalThing?.isEnabled ?? true;

不推薦的寫法ui

// 若是你想要optionalThing是空值時返回false
optionalThing?.isEnabled == true;

// 若是你想要optionalThing是空值時返回true
optionalThing?.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. 這樣的雙字母縮寫詞仍然像通常單詞同樣首字母大寫。)

推薦的寫法

HttpConnectionInfo
uiHandler
IOStream
HttpRequest
Id
DB

不推薦的寫法

HTTPConnection
UiHandler
IoStream
HTTPRequest
ID
Db

譯者注:

  • 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;

若是有時間推薦看一看官網上詳細的Effective Dart,https://www.dartcn.com/guides/language/effective-dart/
做者把一些flutter經常使用的dart語法規範總結了出來,一些語法作了補充。

Dart基礎——Dart語法規範

整潔的代碼是創造一個好工程的前提

相關文章
相關標籤/搜索