目次

JavaScript開発者向けのDart学習

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

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

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

Dartでは、デフォルトでNull安全性が有効になっています。JavaScriptはNull安全性をサポートしていません。JavaScript開発者として、Null安全なコードの記述方法を習得するには時間がかかるかもしれませんが、その見返りとして、Dartコードをコンパイルする前でも検出されるNull参照例外に対するより良い保護が得られます。(したがって、JavaScript変数に対してNullであることが判明した場合に操作を行うときに発生する恐ろしいTypeErrorを回避できます。)

規約とLint

#

JavaScriptとDartには、標準的な規約を強制するためのlintツールがあります。JavaScriptには多くのツール、標準、構成がありますが、Dartにはレイアウトとスタイルの規約の公式セットが1つと、コンプライアンスを簡素化するlinterが1つあります。Dartアナライザーは、より分析的な機能を提供するとともに、コードをlintします。プロジェクトのlintルールをカスタマイズするには、静的解析のカスタマイズの手順に従ってください。

Dartには、エラーを検出して修正するためのdart fixが用意されています。

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

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

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

組み込み型

#

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

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

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

  • 数値(numintdouble
  • 文字列(String
  • ブール値(bool
  • リスト(List、配列とも呼ばれます)
  • セット(Set
  • マップ(Map
  • シンボル(Symbol
  • nullNull

詳細については、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のテンプレートリテラルと同様に、${<式>}構文を使用して、式を文字列リテラルに挿入できます。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つの値は、値または式がtruefalseかを表します。リテラルtruefalseを使用して値を返すか、x < 5y == nullなどの式を使用して値を生成できます。

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

変数

#

Dartの変数は、2つの例外を除いて、JavaScriptの変数と同様に機能します

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

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

  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開発者は、実行時ではなくコードの作成時にnull参照例外をキャッチできるため、メリットがあります。

Nullable型と非Nullable型

#

次のコード例の変数のいずれも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.

実行時に、aが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が関数式をコンストラクターのように動作させたり、このへのカスタムバインディングを作成したりするためのサポートなどです。

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

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 キーワードを使用して最終的なイテラブルにアイテムを追加するか、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));

非同期ジェネレーター関数を定義することもできます。これは、イテラブルではなくストリームを返します。詳細については、後続の 非同期 セクションを参照してください。

ステートメント

#

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

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

#

ほとんどの制御ステートメントは、JavaScript の対応するステートメントと同様に動作します。一部には、コレクションに対する追加の使用法があります。

反復

#

JavaScript と Dart の両方に for-in ループがありますが、その動作は異なります。

JavaScript の for-in ループは、オブジェクトのプロパティを反復処理します。JavaScript のイテラブルオブジェクトの要素を反復処理するには、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 を使用する場合、case に配置されたラベルと組み合わせることができます。

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 (式の値は x)x++x++
x = x - 1 (式の値は x - 1)--x--x
x = x - 1 (式の値は x)x--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 は「偽」の値と見なされます。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'));

length を使用して、List 内の値の数を取得します。

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

isEmpty を使用して、List が空かどうかを確認します。

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

isNotEmpty を使用して、List が空でないかどうかを確認します。

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

埋め込み

#

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

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

生成

#

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'));

length を使用して、Set 内の値の数を取得します。

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

isEmpty を使用して、Set が空かどうかを確認します。

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

isNotEmpty を使用して、Set が空でないかどうかを確認します。

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'];

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

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

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

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'),
]);

Mapからエントリを削除するには、removeメソッドを使用します。指定されたテストを満たすすべてのエントリを削除するには、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のサイズまたは内容を変更することは依然として可能です。
    final fruits = <String>{'apple', 'orange', 'pear'};
  • unmodifiableコンストラクターを使用して、コレクション型のfinalバージョンを作成します(次の例を参照)。これにより、サイズまたは内容を変更できないコレクションが作成されます。
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}

コレクション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対応物との関係を示します。

Futures

#

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

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');
});

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

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.'
    );
  });

futuresを作成することもできます。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と同様に、これにより、値を取得するためにFuturethenを呼び出す必要がなくなり、より同期的な方法で非同期コードを記述できます。JavaScriptと同様に、futureの待機はasyncコンテキスト(別のasync関数など)内でのみ可能です。

次の例は、値のためにfutureを待機する方法を示しています。

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

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

Streams

#

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は、ストリームを作成および制御できます。そのストリームプロパティは、制御するストリームを公開します。そのメソッドは、ストリームにイベントを追加する方法を提供します。

たとえば、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*キーワードは、他のイテラブルではなくストリームで動作します。これにより、他のストリームからのイベントをこのストリームに放出できます。次の例では、新しく生成されたストリームが完了すると、関数は続行します。

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

futures、ストリーム、その他の非同期機能の詳細については、非同期プログラミングドキュメントを参照してください。

クラス

#

表面上、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);
}

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

#

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

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とは異なり、そのメソッドとフィールドの実装は新しいクラスと共有されません。クラスは単一のクラスのみを拡張できますが、実装クラスがすでに別のクラスを拡張している場合でも、一度に複数のインターフェイスを実装できます。

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のフィールドとメソッドをクラスで使用でき、クラスの一部であるかのようにその機能を使用できます。1つのクラスは複数の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を使用できるため、同じクラスで使用する場合、互いに重複するメソッドまたはフィールドを持つことができます。それらは、それらを使用するクラス、またはそのクラスのスーパークラスと重複することさえあります。クラスに追加される順序が重要です。

例を挙げると

dart
class Bird extends Animal with Consumer, Flyer {

Birdのインスタンスでメソッドが呼び出されると、Dartは独自のクラスであるBirdから開始します。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のゲッターとセッターは、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にはアクセス修飾子のキーワードはありません。すべてのクラスメンバーはデフォルトでpublicです。

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の使用を予約してください。

遅延変数

#

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クラスのメンバー型で使用されます。(たとえば、最初のゲッターは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};

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

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>

Docコメント

#

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

通常のコメントに加えて、Dartにはドキュメントコメントもあり、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を使用するオープンソースフレームワークです。これらのドキュメントでは、言語に関する詳細な情報と、開始するための実用的な方法を提供しています。

いくつかの可能な次のステップ