メインコンテンツにスキップ

JavaScript開発者としてDartを学ぶ

このガイドは、Dartを学ぶ上でJavaScriptプログラミングの知識を活用することを目的としています。両言語の主要な類似点と相違点を示し、JavaScriptではサポートされていないDartの概念を紹介します。JavaScript開発者にとって、Dartは非常に馴染み深いものとなるはずです。なぜなら、両言語は多くの概念を共有しているからです。

JavaScriptと同様に、Dartはイベントループ上で動作するため、両言語はコードを同様の方法で実行します。たとえば、Future(JavaScriptのPromise)のような非同期概念やasync/await構文は非常に似ています。

DartはJavaScriptとは異なり、強力に型付けされています。TypeScriptやFlowを使用したことがある場合、Dartの学習は容易になるでしょう。主に純粋なJavaScriptで作業してきた場合、多少の調整が必要かもしれません。強力な型付けにより、DartはJavaScriptコードに存在する可能性のある多くのエラーをコンパイル前に検出します。

Dartはデフォルトでnull安全を有効にしています。JavaScriptはnull安全をサポートしていません。JavaScript開発者にとっては、null安全なコードの書き方を学ぶのに時間がかかるかもしれませんが、その見返りとして、Dartコードのコンパイル前でも検出されるnull参照例外に対する保護が強化されます。(これにより、nullであることが判明したJavaScript変数の操作で発生する、あの忌まわしいTypeErrorを回避できます。)

規約とリンティング

#

JavaScriptとDartはどちらも、標準的な規約を強制するためのリンティングツールを備えています。JavaScriptは多くのツール、標準、構成を提供しますが、Dartはレイアウトとスタイルの規約の公式セットと、準拠を容易にするためのリンターを備えています。Dartアナライザは、より多くの分析機能を提供するとともにコードをリンティングします。プロジェクトのリンティングルールをカスタマイズするには、静的解析のカスタマイズの手順に従ってください。

Dartは、エラーを検索して修正するためのdart fixを提供しています。

Dartは、PrettierのようなJavaScriptツールに似たコードフォーマッターも提供しています。任意のDartプロジェクトのコードをフォーマットするには、コマンドラインでdart formatを実行します。DartおよびFlutterのIDEプラグインもこの機能を提供します。

Dartは、コレクション、パラメータ、または引数のカンマ区切りリストに対して末尾カンマをサポートしています。末尾カンマを追加すると、フォーマッターは各リスト項目を独自の行に配置します。リストに後でさらに項目が追加される可能性があると判断した場合は、末尾カンマを追加してください。フォーマットの便宜のためだけに末尾カンマを追加することは避けてください。

JavaScriptは、リストとマップのリテラルでのみ末尾カンマをサポートしています。

組み込み型

#

JavaScriptとDartはどちらもデータをに分類します。すべての変数には関連付けられた型があります。型は、変数が格納できる値の種類と、これらの値に対して実行できる操作を決定します。DartはJavaScriptと異なり、すべての式と変数に静的型を割り当てます。静的型は、変数の値、または式の値の実行時型を予測します。これは、Dartアプリが健全な静的型付けを持っていることを意味します。

JavaScriptは、プリミティブ型numstringboolean、およびnull値、さらに配列Map型を提供します。

Dartは次の組み込み型をサポートしています

  • 数値 (num, int, double)
  • 文字列 (String)
  • ブール値 (bool)
  • リスト (List、配列とも呼ばれる)
  • セット (Set)
  • マップ (Map)
  • シンボル (Symbol)
  • null (Null)

詳細については、Dart言語ツアー組み込み型を確認してください。

Dartでは、Null以外のすべての型はObjectのサブタイプです。すべての値もオブジェクトです。DartはJavaScriptのような「プリミティブ型」を使用しません。対照的に、Dartは数値、ブール値、およびnull値を正規化または正規化します。これは、数値1を持つint値が1つだけ存在することを意味します。

例:等価演算子==identical()メソッドは、数値型の同じ値に対してtrueを返します。以下のコードで示されている例を確認してください。

dart
var a = 2;
var b = 1 + 1;

print(a == b); // Prints true
print(identical(a, b)); // Prints true; only one "2" object exists

プリミティブ型

#

このセクションでは、DartがJavaScriptのプリミティブ型をどのように表現するかを説明します。

数値

#

Dartには、数値を保持するための3つのデータ型があります。

num
JavaScriptの汎用数値型に相当します。
int
小数部を持たない数値。
double
64ビット(倍精度)浮動小数点数。

Dart APIには、これらの型すべてがクラスとして含まれています。int型とdouble型はどちらもnumを親クラスとして共有しています。

num subclasses Object and int and double each subclass num

Dartは数値をオブジェクトとして扱うため、数値は独自のユーティリティ関数をオブジェクトメソッドとして公開できます。数値に関数を適用するために追加のオブジェクトを使用する必要はありません。

例:doubleを整数に丸める場合。

js
let rounded = Math.round(2.5);
dart
var rounded = 2.5.round();

文字列

#

Dartの文字列はJavaScriptの文字列と同様に機能します。文字列リテラルを記述するには、単一引用符(')または二重引用符(")で囲みます。ほとんどのDart開発者は単一引用符を使用しますが、言語は標準を強制しません。文字列内の単一引用符をエスケープしたくない場合は、二重引用符を使用してください。

dart
var a = 'This is a string.';
特殊文字のエスケープ
#

文字列内に別の意味を持つ文字(文字列補間用の$など)を含めるには、その文字をエスケープする必要があります。Dartでの特殊文字のエスケープは、JavaScriptやほとんどの他の言語と同様に機能します。特殊文字をエスケープするには、その文字の前にバックスラッシュ文字(\)を付けます。

以下のコードにいくつかの例を示します。

dart
final singleQuotes = 'I\'m learning Dart'; // I'm learning Dart
final doubleQuotes = "Escaping the \" character"; // Escaping the " character
final dollarEscape = 'The price is \$3.14.'; // The price is $3.14.
final backslashEscape = 'The Dart string escape character is \\.';
final unicode = '\u{1F60E}'; // 😎,  Unicode scalar U+1F60E
文字列補間
#

JavaScriptはテンプレートリテラルをサポートしています。これらは、次の理由でバックティック(`)文字区切り文字を使用します。

  • 複数行文字列を許可するため
  • 埋め込み式で文字列を補間するため
  • タグ付きテンプレートと呼ばれる特別な構造を作成するため

Dartでは、文字列を連結したり、文字列リテラル内に補間を使用したりするために、文字列をバックティックで囲む必要はありません。

詳細については、Dart言語ツアーの文字列を確認してください。

JavaScriptテンプレートリテラルと同様に、${<expression>}構文を使用して、文字列リテラルに式を挿入できます。Dartはこの構文を使用し、式が単一の識別子を使用する場合は波括弧を省略できます。

dart
var food = 'bread';
var str = 'I eat $food'; // I eat bread
var str = 'I eat ${food}'; // I eat bread

文字列連結と複数行宣言

#

JavaScriptでは、テンプレートリテラルを使用して複数行文字列を定義できます。Dartには、複数行文字列を定義する2つの方法があります。

  1. 暗黙的な文字列連結の使用:Dartは、複数行にまたがっていても、隣接する文字列リテラルを連結します。
    dart
    final s1 = 'String '
        'concatenation'
        " even works over line breaks.";
  2. 複数行文字列リテラルの使用:文字列の両側に3つの引用符(単一または二重)を使用すると、リテラルは複数行にまたがることができます。
    dart
    final s2 = '''
    You can create
    multiline strings like this one.
    ''';
    
    final s3 = """
    This is also a
    multiline string.""";

等価性

#

Dartは、2つの文字列がコードユニットの同じシーケンスを含んでいる場合、それらを等しいと見なします。2つの文字列が同じシーケンスを持っているかどうかを判断するには、等価演算子(==)を使用します。

dart
final s1 = 'String '
    'concatenation'
    " works even over line breaks.";
assert(s1 ==
    'String concatenation works even over '
        'line breaks.');

ブール値

#

DartとJavaScriptの両方のブール値は、二項条件を表現します。これらの2つの値は、値または式がtrueまたはfalseであるかどうかを表します。リテラルtruefalseを使用して値を返すことも、x < 5y == nullのような式で生成することもできます。

js
let isBananaPeeled = false;
dart
var isBananaPeeled = false;

変数

#

Dartの変数はJavaScriptの変数と同様に機能しますが、2つの例外があります。

  1. 各変数には型があります。
  2. Dartは、JavaScriptのletおよびconst変数のように、すべての変数をブロックレベルでスコープします。

Dart変数は、次のいずれかの方法で型を取得します。

  1. 宣言済み:宣言に記述された型。
  2. 推論済み:変数を初期化するために使用される式。、慣例により、アナライザが型を推論できる場合はvarまたはfinalを使用します。
js
// Declare and initialize a variable at once
let name = "bob";
dart
// Declare a variable with a specific type
// when you don't provide an initial value
String name;
// Declare and initialize a variable
// at the same time and Dart infers
// the type
var name = 'bob';

変数は、その型の値のみを受け入れることができます。

dart
var name = 'bob';
name = 5; // Forbidden, as `name` has type `String`.

初期値または明示的な型を指定しない場合、Dartは変数の型を汎用型dynamicとして推論します。

JavaScript変数と同様に、dynamic型を使用するDart変数には任意の値を代入できます。

js
// Declare a variable
let name;
// Initialize the variable
name = "bob";
dart
// Declare a variable without a type or assigned value
// and Dart infers the 'dynamic' type
var name;
// Initialize the variable and the type remains `dynamic`
name = 'bob';
name = 5; // Allowed, as `name` has type `dynamic`.

Finalとconst

#

JavaScriptとDartはどちらも変数修飾子を使用します。どちらもconstを使用しますが、constの動作方法が異なります。JavaScriptがconstを使用するところを、Dartはfinalを使用します。

Dart変数にfinalを追加するか、JavaScript変数にconstを追加する場合、他のコードがその値を読む前に変数を初期化する必要があります。初期化されると、これらの変数の参照を変更することはできません。

Dartがconstを使用する場合、コンパイル時に作成される特別な値を指します。Dartは、これらの不変値を生成するために限定された式を使用します。これらの式は副作用を持てません。これらの条件下では、コンパイラは定数変数または式の正確な値を予測できます。静的型だけでなく。

dart
final String name;
// Cannot read name here, not initialized.
if (useNickname) {
  name = "Bob";
} else {
  name = "Robert";
}
print(name); // Properly initialized here.

Dartでは、定数変数は定数値を含める必要があります。非定数変数は、constとしてマークすることもできる定数値を含めることができます。

dart
var foo = const [];
  // foo is not constant, but the value it points to is.
  // You can reassign foo to a different list value,
  // but its current list value cannot be altered.

const baz = []; // Equivalent to `const []`

同様に、クラスは不変インスタンスを生成する独自のconstコンストラクタを持つことができます。

JavaScriptまたはDartでconst変数を変更することはできません。JavaScriptではconstオブジェクトのフィールドを変更できますが、Dartはできません。

詳細については、クラスセクションを参照してください。

Null 安全性

#

JavaScriptとは異なり、Dartはnull安全をサポートしています。Dartでは、すべての型はデフォルトでnull非許容です。これは、Dart開発者にとって、Dartは実行時ではなくコード作成時にnull参照例外を検出するため、有益です。

Null許容 vs Null非許容型

#

以下のコード例の変数はnullにはできません。

dart
// In null-safe Dart, none of these can ever be null.
var i = 42; // Inferred to be an int.
String name = getFileName();
final b = Foo(); // Foo() invokes a constructor

変数がnullの値を持つ可能性があることを示すには、型宣言に?を追加します。

dart
int? aNullableInt = null;

関数宣言のような他の型宣言についても同様です。

dart
String? returnsNullable() {
  return random.nextDouble() < 0.5
    ? 'Sometimes null!'
    : null;
}

String returnsNonNullable() {
  return 'Never null!';
}

Null許容演算子

#

Dartは、null許容性を処理するためのいくつかの演算子をサポートしています。JavaScriptと同様に、Dartはnull代入演算子(??=)、null合体演算子(??)、およびオプショナルチェイニング演算子(?.)をサポートしています。これらの演算子はJavaScriptと同じように機能します。

! 演算子

#

null許容変数または式がnull非許容である可能性がある場合、コンパイラにコンパイル時エラーを抑制するように(!)演算子で指示できます。この演算子を式の後に配置します。

これはDartの否定(!)演算子と混同しないでください。これは同じ記号を使用しますが、式の前に配置されます。

dart
int? a = 5;

int b = a; // Not allowed.
int b = a!; // Allowed.

実行時に、nullになる場合、実行時エラーが発生します。

?.演算子と同様に、オブジェクトのプロパティまたはメソッドにアクセスする場合は!演算子を使用します。

dart
myObject!.someProperty;
myObject!.someMethod();

実行時にmyObjectnullの場合、実行時エラーが発生します。

関数

#

Dartの関数はJavaScriptの対応するものとほぼ同じように機能しますが、いくつかの追加機能があり、宣言時にわずかな構文の違いがあります。JavaScriptと同様に、トップレベル、クラスフィールド、またはローカルスコープなど、ほぼどこにでも関数を宣言できます。

js
// On the top level
function multiply(a, b) {
  return a * b;
}

// As a class field
class Multiplier {
  multiply(a, b) {
    return a * b;
  }
}

// In a local scope
function main() {
  function multiply(a, b) {
    return a * b;
  }

  console.log(multiply(3, 4));
}
dart
// On the top level
int multiply(a, b) {
  return a * b;
}

// As a class field
class Multiplier {
  multiply(a, b) {
    return a * b;
  }
}

// In a local scope
main() {
  multiply(a, b) {
    return a * b;
  }

  print(multiply(3, 4));
}

アロー構文

#

DartとJavaScriptはどちらもアロー構文(=>)をサポートしていますが、サポート方法が異なります。Dartでは、関数が単一の式またはreturnステートメントを含む場合にのみアロー構文を使用できます。

例:以下のisNoble関数は同等です。

dart
bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}
dart
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

パラメータ

#

JavaScriptでは、すべてのパラメータは位置パラメータになり得ます。デフォルトでは、Dartはすべてのパラメータを関数に引数として渡すことを要求します

dart
int multiply(int a, int b) {
  return a * b;
}

main() {
  multiply(3, 5); // Valid. All parameters are provided.
  multiply(3); // Invalid. All parameters must be provided.
}

これは2つの状況で変更できます。

  1. 位置パラメータがオプションとしてマークされている場合。
  2. パラメータが名前付きで、必須としてマークされていない場合。

オプションの位置パラメータを定義するには、必須の位置パラメータの後に角括弧で囲みます。オプションパラメータの後に必須パラメータを配置することはできません。

null安全のため、オプションの位置パラメータにはデフォルト値があるか、null許容としてマークされている必要があります。詳細については、前のnull安全のセクションを参照してください。

以下のコードには、オプションの位置パラメータを定義する関数の有効な例が1つと、無効な例が2つあります。

dart
// Valid: `b` has a default value of 5. `c` is marked as nullable.
multiply(int a, [int b = 5, int? c]) {
  ...
}
// Invalid: a required positional parameter follows an optional one.
multiply(int a, [int b = 5], int c) {
  ...
}
// Invalid: Neither optional positional parameter has a default
//          value or has been flagged as nullable.
multiply(int a, [int b, int c]) {
  ...
}

以下の例は、オプションパラメータを持つ関数を呼び出す方法を示しています。

dart
multiply(int a, [int b = 5, int? c]) {
  ...
}

main() {
  // All are valid function calls.
  multiply(3);
  multiply(3, 5);
  multiply(3, 5, 7);
}

Dartは名前付きパラメータをサポートしています。これらは、位置パラメータのように定義された順序で提供する必要はありません。代わりに名前で参照します。デフォルトでは、これらは必須としてフラグ付けされていない限りオプションです。名前付きパラメータは、波括弧で囲んで定義されます。名前付きパラメータを必須の位置パラメータと組み合わせることができます。このシナリオでは、名前付きパラメータは常に位置パラメータの後に配置されます。名前付きパラメータを持つ関数を呼び出すときは、渡す値をパラメータの名前の前にコロンで区切ってプレフィックスを付けて渡します。例:f(namedParameter: 5)

繰り返しますが、null安全では、必須としてフラグ付けされていない名前付きパラメータにはデフォルト値があるか、null許容としてフラグ付けされている必要があります。

以下のコードは、名前付きパラメータを持つ関数を定義しています。

dart
// Valid:
// - `a` has been flagged as required
// - `b` has a default value of 5
// - `c` is marked as nullable
// - Named parameters follow the positional one
multiply(bool x, {required int a, int b = 5, int? c}) {
  ...
}

以下の例は、名前付きパラメータを持つ関数を呼び出しています。

dart
// All are valid function calls.
// Beyond providing the required positional parameter:
multiply(false, a: 3); // Only provide required named parameters
multiply(false, a: 3, b: 9); // Override default value of `b`
multiply(false, c: 9, a: 3, b: 2); // Provide all named parameters out of order

第一級関数

#

JavaScriptとDartは関数を第一級オブジェクトとして扱います。これは、Dartが関数を他のオブジェクトと同様に扱うことを意味します。たとえば、以下のコードは、関数を別の関数のパラメータとして渡す方法を示しています。

dart
void printElement(int element) {
  print(element);
}

var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach(printElement);

無名関数

#

JavaScriptとDartはどちらも無名関数、つまり名前のない関数をサポートしています。名前付き関数と同様に、無名関数を他の値のように渡すことができます。たとえば、無名関数を変数に格納したり、別の関数の引数として渡したり、別の関数から返したりできます。

JavaScriptには、無名関数を宣言する2つの方法があります。

  1. 標準的な関数式を使用する
  2. アロー構文を使用する

同様に、Dartにも無名関数を宣言する2つの方法があります。どちらもJavaScriptのアロー式と似たように機能します。Dartの無名関数は、通常の関数式に伴う追加機能はサポートしていません。たとえば、JavaScriptが関数式をコンストラクタのように扱ったり、thisのカスタムバインディングを作成したりするサポートがあります。

詳細については、クラスセクションを参照してください。

js
// A regular function expression
// assigned to a variable
let funcExpr = function(a, b) {
  return a * b;
}
// The same anonymous function
// expressed as an arrow
// function with curly braces.
let arrowFuncExpr = (a, b) => {
  return a * b;
}
// An arrow function with only
// one return statement as
// its contents does not
// require a block.
let arrowFuncExpr2 = (a, b) => a * b;
dart
// Assign an anonymous function
// to a variable.
var blockFunc =
  optionalCallback ?? (int a, int b) {
    return a * b;
};

// For an expression with only a return statement,
// you can use the arrow syntax:
var singleFunc = (int a, int b) => a * b;

JavaScriptと同様に、無名関数を他の関数に渡すことができます。開発者は、配列やリストのmap関数を使用する際に、無名関数を渡すことがよくあります。

js
// returns [4, 5, 6]
[1, 2, 3].map(e => e + 3);

// returns [5, 7, 9]
[1, 2, 3].map(e => {
  e *= 2;
  return e + 3;
});
dart
// returns [4, 5, 6]
[1, 2, 3].map((e) => e + 3).toList();

// returns [5, 7, 9]
var list2 = [1, 2, 3].map((e) {
  e *= 2;
  return e + 3;
}).toList();

ジェネレータ関数

#

両言語はジェネレータ関数をサポートしています。これらの関数は、不要な作業を回避するために計算された項目の反復可能なコレクションを返します。

Dartでジェネレータ関数を記述するには、関数パラメータの後にsync*キーワードを追加し、Iterableを返します。yieldキーワードを使用して最終的なiterableに項目を追加するか、yield*を使用して項目のセット全体を追加します。

以下の例は、基本的なジェネレータ関数を記述する方法を示しています。

js
function* naturalsTo(n) {
  let k = 0;
  while (k < n) {
    yield k++;
  }
}

// Returns [0, 1, 2, 3, 4]
for (let value of naturalsTo(5)) {
  console.log(value);
}
dart
Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) {
    yield k++;
  }
}

// Returns an iterable with [0, 1, 2, 3, 4]
print(naturalsTo(5).toList());
js
function* doubleNaturalsTo(n) {
  let k = 0;
  while (k < n) {
    yield* [k, k];
    k++;
  }
}

// Returns [0, 0, 1, 1, 2, 2]
for (let value of doubleNaturalsTo(3)) {
  console.log(value);
}
dart
Iterable<int> doubleNaturalsTo(int n) sync* {
  int k = 0;
  while (k < n) {
    yield* [k, k];
    k++;
  }
}

// Returns an iterable with [0, 0, 1, 1, 2, 2]
print(doubleNaturalsTo(3));

非同期ジェネレータ関数を定義することもできます。これは、iterableではなくストリームを返します。次の非同期セクションで詳細を学びます。

ステートメント

#

このセクションでは、JavaScriptとDartのステートメントの違いについて説明します。

制御フロー (if/else, for, while, switch)

#

ほとんどの制御ステートメントは、JavaScriptの対応するものと同様に機能します。コレクションには追加の用途があるものもあります。

反復

#

JavaScriptとDartはどちらもfor-inループを持っていますが、その動作は異なります。

JavaScriptのfor-inループは、オブジェクトのプロパティを反復処理します。JavaScriptのiterableオブジェクトの要素を反復処理するには、for-ofまたはArray.forEach()を使用する必要があります。Dartのfor-inループは、JavaScriptのfor-ofと同様に機能します。

以下の例は、コレクションを反復処理し、各要素を出力する方法を示しています。

js
for (const element of list) {
  console.log(element);
}
dart
for (final element in list) {
  print(element);
}

Switch

#

switchステートメントでcontinueを使用する場合、ケースに付けられたラベルと組み合わせることができます。

dart
switch (testEnum) {
  case TestEnum.A:
    print('A');
    continue b;
  b:
  case TestEnum.B:
    print('B');
    break;
}

演算子

#

DartとJavaScriptはどちらも定義済みの演算子を含んでいます。どちらの言語も新しい演算子の追加をサポートしていません。Dartはoperatorキーワードを使用して一部の既存の演算子をオーバーロードできます。例:

dart
class Vector {
  final double x;
  final double y;
  final double z;
  Vector(this.x, this.y, this.z);
  Vector operator +(Vector other) => Vector(
    x + other.x, 
    y + other.y,
    z + other.z,
  );
  Vector operator *(double scalar) => Vector(
    x * scalar,
    y * scalar,
    z * scalar,
  );
}

算術演算子

#

両言語の等価性および関係演算子は、以下の表に示すようにほぼ同一です。

意味JavaScript演算子Dart演算子
加算++
減算--
単項マイナス、否定とも呼ばれる-expr-expr
乗算**
除算//
整数結果を返す除算~/
整数除算の剰余(モジュロ)を取得する%%
x = x + 1 (式の値はx + 1++x++x
x = x + 1 (式の値はxx++x++
x = x - 1 (式の値はx - 1--x--x
x = x - 1 (式の値はxx--x--

dart
assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // Result is a double
assert(5 ~/ 2 == 2); // Result is an int
assert(5 % 2 == 1); // Remainder

a = 0;
b = ++a; // Increment a before b gets its value.
assert(a == b); // 1 == 1

a = 0;
b = a++; // Increment a AFTER b gets its value.
assert(a != b); // 1 != 0

a = 0;
b = --a; // Decrement a before b gets its value.
assert(a == b); // -1 == -1

a = 0;
b = a--; // Decrement a AFTER b gets its value.
assert(a != b); // -1 != 0

Dartには~/演算子(切り捨て除算演算子と呼ばれる)もあり、doubleを分割して切り捨てられた整数を出力することに気づいたでしょう。

dart
assert(25 == 50.4 ~/ 2);
assert(25 == 50.6 ~/ 2);
assert(25 == 51.6 ~/ 2);

等価性および関係演算子

#

両言語の等価性および関係演算子は同じように機能します。

意味JavaScript演算子Dart演算子
厳密な等価=====
抽象的な等価==
厳密な不等価!==!=
抽象的な不等価!=
より大きい>>
より小さい<<
以上>=>=
以下<=<=

JavaScriptの==および!=演算子には相当するものはありません。

dart
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);

型テスト演算子

#

テスト演算子の実装は、2つの言語間で若干異なります。

意味JavaScript演算子Dart演算子
型キャストx as T
オブジェクトが指定された型を持っている場合はtruex instanceof Tx is T
オブジェクトが指定された型を持っていない場合はtrue!(x instanceof T)x is! T

obj is Tの結果は、objTによって指定されたインターフェースを実装している場合にtrueになります。たとえば、obj is Object?は常にtrueです。

型キャスト演算子(as)を使用して、値が特定の型であることを保証します。コンパイラは、オブジェクトがその型であるとわかっている場合、これを使用できます。

dart
(person as Employee).employeeNumber = 4204583;

オブジェクトがT型であるとわかっていない場合は、オブジェクトを使用する前にis Tを使用して型をチェックします。

Dartでは、ifステートメントのスコープ内でローカル変数の型が更新されます。インスタンス変数には当てはまりません。

dart
if (person is Employee) {
   person.employeeNumber = 4204583;
}

論理演算子

#

論理演算子を使用して、ブール式を反転または結合できます。両言語の論理演算子は同一です。

意味JavaScript演算子Dart演算子
次の式を反転します(falseをtrueに、trueをfalseに変更します)。!x!x
論理OR||||
論理AND&&&&

JavaScriptは、ブール値が必要な場所であればどの値でも使用できます。その後、それらの値をtrueまたはfalseに変換します。JavaScriptは、空の文字列と数値0を「falsy」値と見なします。Dartは、条件や論理演算子のオペランドとしてbool値を使用できます。

dart
if (!done && (col == 0 || col == 3)) {
  // ...Do something...
}

ビット演算子およびシフト演算子

#

整数でビット演算子およびシフト演算子を使用して、数値の個々のビットを操作できます。両言語の演算子は、以下の表に示すようにほぼ同一です。

意味JavaScript演算子Dart演算子
ビットAND&&
ビットOR||
ビットXOR^^
単項ビット補数(0は1になり、1は0になります)。~expr~expr
左シフト<<<<
右シフト>>>>
符号なし右シフト>>>>>>

dart
final value = 0x22;
final bitmask = 0x0f;

assert((value & bitmask) == 0x02); // AND
assert((value & ~bitmask) == 0x20); // AND NOT
assert((value | bitmask) == 0x2f); // OR
assert((value ^ bitmask) == 0x2d); // XOR
assert((value << 4) == 0x220); // Shift left
assert((value >> 4) == 0x02); // Shift right
assert((-value >> 4) == -0x03); // Shift right
assert((value >>> 4) == 0x02); // Unsigned shift right
assert((-value >>> 4) > 0); // Unsigned shift right

条件演算子

#

DartとJavaScriptはどちらも、式を評価するための条件演算子(?:)を含んでいます。一部の開発者は、3つのオペランドを取るため、これを三項演算子と呼びます。Dartには3つのオペランドを取る別の演算子([]=)があるため、この演算子(?:)を条件演算子と呼びます。この演算子は、ステートメントに対してif-elseが機能するのと同様に、式に対して機能します。

js
let visibility = isPublic ? "public" : "private";
dart
final visibility = isPublic ? 'public' : 'private';

代入演算子

#

値の代入には(=)演算子を使用します。

dart
// Assign value to a
a = value;

この演算子には、null対応のバリアント(??=)もあります。

詳細については、null代入演算子セクションを参照してください。

JavaScriptとDartには、計算して変数に新しい値を代入する演算子が含まれています。これらの代入演算子は、右辺の値と変数の初期値をオペランドとして使用します。

以下の表に、これらの代入演算子を示します。

演算子説明
=代入
+=加算代入
-=減算代入
*=乗算代入
/=除算代入
~/=切り捨て除算代入
%=剰余(モジュロ)代入
>>>=符号なし右シフト代入
^=ビットXOR代入
<<=左シフト代入
>>=右シフト代入
&=ビットAND代入
|=ビットOR代入

JavaScriptは~/=代入演算子をサポートしていません。

dart
var a = 5;
a *= 2; // Multiply `a` by 2 and assign the result back to a.
print(a); // `a` is now 10.

カスケード (.. 演算子)

#

Dartでは、単一のオブジェクトに対して複数のメソッド呼び出し、プロパティ代入、またはその両方を連鎖させることができます。Dartはこの処理をカスケードと呼び、カスケード構文(..)を使用してこのアクションを実行します。

JavaScriptにはこの構文がありません。

以下の例は、カスケード構文を使用して新しく構築されたオブジェクトに複数のメソッドを連鎖させる方法を示しています。

dart
var animal = Animal() // Sets multiple properties and methods
  ..name = "Bob"
  ..age = 5
  ..feed()
  ..walk();

print(animal.name); // "Bob"
print(animal.age); // 5

最初のカスケード構文をnull対応にするには、?..のように記述します。

dart
var result = maybePerson
    ?..employment = employer
    ..salary = salary;

maybePersonの値がnullの場合、Dartはカスケード全体を無視します。

コレクション

#

このセクションでは、Dartのいくつかのコレクション型について説明し、JavaScriptの類似型と比較します。

リスト

#

Dartは、JavaScript配列と同じ方法でリストリテラルを記述します。Dartはリストを角括弧で囲み、値をカンマで区切ります。

dart
// Initialize list and specify full type
final List<String> list1 = <String>['one', 'two', 'three'];

// Initialize list using shorthand type
final list2 = <String>['one', 'two', 'three'];

// Dart can also infer the type
final list3 = ['one', 'two', 'three'];

以下のコードサンプルは、DartのListに対して実行できる基本的なアクションの概要を示しています。以下の例は、インデックス演算子を使用してListから値を取得する方法を示しています。

dart
final fruits = <String>['apple', 'orange', 'pear'];
final fruit = fruits[1];

addメソッドを使用してListの末尾に値を追加します。addAllメソッドを使用して別のListを追加します。

dart
final fruits = <String>['apple', 'orange', 'pear'];
fruits.add('peach');
fruits.addAll(['kiwi', 'mango']);

insertメソッドを使用して特定の位置に値を挿入します。insertAllメソッドを使用して特定の位置に別のListを挿入します。

dart
final fruits = <String>['apple', 'orange', 'pear'];
fruits.insert(0, 'peach');
fruits.insertAll(0, ['kiwi', 'mango']);

インデックス演算子と代入演算子を組み合わせて、List内の値を更新します。

dart
final fruits = <String>['apple', 'orange', 'pear'];
fruits[2] = 'peach';

次のいずれかのメソッドを使用してListから項目を削除します。

dart
final fruits = <String>['apple', 'orange', 'pear'];
// Remove the value 'pear' from the list.
fruits.remove('pear');
// Removes the last element from the list.
fruits.removeLast();
// Removes the element at position 1 from the list.
fruits.removeAt(1);
// Removes the elements with positions greater than
// or equal to start (1) and less than end (3) from the list.
fruits.removeRange(1, 3);
// Removes all elements from the list that match the given predicate.
fruits.removeWhere((fruit) => fruit.contains('p'));

Listの値の数を得るためにlengthを使用します。

dart
final fruits = <String>['apple', 'orange', 'pear'];
assert(fruits.length == 3);

Listが空かどうかを確認するためにisEmptyを使用します。

dart
var fruits = [];
assert(fruits.isEmpty);

Listが空でないかどうかを確認するためにisNotEmptyを使用します。

dart
final fruits = <String>['apple', 'orange', 'pear'];
assert(fruits.isNotEmpty);

Filled

#

DartのListクラスには、各項目が同じ値を持つリストを作成する方法が含まれています。このfilledコンストラクタは、1つのデフォルト値を持つサイズnの固定長リストを作成します。以下の例は、3つの項目を持つリストを作成します。

dart
final list1 = List.filled(3, 'a'); // Creates: [ 'a', 'a', 'a' ]
  • デフォルトでは、このリストから要素を追加または削除することはできません。このリストで要素の追加または削除を許可するには、パラメータリストの末尾に, growable: trueを追加します。
  • インデックス値を使用して、このリストの要素にアクセスおよび更新できます。

Generate

#

DartのListクラスには、増分値のリストを作成する方法が含まれています。このgenerateコンストラクタは、要素値を構築するためのテンプレートを持つサイズnの固定長リストを作成します。このテンプレートはインデックスをパラメータとして取ります。

dart
// Creates: [ 'a0', 'a1', 'a2' ]
final list1 = List.generate(3, (index) => 'a$index');

セット

#

JavaScriptとは異なり、DartはリテラルでSetを定義することをサポートしています。Dartはリストと同じ方法でセットを定義しますが、角括弧ではなく波括弧を使用します。セットは順序付けられていないコレクションであり、一意の項目のみを含みます。Dartはハッシュコードを使用してこれらの項目のユニークさを強制します。これは、オブジェクトがSetに格納されるにはハッシュ値が必要であることを意味します。

以下のコードスニペットは、Setを初期化する方法を示しています。

dart
final abc = {'a', 'b', 'c'};

空のセットの構文は、空の波括弧({})を指定すると空のMapが作成されるため、最初は混乱するかもしれません。空のSetを作成するには、{}宣言の前に型引数を付けるか、{}Set型の変数に代入します。

dart
final names = <String>{};
// Set<String> names = {}; // This works, too.
// final names = {}; // Creates an empty map, not a set.

以下の例は、DartのSetに対して実行できる基本的なアクションの概要を示しています。

addメソッドを使用してSetに値を追加します。addAllメソッドを使用して複数の値を追加します。

dart
final fruits = {'apple', 'orange', 'pear'};
fruits.add('peach');
fruits.addAll(['kiwi', 'mango']);

Setの以下のいずれかのメソッドを使用して、セットからコンテンツを削除します。

dart
final fruits = {'apple', 'orange', 'pear'};
// Remove the value 'pear' from the set.
fruits.remove('pear');
// Remove all elements in the supplied list from the set.
fruits.removeAll(['orange', 'apple']);
// Removes all elements from the list that match the given predicate.
fruits.removeWhere((fruit) => fruit.contains('p'));

Setの値の数を得るためにlengthを使用します。

dart
final fruits = {'apple', 'orange', 'pear'};
assert(fruits.length == 3);

Setが空かどうかを確認するためにisEmptyを使用します。

dart
var fruits = <String>{};
assert(fruits.isEmpty);

Setが空でないかどうかを確認するためにisNotEmptyを使用します。

dart
final fruits = {'apple', 'orange', 'pear'};
assert(fruits.isNotEmpty);

マップ

#

DartのMap型は、JavaScriptのMap型に似ています。どちらの型もキーを値に関連付けます。すべてのキーが同じ型である場合、キーはいずれかのオブジェクト型にすることができます。このルールは値にも適用されます。各キーは最大で1回出現しますが、同じ値を複数回使用できます。

Dartはハッシュテーブルに基づいた辞書です。これは、キーがハッシュ可能である必要があることを意味します。すべてのDartオブジェクトにはハッシュが含まれています。

リテラルを使用して作成された、これらの単純なMapの例を検討してください。

dart
final gifts = {
  'first': 'partridge',
  'second': 'turtle doves',
  'fifth': 'golden rings'
};

final nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

以下のコードサンプルは、DartのMapに対して実行できる基本的なアクションの概要を示しています。以下の例は、インデックス演算子を使用してMapから値を取得する方法を示しています。

dart
final gifts = {'first': 'partridge'};
final gift = gifts['first'];

containsKeyメソッドを使用して、Mapにキーが含まれているかどうかを確認します。

dart
final gifts = {'first': 'partridge'};
assert(gifts.containsKey('fifth'));

インデックス代入演算子([]=)を使用して、Mapにエントリを追加または更新します。Mapがまだキーを含んでいない場合、Dartはエントリを追加します。キーが存在する場合、Dartはその値を更新します。

dart
final gifts = {'first': 'partridge'};
gifts['second'] = 'turtle'; // Gets added
gifts['second'] = 'turtle doves'; // Gets updated

addAllメソッドを使用して別のMapを追加します。addEntriesメソッドを使用して他のエントリをMapに追加します。

dart
final gifts = {'first': 'partridge'};
gifts['second'] = 'turtle doves';
gifts.addAll({
  'second': 'turtle doves',
  'fifth': 'golden rings',
});
gifts.addEntries([
  MapEntry('second', 'turtle doves'),
  MapEntry('fifth', 'golden rings'),
]);

removeメソッドを使用してMapからエントリを削除します。removeWhereメソッドを使用して、指定されたテストを満たすすべてエントリを削除します。

dart
final gifts = {'first': 'partridge'};
gifts.remove('first');
gifts.removeWhere((key, value) => value == 'partridge');

Mapのキーと値のペアの数を得るためにlengthを使用します。

dart
final gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);

Mapが空かどうかを確認するためにisEmptyを使用します。

dart
final gifts = {};
assert(gifts.isEmpty);

Mapが空でないかどうかを確認するためにisNotEmptyを使用します。

dart
final gifts = {'first': 'partridge'};
assert(gifts.isNotEmpty);

変更不可

#

純粋なJavaScriptは不変性をサポートしていません。Dartは、配列、セット、または辞書のようなコレクションを不変にするための複数の方法を提供します。

  • コレクションがコンパイル時定数であり、変更されない場合は、constキーワードを使用します。
    const fruits = <String>{'apple', 'orange', 'pear'};
  • Setfinalフィールドに代入します。これは、Set自体がコンパイル時定数である必要がないことを意味します。これにより、フィールドは別のSetでオーバーライドされないことが保証されますが、Setのサイズや内容を変更することは still できます。
    final fruits = <String>{'apple', 'orange', 'pear'};
  • (以下の例に示すように)unmodifiableコンストラクタを使用して、コレクション型の最終バージョンを作成します。これにより、サイズや内容を変更できないコレクションが作成されます。
dart
final _set = Set<String>.unmodifiable(['a', 'b', 'c']);
final _list = List<String>.unmodifiable(['a', 'b', 'c']);
final _map = Map<String, String>.unmodifiable({'foo': 'bar'});

スプレッド演算子

#

JavaScriptと同様に、Dartはスプレッド演算子(...)とnull対応スプレッド演算子(...?)を使用して、リストを別のリストに埋め込むことをサポートしています。

dart
var list1 = [1, 2, 3];
var list2 = [0, ...list1]; // [0, 1, 2, 3]
// When the list being inserted could be null:
list1 = null;
var list2 = [0, ...?list1]; // [0]

これはセットとマップにも当てはまります。

dart
// Spread operator with maps
var map1 = {'foo': 'bar', 'key': 'value'};
var map2 = {'foo': 'baz', ...map1}; // {foo: bar, key: value}
// Spread operator with sets
var set1 = {'foo', 'bar'};
var set2 = {'foo', 'baz', ...set1}; // {foo, baz, bar}

Collection if/for

#

Dartでは、forおよびifキーワードはコレクションに関して追加の機能を持っています。

コレクションifステートメントは、指定された条件が満たされた場合にのみ、リストリテラルから項目を含めます。

dart
var nav = [
  'Home',
  'Furniture',
  'Plants',
  if (promoActive) 'Outlet',
];

これはマップやセットについても同様に機能します。

コレクションforステートメントにより、複数の項目を別のリストにマッピングできます。

dart
var listOfInts = [1, 2, 3];
var listOfStrings = [
  '#0',
  for (var i in listOfInts) '#$i',
]; // [#0, #1, #2, #3]

これはマップやセットについても同様に機能します。

非同期

#

JavaScriptと同様に、Dart仮想マシン(VM)は、すべてのDartコードを処理する単一のイベントループを実行します。これは、非同期性に関する同様のルールがここに適用されることを意味します。すべてのコードは同期的に実行されますが、利用可能な非同期ツールをどのように使用するかによって、異なる順序で処理できます。これらの構造の一部と、JavaScriptの対応するものとの関連性を以下に示します。

Future

#

FutureはDartのJavaScript Promiseバージョンです。どちらも非同期操作の結果であり、後で解決されます。

DartまたはDartパッケージの関数は、値が後で利用可能になる可能性があるため、それらが表す値ではなくFutureを返す場合があります。

以下の例は、DartでFutureを処理する方法がJavaScriptでPromiseを処理する方法と同じであることを示しています。

js
const httpResponseBody = func();

httpResponseBody.then(value => {
  console.log(
    `Promise resolved to a value: ${value}`
  );
});
dart
Future<String> httpResponseBody = func();

httpResponseBody.then((String value) {
  print('Future resolved to a value: $value');
});

同様に、FutureはPromiseのように失敗する可能性があります。エラーのキャッチも同様です。

js
httpResponseBody
  .then(...)
  .catch(err => {
    console.log(
      "Promise encountered an error before resolving."
    );
  });
dart
httpResponseBody
  .then(...)
  .catchError((err) {
    print(
      'Future encountered an error before resolving.'
    );
  });

Futureを作成することもできます。Futureを作成するには、async関数を定義して呼び出します。Futureにする必要がある値がある場合、以下の例のように関数を変換します。

dart
String str = 'String Value';
Future<String> strFuture = Future<String>.value(str);

Async/Await

#

JavaScriptのPromiseに慣れている場合は、async/await構文にも慣れている可能性が高いです。この構文はDartでも同一です。関数はasyncとマークされ、async関数は常にFutureを返します。関数がStringを返し、asyncとマークされている場合、代わりにFuture<String>を返します。何も返さないがasyncである場合、Future<void>を返します。

以下の例は、async関数を記述する方法を示しています。

js
// Returns a Promise of a string,
// as the method is async
async fetchString() {
  // Typically some other async
  // operations would be done here.
  return "String Value";
}
dart
// Returns a future of a string,
// as the method is async
Future<String> fetchString() async {
  // Typically some other async
  // operations would be done here.
  return 'String Value';
}

このasync関数は次のように呼び出します。

dart
Future<String> stringFuture = fetchString();
stringFuture.then((String str) {
  print(str); // 'String Value'
});

awaitキーワードを使用してFutureの値を取得します。JavaScriptと同様に、これによりFutureのthenを呼び出して値を取得する必要がなくなり、非同期コードをより同期的に記述できます。JavaScriptと同様に、Futureのawaitはasyncコンテキスト(別のasync関数など)内でのみ可能です。

以下の例は、Futureをawaitしてその値を取得する方法を示しています。

dart
// We can only await futures within an async context.
Future<void> asyncFunction() async {
  var str = await fetchString();
  print(str); // 'String Value'
}

Futureおよびasync/await構文の詳細については、非同期プログラミングチュートリアルを参照してください。

Stream

#

Dartの非同期ツールボックスのもう1つのツールはStreamです。JavaScriptには独自のストリームの概念がありますが、Dartのストリームは、一般的に使用されるrxjsライブラリにあるObservableに似ています。このライブラリに慣れている場合は、Dartのストリームは馴染み深いものとなるはずです。

これらの概念に慣れていない方へ:Streamは基本的にFutureのように機能しますが、複数の値が時間とともに分散され、イベントバスのようになります。コードはストリームをリッスンでき、完了または失敗状態に達することができます。

リスニング

#

ストリームをリッスンするには、そのlistenメソッドを呼び出し、コールバックメソッドを指定します。ストリームが値を発行するたびに、Dartはこのメソッドを呼び出します。

dart
Stream<int> stream = ...
stream.listen((int value) {
  print('A value has been emitted: $value');
});

listenメソッドには、エラーを処理するためのオプションのコールバック、またはストリームが完了したときのコールバックが含まれています。

dart
stream.listen(
  (int value) { ... },
  onError: (err) {
    print('Stream encountered an error! $err');
  },
  onDone: () {
    print('Stream completed!');
  },
);

listenメソッドはStreamSubscriptionのインスタンスを返します。これを使用してストリームのリッスンを停止できます。

dart
StreamSubscription subscription = stream.listen(...);
subscription.cancel();

これはストリームをリッスンする唯一の方法ではありません。Futureasync/await構文と同様に、asyncコンテキストでストリームとfor-inループを組み合わせることができます。forループは、発行された各項目に対してコールバックメソッドを呼び出し、ストリームが完了またはエラーになるまで終了します。

dart
Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (final value in stream) {
    sum += value;
  }
  return sum;
}

この方法でストリームをリッスン中にエラーが発生した場合、エラーはawaitキーワードを含む行で再スローされます。try-catchステートメントでこのエラーを処理できます。

dart
try {
  await for (final value in stream) { ... }
} catch (err) {
  print('Stream encountered an error! $err');
}

Stream の作成

#

Futureと同様に、ストリームを作成するにはいくつかの異なる方法があります。Streamクラスには、FutureまたはIterableからストリームを作成するためのユーティリティコンストラクタ、または時間間隔で値を発行するストリームを作成するためのコンストラクタがあります。詳細については、Stream APIページを参照してください。

StreamController
#

ユーティリティクラスStreamControllerは、ストリームを作成および制御できます。そのstreamプロパティは、制御しているストリームを公開します。そのメソッドは、そのストリームにイベントを追加する方法を提供します。

たとえば、addメソッドは新しい項目を発行でき、closeメソッドはストリームを完了します。

以下の例は、ストリームコントローラーの基本的な使用法を示しています。

dart
var listeners = 0;
StreamController<int>? controller;
controller = StreamController<int>(
  onListen: () {
    // Emit a new value every time the stream gets a new listener.
    controller!.add(listeners++);
    // Close the stream after the fifth listener.
    if (listeners > 5) controller.close();
  }
);
// Get the stream for the stream controller
var stream = controller.stream;
// Listen to the stream
stream.listen((int value) {
  print('$value');
});
非同期ジェネレータ
#

非同期ジェネレータ関数はストリームを作成できます。これらの関数は同期ジェネレータ関数に似ていますが、async*キーワードを使用し、Streamを返します。

非同期ジェネレータ関数では、yieldキーワードは指定された値をストリームに発行します。ただし、yield*キーワードは、他のiterableではなくストリームと連携します。これにより、他のストリームからのイベントをこのストリームに発行できます。以下の例では、新しくyieldされたストリームが完了すると、関数は続行されます。

dart
Stream<int> asynchronousNaturalsTo(int n) async* {
  var k = 0;
  while (k < n) yield k++;
}

Stream<int> stream = asynchronousNaturalsTo(5);

// Prints each of 0 1 2 3 4 in succession.
stream.forEach(print(value));

Future、Stream、およびその他の非同期機能の詳細については、非同期プログラミングドキュメントを参照してください。

クラス

#

表面上、DartのクラスはJavaScriptのクラスに似ていますが、JavaScriptのクラスは技術的にはプロトタイプのラッパーです。Dartでは、クラスは言語の標準機能です。このセクションでは、Dartでのクラスの定義と使用方法、およびJavaScriptとの違いについて説明します。

"this" コンテキスト

#

Dartのthisキーワードは、JavaScriptよりも直接的です。Dartでは、関数をthisにバインドすることはできず、thisはJavaScriptのように実行コンテキストに依存しません。Dartでは、thisはクラス内でのみ使用され、常に現在のインスタンスを参照します。

コンストラクタ

#

このセクションでは、DartのコンストラクタがJavaScriptのものとどのように異なるかについて説明します。

標準コンストラクタ

#

標準クラスコンストラクタは、JavaScriptコンストラクタと非常によく似ています。Dartでは、constructorキーワードは完全なクラス名に置き換えられ、すべてのパラメータは明示的に型付けされる必要があります。Dartでは、newキーワードはかつてクラスインスタンスの作成に必要でしたが、現在はオプションであり、その使用は推奨されなくなりました。

dart
class Point {
  final double x;
  final double y;

  Point(double x, double y) : this.x = x, this.y = y { }
}

// Create a new instance of the Point class
Point p = Point(3, 5);

初期化子リスト

#

初期化リストを使用してコンストラクタを記述します。初期化リストをコンストラクタのパラメータと本体の間に挿入します。

dart
class Point {
  ...
  Point.fromJson(Map<String, double> json)
      : x = json['x']!,
        y = json['y']! {
    print('In Point.fromJson(): ($x, $y)');
  }
  ...
}

コンストラクタパラメータ

#

コンストラクタでクラスフィールドを代入するコードを書くのは、ボイラープレートコードを作成するような感覚になることがあるため、Dartにはこれを容易にするための構文糖衣、初期化パラメータと呼ばれるものがあります。

dart
class Point {
  double x;
  double y;

  // Syntactic sugar for setting x and y
  // before the constructor body runs.
  Point(this.x, this.y);
}

// Create a new instance of the Point class
Point p = Point(3, 5);

関数と同様に、コンストラクタは位置パラメータまたは名前付きパラメータを取るオプションがあります。

dart
class Point {
  ...
  // With an optional positioned parameter
  Point(this.x, [this.y = 5]);
  // With named parameters
  Point({ required this.y, this.x = 5 });
  // With both positional and named parameters
  Point(int x, int y, { boolean multiply }) {
    ...
  }
  ...
}

名前付きコンストラクタ

#

JavaScriptとは異なり、Dartはクラスに名前を付けることで複数のコンストラクタを持たせることを許可しています。オプションで1つの名前なしコンストラクタを持つことができます。追加のコンストラクタは名前付きである必要があります。

dart
const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  double x = 0;
  double y = 0;

  Point(this.x, this.y);

  // Named constructor
  Point.origin()
      : x = xOrigin,
        y = yOrigin;
}

constコンストラクタ

#

不変クラスインスタンスを有効にするには、constコンストラクタを使用します。constコンストラクタを持つクラスはfinalインスタンス変数のみを持つことができます。

dart
class ImmutablePoint {
  final double x, y;

  const ImmutablePoint(this.x, this.y);
}

コンストラクタのリダイレクト

#

コードの重複を防ぐため、またはパラメータに追加のデフォルトを追加するために、他のコンストラクタからコンストラクタを呼び出すことができます。

dart
class Point {
  double x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(double x) : this(x, 0);
}

ファクトリコンストラクタ

#

新しいクラスインスタンスを作成する必要がない場合は、ファクトリコンストラクタを使用できます。一例として、キャッシュされたインスタンスを返す場合が挙げられます。

dart
class Logger {
  static final Map<String, Logger> _cache =
      <String, Logger>{};
 
  final String name;
 
  // Factory constructor that returns a cached copy,
  // or creates a new one if it is not yet available.
  factory Logger(String name) {
    return _cache.putIfAbsent(
        name, () => _cache[name] ??= Logger._internal(name);
  }

  // Private constructor for internal use only
  Logger._internal(this.name);
}

メソッド

#

DartとJavaScriptの両方で、メソッドはオブジェクトの動作を提供する関数として機能します。

js
function doSomething() { // This is a function
  // Implementation..
}

class Example {
  doSomething() { // This is a method
    // Implementation..
  }
}
dart
void doSomething() { // This is a function
 // Implementation..
}

class Example {
 void doSomething() { // This is a method
   // Implementation..
 }
}

クラスの拡張

#

Dartはクラスが他のクラスを拡張することを許可しており、これはJavaScriptが行う方法と同じです。

dart
class Animal {
  int eyes;
 
  Animal(this.eyes);
 
  makeNoise() {
    print('???');
  }
}

class Cat extends Animal {
  Cat(): super(2);

  @override
  makeNoise() {
    print('Meow');
  }
}
Animal animal = Cat();
print(animal.eyes); // 2
animal.makeNoise(); // Meow

親クラスのメソッドをオーバーライドする場合は、@overrideアノテーションを使用します。このアノテーションはオプションですが、オーバーライドが意図的であることを示しています。Dartアナライザは、メソッドが実際にはスーパークラスメソッドをオーバーライドしていない場合に警告を表示します。

オーバーライドされている親メソッドは、superキーワードを使用して引き続き呼び出すことができます。

dart
class Cat extends Animal {
  ...
  @override
  makeNoise() {
    print('Meow');
    super.makeNoise();
  }
}
Animal animal = Cat();
animal.makeNoise(); // Meow
                    // ???

インターフェースとしてのクラス

#

JavaScriptと同様に、Dartにはインターフェースの個別の定義はありません。ただし、JavaScriptとは異なり、すべてのクラス定義はインターフェースとしても機能します。implementsキーワードを使用して、クラスをインターフェースとして実装できます。

クラスがインターフェースとして実装される場合、その公開APIは新しいクラスによって実装される必要があります。extendsとは異なり、メソッドとフィールドの実装は新しいクラスと共有されません。クラスは1つのクラスのみを拡張できますが、実装クラスが既に別のクラスを拡張している場合でも、複数のインターフェースを同時に実装できます。

dart
class Consumer {
  consume() {
    print('Eating food...');
  }
}
class Cat implements Consumer {
  consume() {
    print('Eating mice...');
  }
}
Consumer consumer = Cat();
consumer.consume(); // Eating mice

インターフェースを実装する場合、メソッド本体は継承されないため、superメソッドを呼び出すことはできません。

dart
class Cat implements Consumer {
  @override
  consume() {
    print('Eating mice...');
    super.consume(); 
    // Invalid. The superclass `Object` has no `consume` method.
  }
}

抽象クラスと抽象メソッド

#

クラスが拡張されたり、インターフェースが実装されたりするだけで、インスタンスの構築が許可されないことを保証するには、それをabstractとしてマークします。

abstractとしてマークされたクラスは、本体を必要とせず、クラスが拡張されたり、インターフェースが実装されたりしたときに実装が必要な抽象メソッドを持つことができます。

dart
abstract class Consumer {
  consume();
}
// Extending the full class
class Dog extends Consumer {
  consume() {
    print('Eating cookies...');
  }
}
// Just implementing the interface
class Cat implements Consumer {
  consume() {
    print('Eating mice...');
  }
}
Consumer consumer;
consumer = Dog();
consumer.consume(); // Eating cookies...
consumer = Cat();
consumer.consume(); // Eating mice...

Mixin

#

Mixinは、クラス間の機能共有に使用されます。Mixinのフィールドとメソッドをクラスで使用でき、それらの機能をクラスの一部であるかのように使用できます。クラスは複数のMixinを使用できます。これは、複数のクラスが互いに継承したり共通の祖先を共有したりすることなく、同じ機能を共有する場合に役立ちます。

withキーワードを使用して、1つ以上のカンマ区切りのMixinをクラスに追加します。

JavaScriptにはキーワードの同等物はありません。JavaScriptはObject.assignを使用して、インスタンス化後に既存のオブジェクトに追加のオブジェクトをマージできます。

以下の例は、JavaScriptとDartが同様の動作をどのように達成するかを示しています。

js
class Animal {}

// Defining the mixins
class Flyer {
  fly = () => console.log('Flaps wings');
}
class Walker {
  walk = () => console.log('Walks on legs');
}
 
class Bat extends Animal {}
class Goose extends Animal {}
class Dog extends Animal {}

// Composing the class instances with
// their correct functionality.
const bat =
  Object.assign(
    new Bat(),
    new Flyer()
    );
const goose =
  Object.assign(
    new Goose(),
    new Flyer(),
    new Walker()
    );
const dog =
  Object.assign(
    new Dog(),
    new Walker()
    );

// Correct calls
bat.fly();
goose.fly();
goose.walk();
dog.walk();
// Incorrect calls
bat.walk(); // `bat` lacks the `walk` method
dog.fly(); // `dog` lacks the `fly` method
dart
abstract class Animal {}

// Defining the mixins
class Flyer {
  fly() => print('Flaps wings');
}
class Walker {
  walk() => print('Walks on legs');
}
 
class Bat extends Animal with Flyer {}
class Goose extends Animal with Flyer, Walker {}
class Dog extends Animal with Walker {}

// Correct calls
Bat().fly();
Goose().fly();
Goose().walk();
Dog().walk();
// Incorrect calls
Bat().walk(); // Not using the Walker mixin
Dog().fly(); // Not using the Flyer mixin

または、classキーワードをmixinに置き換えることで、Mixinが通常のクラスとして使用されないようにすることができます。

dart
mixin Walker {
  walk() => print('Walks legs');
}
// Not possible, as Walker is no longer a class.
class Bat extends Walker {}

複数のMixinを使用できるため、同じクラスで使用されるMixin間で、メソッドまたはフィールドが重複する可能性があります。また、それらを使用するクラス、またはそのクラスのスーパークラスと重複することさえあります。それらがクラスに追加される順序が重要です。

例を挙げると。

dart
class Bird extends Animal with Consumer, Flyer {

Birdのインスタンスでメソッドが呼び出されると、Dartはまず独自のクラスBirdから開始します。これは他の実装よりも優先されます。Birdに実装がない場合、Flyerがチェックされ、次にConsumerがチェックされ、実装が見つかるまで続行されます。親クラスAnimalは最後にチェックされます。

拡張機能

#

クラスの拡張、インターフェースの実装、またはMixinの使用はすべて、影響を受けるクラスが編集可能な場合に機能します。ただし、既存のクラスや別のライブラリまたはDart SDKの一部であるクラスを拡張することが有用な場合があります。

これらの場合、Dartは既存のクラスの拡張機能を記述する機能を提供します。

例として、Dart SDKのStringクラスの以下の拡張機能は、整数の解析を許可します。

dart
extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }
}

拡張機能が利用可能になるには、同じファイルに存在するか、そのファイルがインポートされている必要があります。

次のように使用します。

dart
import 'string_apis.dart'; // Import the file the extension is in
var age = '42'.parseInt(); // Use the extension method.

ゲッターとセッター

#

Dartのgetterとsetterは、JavaScriptの対応するものとまったく同じように機能します。

js
class Person {
  _age = 0;

  get age() {
    return this._age;
  }

  set age(value) {
    if (value < 0) {
      throw new Error(
        'age cannot be negative'
        );
    }
    this._age = value;
  }
}

var person = new Person();
person.age = 10;
console.log(person.age);
dart
class Person {
  int _age = 0;
 
  int get age {
    return _age;
  }
 
  set age(int value) {
    if (value < 0) {
      throw ArgumentError(
        'Age cannot be negative'
        );
    }
    _age = value;
  }
}

void main() {
  var person = Person();
  person.age = 10;
  print(person.age);
}

公開メンバーと非公開メンバー

#

JavaScriptと同様に、Dartにはアクセス修飾子キーワードはありません。すべてのクラスメンバーはデフォルトで公開されます。

JavaScriptは、EcmaScript標準の次の実用的な改訂で、プライベートクラスメンバーを含める予定です。そのため、この実装はさまざまなブラウザやランタイムでしばらくの間利用可能でした。

JavaScriptでクラスメンバーをプライベートにするには、その名前にポンド(またはハッシュ)記号(#)を付けます。

js
class Animal {
  eyes; // Public field
  #paws; // Private field

  #printEyes() { // Private method
    print(this.eyes);
  }

  printPaws() { // Public method
    print(this.#paws);
  }
}

Dartでクラスメンバーをプライベートにするには、その名前にアンダースコア(_)を付けます。

dart
class Animal {
  int eyes; // Public field
  int _paws; // Private field

  void _printEyes() { // Private method
    print(this.eyes);
  }

  void printPaws() { // Public method
    print(this._paws);
  }
}

JavaScriptはハッシュを規約として使用します。Dartのコンパイラは、この機能のためにアンダースコアの使用を強制します。

Dartはプライベートメンバーをクラスではなくライブラリにプライベートにします。これは、同じライブラリ内のコードからプライベートメンバーにアクセスできることを意味します。デフォルトでは、Dartはプライベートクラスメンバーへのアクセスを同じファイル内のコードに制限します。ライブラリのスコープを1つのファイル以上に拡張するには、partディレクティブを追加します。可能な場合は、partの使用は避けてくださいpartの使用はコードジェネレータのために予約してください。

late 変数

#

Dartがクラスフィールドを後で初期化することを示すには、それらのクラスフィールドにlateキーワードを割り当てます。これらのクラスフィールドはnull許容のままです。変数をすぐに監視またはアクセスする必要がなく、後で初期化できる場合はこれを行います。これは、フィールドをnull許容としてマークすることとは異なります。

  • (null許容でない)lateフィールドには、後でnullを割り当てることはできません。

  • (null許容でない)lateフィールドは、初期化される前にアクセスされると実行時エラーをスローします。これは避けるべきです。

dart
class PetOwner {
  final String name;
  late final Pet _pet;
  PetOwner(this.name, String petName) {
    // Cyclic object graph, cannot set _pet before owner exists.
    _pet = Pet(petName, this);
  }
  Pet get pet => _pet;
}
class Pet {
  final String name;
  final PetOwner owner;
  Pet(this.name, this.owner);
}

コンパイラがコードが変数を初期化したかどうかを判断できない場合にのみ、lateをローカル変数に使用します。

dart
doSomething(int n, bool capture) {
  late List<Foo> captures;
  if (capture) captures = [];
  for (var i = 0; i < n; i++) {
    var foo = something(i);
    if (capture) captures.add(foo);
  }
}

前の例では、captureがtrueの場合、コンパイラはcapturesを割り当てるかどうかを判断できません。lateを使用すると、実行時まで通常の「割り当て済み」チェックが遅延されます。

ジェネリクス

#

JavaScriptはジェネリクスを提供していませんが、Dartは型安全性を向上させ、コードの重複を減らすために提供しています。

ジェネリックメソッド

#

メソッドにジェネリクスを適用できます。ジェネリック型パラメータを定義するには、メソッド名の後に角括弧< >の間に配置します。その後、この型をメソッド内で戻り値の型として、またはメソッドのパラメータ内で使用できます。

dart
Map<Object?, Object?> _cache = {};
T cache<T>(T value) => (_cache[value] ??= value) as T;

複数のジェネリック型を定義するには、カンマで区切ります。

dart
// Defining a method with multiple generics.
T transform<T, Q>(T param1, Q param2) {
   ...
}
// Calling the method with explicitly defined types.
transform<int, String>(5, 'string value');
// Types are optional when the analyzer can infer them.
transform(5, 'string value');

ジェネリッククラス

#

ジェネリクスはクラスにも適用できます。コンストラクタを呼び出すときに使用する型を含めることができます。これにより、再利用可能なクラスを特定の型に調整できます。

以下の例では、Cacheクラスは特定の型をキャッシュします。

dart
class Cache<T> {
  T getByKey(String key) {}
  void setByKey(String key, T value) {}
}
// Creating a cache for strings
var stringCache = Cache<String>(); // stringCache has type Cache<String>
stringCache.setByKey('Foo', 'Bar'); // Valid, setting a string value.
stringCache.setByKey('Baz', 5); // Invalid, int type does not match generic.

型宣言を省略した場合、実行時型はCache<dynamic>になり、setByKeyの両方の呼び出しが有効になります。

ジェネリクスの制限

#

ジェネリクスを使用して、extendsを使用してコードを型のファミリーに制限できます。これにより、クラスが特定の型に拡張するジェネリック型でインスタンス化されることが保証されます。

dart
class NumberManager<T extends num> {
   ...
}
// Valid.
var manager = NumberManager<int>();
var manager = NumberManager<double>();
// Invalid, String nor its parent classes extend num.
var manager = NumberManager<String>();

リテラルでのジェネリクス

#

MapSet、およびListリテラルは型引数を受け入れることができます。これは、Dartが型を推論できない場合や、型を正しく推論できない場合に役立ちます。

たとえば、Listクラスはジェネリック定義class List<E>を持っています。型パラメータEは、リストの内容の型を参照します。通常、この型は自動的に推論され、一部のListクラスのメンバー型で使用されます。(たとえば、その最初のgetterはE型の値を返します。)Listリテラルを定義するときは、次のようにジェネリック型を明示的に定義できます。

dart
// Automatic type inference
var objList = [5, 2.0]; // Type: List<num>
// Explicit type definition:
var objList = <Object>[5, 2.0]; // Type: List<Object>
// Sets work identically:
var objSet = <Object>{5, 2.0};

これは、キーと値の型をジェネリクス(class Map<K, V>)で定義するMapにも当てはまります。

dart
// Automatic type inference
var map = {
  'foo': 'bar'
}; // Type: Map<String, String>
// Explicit type definition:
var map = <String, Object>{
  'foo': 'bar'
}; // Type: Map<String, Object>

ドキュメントコメント

#

通常のコメントは、DartではJavaScriptと同じように機能します。//を使用すると、それ以降のすべてを行の残りの部分でコメントアウトできます。/* ... */を使用して、複数行にまたがるブロックコメントを作成できます。

通常のコメントに加えて、Dartにはドキュメントコメントもあります。これはdart docと連携して機能します。dart docは、DartパッケージのHTMLドキュメントを生成するファーストパーティツールです。ドキュメントコメントを公開メンバーのすべての宣言の上に配置することがベストプラクティスと見なされています。

2つではなく3つのスラッシュ(///)を使用してドキュメントコメントを定義します。

dart
/// The number of characters in this chunk when unsplit.
int get length => ...

次のステップ

#

このガイドでは、DartとJavaScriptの主な違いについて説明しました。この時点で、Dartのドキュメントを読むことを検討してください。Flutterのドキュメントを読むこともできます。Dartで構築されたFlutterは、単一のコードベースからネイティブにコンパイルされたマルチプラットフォームアプリケーションを構築するためにDartを使用するオープンソースフレームワークです。これらのドキュメントは、言語に関する詳細情報と、開始するための実践的な方法を提供します。

可能な次のステップ