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は、プリミティブ型num
、string
、boolean
、および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
を返します。次のコードに示す例を確認してください。
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
を共有しています。
Dartでは数値がオブジェクトとして扱われるため、数値はオブジェクトメソッドとして独自のユーティリティ関数を公開できます。数値に関数を適用するために追加のオブジェクトを使用する必要はありません。
たとえば、double
を整数に丸めるには
let rounded = Math.round(2.5);
var rounded = 2.5.round();
文字列
#Dartの文字列はJavaScriptの文字列と同様に機能します。文字列リテラルを記述するには、一重引用符('
)または二重引用符("
)で囲みます。Dart開発者の大半は一重引用符を使用しますが、言語は標準を強制しません。文字列内の一重引用符をエスケープしたくない場合は、二重引用符を使用します。
var a = 'This is a string.';
特殊文字のエスケープ
#文字列補間に使用される$
のように、別の意味を持つ文字を文字列に含めるには、その文字をエスケープする必要があります。Dartでの特殊文字のエスケープは、JavaScriptや他のほとんどの言語と同様に機能します。特殊文字をエスケープするには、その文字の前にバックスラッシュ文字(\
)を付けます。
次のコードにいくつかの例を示します。
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はこの構文を使用し、式が単一の識別子を使用する場合は中括弧を省略できます。
var food = 'bread';
var str = 'I eat $food'; // I eat bread
var str = 'I eat ${food}'; // I eat bread
文字列連結と複数行宣言
#JavaScriptでは、テンプレートリテラルを使用して複数行の文字列を定義できます。Dartには、複数行の文字列を定義する方法が2つあります。
- 暗黙的な文字列連結を使用する:Dartは、複数行にまたがっていても、隣接する文字列リテラルを連結しますdart
final s1 = 'String ' 'concatenation' " even works over line breaks.";
- 複数行文字列リテラルを使用する:文字列の両側に3つの引用符(一重または二重)を使用すると、リテラルは複数行にまたがることができます。dart
final s2 = ''' You can create multiline strings like this one. '''; final s3 = """ This is also a multiline string.""";
等価性
#Dartは、同じコードユニットのシーケンスを含む2つの文字列を等しいと見なします。2つの文字列が同じシーケンスを持つかどうかを判断するには、等価演算子(==
)を使用します。
final s1 = 'String '
'concatenation'
" works even over line breaks.";
assert(s1 ==
'String concatenation works even over '
'line breaks.');
ブール値
#DartとJavaScriptの両方のブール値は、二項条件を表します。これらの2つの値は、値または式がtrue
かfalse
かを表します。リテラルtrue
とfalse
を使用して値を返すか、x < 5
やy == null
などの式を使用して値を生成できます。
let isBananaPeeled = false;
var isBananaPeeled = false;
変数
#Dartの変数は、2つの例外を除いて、JavaScriptの変数と同様に機能します
- 各変数には型があります。
- Dartは、JavaScriptの
let
変数やconst
変数のように、すべての変数をブロックスコープで定義します。
Dartの変数は、次の2つの方法のいずれかで型を取得します
- 宣言済み:宣言に記述された型。
- 推論済み:変数を初期化するために使用される式。慣例により、アナライザーが型を推論できる場合は、
var
またはfinal
を使用します。
// Declare and initialize a variable at once
let name = "bob";
// 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';
変数は、その型の値のみを受け入れることができます。
var name = 'bob';
name = 5; // Forbidden, as `name` has type `String`.
初期値または明示的な型を提供しない場合、Dartは変数の型を包括的な型dynamic
であると推論します。
JavaScriptの変数と同様に、dynamic
型を使用するDart変数に任意の値を代入できます。
// Declare a variable
let name;
// Initialize the variable
name = "bob";
// 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は、これらの不変の値を作成するために限定された式を使用します。これらの式は副作用を持つことはできません。これらの条件下では、コンパイラーは定数変数または式の正確な値を、静的な型だけでなく予測できます。
final String name;
// Cannot read name here, not initialized.
if (useNickname) {
name = "Bob";
} else {
name = "Robert";
}
print(name); // Properly initialized here.
Dartでは、定数変数には定数値を含める必要があります。非定数変数には、const
としてマークすることもできる定数値を含めることができます。
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
にすることはできません。
// 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
を持つ可能性があることを示すには、型宣言に?
を追加します
int? aNullableInt = null;
関数の宣言など、他の型宣言についても同じことが言えます
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の否定(!
)演算子と混同しないでください。否定演算子は同じ記号を使用しますが、式の前に配置します。
int? a = 5;
int b = a; // Not allowed.
int b = a!; // Allowed.
実行時に、aがnull
であることが判明した場合、実行時エラーが発生します。
?.
演算子と同様に、オブジェクトのプロパティまたはメソッドにアクセスする場合は!
演算子を使用します
myObject!.someProperty;
myObject!.someMethod();
実行時にmyObject
がnull
の場合、実行時エラーが発生します。
関数
#Dartの関数はJavaScriptの関数とほぼ同じように機能しますが、追加の機能がいくつかあり、宣言時に構文がわずかに異なる場合があります。JavaScriptと同様に、関数はトップレベル、クラスフィールド、ローカルスコープなど、ほぼどこでも宣言できます。
// 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));
}
// 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
関数は同等です
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
パラメーター
#JavaScriptでは、すべてのパラメーターは位置パラメーターにすることができます。デフォルトでは、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つの状況で変更できます
- 位置パラメーターがオプションとしてマークされている場合。
- パラメーターが名前付きで、必須としてマークされていない場合。
オプションの位置パラメーターを定義するには、必須の位置パラメーターの後ろに角かっこで囲みます。オプションパラメーターの後に必須パラメーターを続けることはできません。
null安全性のため、オプションの位置パラメーターにはデフォルト値があるか、null許容としてマークされている必要があります。詳細については、前のセクションのnull安全性を参照してください。
次のコードには、オプションの位置パラメーターを定義する関数の、有効な例が1つと無効な例が2つあります。
// 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]) {
...
}
次の例は、オプションのパラメーターを持つ関数を呼び出す方法を示しています
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許容としてフラグが付けられている必要があります。
次のコードは、名前付きパラメーターを持つ関数を定義します
// 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}) {
...
}
次の例では、名前付きパラメーターを持つ関数を呼び出します
// 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は関数を他のオブジェクトと同様に扱います。たとえば、次のコードは、関数を別の関数へのパラメーターとして渡す方法を示しています
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
匿名関数
#JavaScriptとDartはどちらも匿名関数、つまり名前のない関数をサポートしています。名前付き関数と同様に、匿名関数を他の値のように渡すことができます。たとえば、匿名関数を変数に格納したり、別の関数の引数として渡したり、別の関数から返したりします。
JavaScriptには、匿名関数を宣言する2つの方法があります
- 標準の関数式を使用する
- アロー構文を使用する
同様に、Dartにも匿名関数を宣言する2つの方法があります。どちらも、JavaScriptのアロー式と同様の方法で機能します。Dartの匿名関数は、通常の関数式に付属する追加機能をサポートしていません。たとえば、JavaScriptが関数式をコンストラクターのように動作させたり、このへのカスタムバインディングを作成したりするためのサポートなどです。
詳細については、クラスセクションを参照してください。
// 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;
// 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
関数を使用するときに匿名関数を渡すことがよくあります
// 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;
});
// 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*
を使用してアイテムのセット全体を追加します。
次の例は、基本的なジェネレーター関数の記述方法を示しています。
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);
}
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());
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);
}
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
のように動作します。
次の例は、コレクションを反復処理し、各要素を出力する方法を示しています。
for (const element of list) {
console.log(element);
}
for (final element in list) {
print(element);
}
Switch
#switch
ステートメントで continue
を使用する場合、case に配置されたラベルと組み合わせることができます。
switch (testEnum) {
case TestEnum.A:
print('A');
continue b;
b:
case TestEnum.B:
print('B');
break;
}
演算子
#Dart と JavaScript の両方に、事前定義された演算子が含まれています。どちらの言語も、新しい演算子の追加はサポートしていません。Dart は、operator
キーワードを使用して既存の演算子をオーバーロードすることをサポートしています。例:
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-- |
例:
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 を除算してフロアされた整数を出力する ~/
演算子(切り捨て除算演算子と呼ばれます)も含まれていることにお気づきでしょう。
assert(25 == 50.4 ~/ 2);
assert(25 == 50.6 ~/ 2);
assert(25 == 51.6 ~/ 2);
等価演算子と関係演算子
#両方の言語の等価演算子と関係演算子は同じように機能します。
意味 | JavaScript 演算子 | Dart 演算子 |
---|---|---|
厳密に等しい | === | == |
抽象的に等しい | == | |
厳密に等しくない | !== | != |
抽象的に等しくない | != | |
より大きい | > | > |
より小さい | < | < |
以上 | >= | >= |
以下 | <= | <= |
==
および !=
JavaScript 演算子には、対応する演算子がありません。
例:
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
型テスト演算子
#テスト演算子の実装は、2 つの言語間で少し異なります。
意味 | JavaScript 演算子 | Dart 演算子 |
---|---|---|
型キャスト | x as T | |
オブジェクトが指定された型を持っている場合に true | x instanceof T | x is T |
オブジェクトに指定された型がない場合に true | !(x instanceof T) | x is! T |
obj is T
の結果は、obj
が T
で指定されたインターフェイスを実装している場合に true です。たとえば、obj is Object?
は常に true です。
型キャスト演算子 (as
) を使用して、値が特定の型であることを確認します。コンパイラーは、オブジェクトがその型を持つことを知っている場合、それを使用できます。
例:
(person as Employee).employeeNumber = 4204583;
オブジェクトが T
型であるかを知らない場合は、オブジェクトを使用する前に is T
を使用して型を確認します。
Dart では、ローカル変数の型は if ステートメントのスコープ内で更新されます。これはインスタンス変数には当てはまりません。
if (person is Employee) {
person.employeeNumber = 4204583;
}
論理演算子
#論理演算子を使用して、ブール式を反転または結合できます。両方の言語の論理演算子は同じです。
意味 | JavaScript 演算子 | Dart 演算子 |
---|---|---|
次の式を反転します(false を true に、true を false に変更します) | !x | !x |
論理 OR | || | || |
論理 AND | && | && |
JavaScript では、ブール値が必要な場合に任意の値を使用できます。次に、これらの値を true
または false
に変換します。JavaScript では、空の文字列と数値 0
は「偽」の値と見なされます。Dart では、条件内および論理演算子のオペランドとして bool
値を使用できます。
例:
if (!done && (col == 0 || col == 3)) {
// ...Do something...
}
ビット演算子とシフト演算子
#整数でビット演算子とシフト演算子を使用することにより、数値の個々のビットを操作できます。次の表に示すように、両方の言語の演算子はほぼ同じです。
意味 | JavaScript 演算子 | Dart 演算子 |
---|---|---|
ビット単位 AND | & | & |
ビット単位 OR | | | | |
ビット単位 XOR | ^ | ^ |
単項ビット単位補数(0 は 1 に、1 は 0 になります) | ~expr | ~expr |
左シフト | << | << |
右シフト | >> | >> |
符号なし右シフト | >>> | >>> |
例:
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 のように式に対して機能します。
let visibility = isPublic ? "public" : "private";
final visibility = isPublic ? 'public' : 'private';
代入演算子
#値の割り当てには (=
) 演算子を使用します。
// Assign value to a
a = value;
この演算子には、null 認識バリアント (??=
) もあります。
詳細については、null 代入演算子のセクションを参照してください。
JavaScript と Dart には、式内の変数に新しい値を計算して割り当てる演算子が含まれています。これらの代入演算子は、右側の値と変数の初期値をオペランドとして使用します。
次の表に、これらの代入演算子を示します。
演算子 | 説明 |
---|---|
= | 代入 |
+= | 加算代入 |
-= | 減算代入 |
*= | 乗算代入 |
/= | 除算代入 |
~/= | 切り捨て除算代入 |
%= | 剰余(剰余)代入 |
>>>= | 符号なし右シフト代入 |
^= | ビット単位 XOR 代入 |
<<= | 左シフト代入 |
>>= | 右シフト代入 |
&= | ビット単位 AND 代入 |
|= | ビット単位 OR 代入 |
JavaScript は ~/=
代入演算子をサポートしていません。
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 にはこの構文がありません。
次の例は、カスケード構文を使用して、新しく構築されたオブジェクトに複数のメソッドを連鎖させる方法を示しています。
var animal = Animal() // Sets multiple properties and methods
..name = "Bob"
..age = 5
..feed()
..walk();
print(animal.name); // "Bob"
print(animal.age); // 5
最初のカスケード構文を null 認識にするには、?..
と記述します。
var result = maybePerson
?..employment = employer
..salary = salary;
maybePerson
値が null
の場合、Dart はカスケード全体を無視します。
コレクション
#このセクションでは、Dart のコレクション型について説明し、JavaScript の同様の型と比較します。
リスト
#Dart は、JavaScript 配列と同じ方法でリストリテラルを記述します。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
から値を取得する方法を示しています。
final fruits = <String>['apple', 'orange', 'pear'];
final fruit = fruits[1];
add
メソッドを使用して、List
の末尾に値を追加します。addAll
メソッドを使用して別の List
を追加します。
final fruits = <String>['apple', 'orange', 'pear'];
fruits.add('peach');
fruits.addAll(['kiwi', 'mango']);
insert
メソッドを使用して、特定の位置に値を挿入します。insertAll
メソッドを使用して、特定の位置に別の List
を挿入します。
final fruits = <String>['apple', 'orange', 'pear'];
fruits.insert(0, 'peach');
fruits.insertAll(0, ['kiwi', 'mango']);
インデックス演算子と代入演算子を組み合わせて、List
内の値を更新します。
final fruits = <String>['apple', 'orange', 'pear'];
fruits[2] = 'peach';
次のいずれかのメソッドを使用して、List
からアイテムを削除します。
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
内の値の数を取得します。
final fruits = <String>['apple', 'orange', 'pear'];
assert(fruits.length == 3);
isEmpty
を使用して、List
が空かどうかを確認します。
var fruits = [];
assert(fruits.isEmpty);
isNotEmpty
を使用して、List
が空でないかどうかを確認します。
final fruits = <String>['apple', 'orange', 'pear'];
assert(fruits.isNotEmpty);
埋め込み
#Dart の List
クラスには、各アイテムが同じ値を持つリストを作成する方法が含まれています。この filled
コンストラクターは、1 つのデフォルト値を持つサイズ n
の固定長のリストを作成します。次の例は、3 つのアイテムのリストを作成します。
final list1 = List.filled(3, 'a'); // Creates: [ 'a', 'a', 'a' ]
- デフォルトでは、このリストから要素を追加または削除することはできません。このリストで要素を追加または削除できるようにするには、パラメーターリストの最後に
, growable: true
を追加します。 - インデックス値を使用して、このリストの要素にアクセスして更新できます。
生成
#Dart List
クラスには、インクリメント値のリストを作成する方法が含まれています。この generate
コンストラクターは、要素値を構築するためのテンプレートを使用して、サイズ n
の固定長のリストを作成します。このテンプレートは、パラメーターとしてインデックスを受け取ります。
// Creates: [ 'a0', 'a1', 'a2' ]
final list1 = List.generate(3, (index) => 'a$index');
セット
#JavaScript とは異なり、Dart はリテラルで Set
を定義することをサポートしています。Dart は、リストと同じ方法で、ただし角かっこではなく中かっこを使用してセットを定義します。セットは、一意のアイテムのみを含む順序付けされていないコレクションです。Dart は、ハッシュコードを使用してこれらのアイテムの一意性を強制します。つまり、オブジェクトを Set
に格納するにはハッシュ値が必要です。
次のコードスニペットは、Set
を初期化する方法を示しています。
final abc = {'a', 'b', 'c'};
空のセットを作成するための構文は、最初混乱するかもしれません。空の中かっこ ({}
) を指定すると、空の Map
が作成されるためです。空の Set
を作成するには、{}
宣言の前に型引数を付けるか、{}
を Set
型の変数に割り当てます。
final names = <String>{};
// Set<String> names = {}; // This works, too.
// final names = {}; // Creates an empty map, not a set.
次の例は、Dart Set
で実行できる基本的なアクションの概要を示しています。
add
メソッドを使用して、Set
に値を追加します。addAll
メソッドを使用して、複数の値を追加します。
final fruits = {'apple', 'orange', 'pear'};
fruits.add('peach');
fruits.addAll(['kiwi', 'mango']);
Set
で次のいずれかのメソッドを使用して、セットからコンテンツを削除します。
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
内の値の数を取得します。
final fruits = {'apple', 'orange', 'pear'};
assert(fruits.length == 3);
isEmpty
を使用して、Set
が空かどうかを確認します。
var fruits = <String>{};
assert(fruits.isEmpty);
isNotEmpty
を使用して、Set
が空でないかどうかを確認します。
final fruits = {'apple', 'orange', 'pear'};
assert(fruits.isNotEmpty);
マップ
#Dart の Map
型は、JavaScript の Map
型に似ています。どちらの型も、キーと値を関連付けます。すべてのキーが同じ型である場合、キーは任意のオブジェクト型にすることができます。このルールは、値にも適用されます。各キーは最大で 1 回発生しますが、同じ値を複数回使用できます。
Dart は、辞書をハッシュテーブルに基づいて作成します。つまり、キーはハッシュ可能である必要があります。すべての Dart オブジェクトにはハッシュが含まれています。
リテラルを使用して作成された、これらの単純な Map
の例を検討してください。
final gifts = {
'first': 'partridge',
'second': 'turtle doves',
'fifth': 'golden rings'
};
final nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
次のコードサンプルは、Dart Map
で実行できる基本的なアクションの概要を示しています。次の例は、インデックス演算子を使用して Map
から値を取得する方法を示しています。
final gifts = {'first': 'partridge'};
final gift = gifts['first'];
Map
にキーが含まれているかどうかを確認するには、containsKey
メソッドを使用します。
final gifts = {'first': 'partridge'};
assert(gifts.containsKey('fifth'));
Map
にエントリを追加または更新するには、インデックス代入演算子 ([]=
) を使用します。Map
にまだキーが含まれていない場合、Dartはエントリを追加します。キーが存在する場合、Dartはその値を更新します。
final gifts = {'first': 'partridge'};
gifts['second'] = 'turtle'; // Gets added
gifts['second'] = 'turtle doves'; // Gets updated
別のMap
を追加するには、addAll
メソッドを使用します。他のエントリをMap
に追加するには、addEntries
メソッドを使用します。
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
メソッドを使用します。
final gifts = {'first': 'partridge'};
gifts.remove('first');
gifts.removeWhere((key, value) => value == 'partridge');
Map
内のキーと値のペアの数を取得するには、length
を使用します。
final gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);
Map
が空かどうかを確認するには、isEmpty
を使用します。
final gifts = {};
assert(gifts.isEmpty);
Map
が空でないかどうかを確認するには、isNotEmpty
を使用します。
final gifts = {'first': 'partridge'};
assert(gifts.isNotEmpty);
変更不可
#純粋なJavaScriptはイミュータビリティをサポートしていません。Dartは、配列、セット、辞書などのコレクションをイミュータブルにするための複数の方法を提供します。
- コレクションがコンパイル時の定数であり、変更すべきでない場合は、
const
キーワードを使用します。const fruits = <String>{'apple', 'orange', 'pear'};
Set
をfinal
フィールドに割り当てます。これはSet
自体がコンパイル時の定数である必要がないことを意味します。これにより、フィールドが別のSet
でオーバーライドされることはありませんが、Set
のサイズまたは内容を変更することは依然として可能です。final fruits = <String>{'apple', 'orange', 'pear'};
unmodifiable
コンストラクターを使用して、コレクション型のfinalバージョンを作成します(次の例を参照)。これにより、サイズまたは内容を変更できないコレクションが作成されます。
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対応スプレッド演算子 (...?
) を使用して、リストを別のリストに埋め込むことをサポートしています。
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]
これは、セットおよびマップでも機能します。
// 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
ステートメントは、指定された条件が満たされた場合にのみ、リストリテラルからアイテムを含めます。
var nav = [
'Home',
'Furniture',
'Plants',
if (promoActive) 'Outlet',
];
これはマップとセットでも同様に機能します。
コレクションfor
ステートメントを使用すると、複数のアイテムを別のリストにマップできます。
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の処理と同じように機能することを示しています。
const httpResponseBody = func();
httpResponseBody.then(value => {
console.log(
`Promise resolved to a value: ${value}`
);
});
Future<String> httpResponseBody = func();
httpResponseBody.then((String value) {
print('Future resolved to a value: $value');
});
同様に、futuresはpromisesのように失敗する可能性があります。エラーのキャッチも同様に機能します。
httpResponseBody
.then(...)
.catch(err => {
console.log(
"Promise encountered an error before resolving."
);
});
httpResponseBody
.then(...)
.catchError((err) {
print(
'Future encountered an error before resolving.'
);
});
futuresを作成することもできます。Future
を作成するには、async
関数を定義して呼び出します。Future
である必要がある値がある場合は、次の例のように関数を変換します。
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
関数を記述する方法を示しています。
// 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";
}
// 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
関数を次のように呼び出します。
Future<String> stringFuture = fetchString();
stringFuture.then((String str) {
print(str); // 'String Value'
});
await
キーワードを使用して、futureの値を取得します。JavaScriptと同様に、これにより、値を取得するためにFuture
でthen
を呼び出す必要がなくなり、より同期的な方法で非同期コードを記述できます。JavaScriptと同様に、futureの待機はasync
コンテキスト(別のasync
関数など)内でのみ可能です。
次の例は、値のためにfutureを待機する方法を示しています。
// We can only await futures within an async context.
Future<void> asyncFunction() async {
var str = await fetchString();
print(str); // 'String Value'
}
Future
とasync
/await
構文の詳細については、非同期プログラミングチュートリアルを参照してください。
Streams
#Dartの非同期ツールボックスのもう1つのツールは、Stream
です。JavaScriptには独自のストリームの概念がありますが、Dartのストリームは、一般的に使用されるrxjs
ライブラリにあるObservable
に近いものです。このライブラリに慣れている場合は、Dartのストリームは馴染み深く感じるはずです。
これらの概念に慣れていない方のために説明すると、Stream
は基本的にFuture
のように機能しますが、イベントバスのように、複数の値が時間の経過とともに分散されます。コードはストリームをリッスンでき、完了するか、失敗状態に達する可能性があります。
リッスン
#ストリームをリッスンするには、そのlisten
メソッドを呼び出し、コールバックメソッドを提供します。ストリームが値を放出するたびに、Dartはこのメソッドを呼び出します。
Stream<int> stream = ...
stream.listen((int value) {
print('A value has been emitted: $value');
});
listen
メソッドには、エラーを処理したり、ストリームが完了したときに処理したりするためのオプションのコールバックが含まれています。
stream.listen(
(int value) { ... },
onError: (err) {
print('Stream encountered an error! $err');
},
onDone: () {
print('Stream completed!');
},
);
listen
メソッドはStreamSubscription
のインスタンスを返します。これを使用してストリームへのリッスンを停止できます。
StreamSubscription subscription = stream.listen(...);
subscription.cancel();
これはストリームをリッスンする唯一の方法ではありません。Future
のasync
/await
構文と同様に、async
コンテキストでストリームとfor-in
ループを組み合わせることができます。for
ループは、放出された各アイテムに対してコールバックメソッドを呼び出し、ストリームが完了またはエラーが発生すると終了します。
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
sum += value;
}
return sum;
}
この方法でストリームをリッスンするときにエラーが発生した場合、エラーはawait
キーワードを含む行で再スローされます。このエラーは、try-catch
ステートメントで処理できます。
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
メソッドはストリームを完了します。
次の例は、ストリームコントローラーの基本的な使用法を示しています。
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*
キーワードは、他のイテラブルではなくストリームで動作します。これにより、他のストリームからのイベントをこのストリームに放出できます。次の例では、新しく生成されたストリームが完了すると、関数は続行します。
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
キーワードがかつて必要でしたが、現在はオプションであり、その使用は推奨されなくなりました。
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);
初期化リスト
#コンストラクターを記述するには、初期化リストを使用します。コンストラクターのパラメーターと本体の間に初期化リストを挿入します。
class Point {
...
Point.fromJson(Map<String, double> json)
: x = json['x']!,
y = json['y']! {
print('In Point.fromJson(): ($x, $y)');
}
...
}
コンストラクターパラメーター
#コンストラクターでクラスフィールドを割り当てるコードを記述すると、ボイラープレートコードを作成しているように感じることがあります。そのため、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);
関数と同様に、コンストラクターには、位置パラメーターまたは名前付きパラメーターを取るオプションがあります。
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つの名前のないコンストラクターを持つことができ、追加のコンストラクターには名前を付ける必要があります。
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
インスタンス変数のみを持つことができます。
class ImmutablePoint {
final double x, y;
const ImmutablePoint(this.x, this.y);
}
コンストラクターリダイレクト
#コードの重複を防いだり、パラメーターの追加のデフォルトを追加したりするために、他のコンストラクターからコンストラクターを呼び出すことができます。
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つの例は、キャッシュされたインスタンスを返す場合です。
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の両方で、メソッドはオブジェクトの動作を提供する関数として機能します。
function doSomething() { // This is a function
// Implementation..
}
class Example {
doSomething() { // This is a method
// Implementation..
}
}
void doSomething() { // This is a function
// Implementation..
}
class Example {
void doSomething() { // This is a method
// Implementation..
}
}
クラスの拡張
#Dartでは、JavaScriptと同様に、クラスが別のクラスを拡張できます。
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
キーワードを使用して呼び出すことができます。
class Cat extends Animal {
...
@override
makeNoise() {
print('Meow');
super.makeNoise();
}
}
Animal animal = Cat();
animal.makeNoise(); // Meow
// ???
インターフェースとしてのクラス
#JavaScriptと同様に、Dartにはインターフェイスの個別の定義はありません。ただし、JavaScriptとは異なり、すべてのクラス定義はインターフェイスとしても機能します。implements
キーワードを使用して、クラスをインターフェイスとして実装できます。
クラスがインターフェイスとして実装されている場合、そのパブリックAPIは新しいクラスによって実装される必要があります。extends
とは異なり、そのメソッドとフィールドの実装は新しいクラスと共有されません。クラスは単一のクラスのみを拡張できますが、実装クラスがすでに別のクラスを拡張している場合でも、一度に複数のインターフェイスを実装できます。
class Consumer {
consume() {
print('Eating food...');
}
}
class Cat implements Consumer {
consume() {
print('Eating mice...');
}
}
Consumer consumer = Cat();
consumer.consume(); // Eating mice
インターフェイスを実装する場合、メソッド本体が継承されないため、super
メソッドを呼び出すことはできません。
class Cat implements Consumer {
@override
consume() {
print('Eating mice...');
super.consume();
// Invalid. The superclass `Object` has no `consume` method.
}
}
抽象クラスと抽象メソッド
#クラスが拡張されたり、インターフェースが実装されたりするだけで、インスタンスの構築を許可しないようにするには、abstract
とマークします。
abstract
とマークされたクラスは、抽象メソッドを持つことができます。抽象メソッドは本体を必要とせず、クラスが拡張されるか、そのインターフェースが実装されるときに実装される必要があります。
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が同様の動作をどのように実現するかを示しています。
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
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が通常のクラスとして使用されるのを防ぐことができます。
mixin Walker {
walk() => print('Walks legs');
}
// Not possible, as Walker is no longer a class.
class Bat extends Walker {}
複数のmixinを使用できるため、同じクラスで使用する場合、互いに重複するメソッドまたはフィールドを持つことができます。それらは、それらを使用するクラス、またはそのクラスのスーパークラスと重複することさえあります。クラスに追加される順序が重要です。
例を挙げると
class Bird extends Animal with Consumer, Flyer {
Bird
のインスタンスでメソッドが呼び出されると、Dartは独自のクラスであるBird
から開始します。Bird
は他の実装よりも優先されます。Bird
に実装がない場合は、Flyer
、次にConsumer
がチェックされ、実装が見つかるまで続きます。親クラスのAnimal
は最後にチェックされます。
拡張
#クラスの拡張、インターフェースの実装、またはmixinの使用はすべて、影響を受けるクラスが編集可能な場合に機能します。ただし、既存のクラス、または別のライブラリやDart SDKの一部であるクラスを拡張すると便利な場合があります。
このような場合、Dartは既存のクラスの拡張機能を記述する機能を提供します。
例として、Dart SDKのString
クラスに対する次の拡張機能は、整数の解析を可能にします。
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
}
拡張機能が利用可能になるには、同じファイル内に存在するか、そのファイルがインポートされている必要があります。
次のように使用します。
import 'string_apis.dart'; // Import the file the extension is in
var age = '42'.parseInt(); // Use the extension method.
ゲッターとセッター
#Dartのゲッターとセッターは、JavaScriptの対応するものとまったく同じように機能します。
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);
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でクラスメンバーをプライベートにするには、その名前の先頭にポンド(またはハッシュ)記号(#
)を付けます。
class Animal {
eyes; // Public field
#paws; // Private field
#printEyes() { // Private method
print(this.eyes);
}
printPaws() { // Public method
print(this.#paws);
}
}
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フィールドは、初期化前にアクセスするとランタイムエラーをスローします。これは避ける必要があります。
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
を使用してください。
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は型安全性を向上させ、コードの重複を減らすために提供しています。
ジェネリックメソッド
#ジェネリックスをメソッドに適用できます。ジェネリック型パラメーターを定義するには、メソッド名の後の山括弧< >
で囲みます。その後、メソッド内でこの型を戻り値の型として、またはメソッドのパラメーター内で使用できます。
Map<Object?, Object?> _cache = {};
T cache<T>(T value) => (_cache[value] ??= value) as T;
複数のジェネリック型を定義するには、コンマで区切ります。
// 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
クラスは特定の型をキャッシュします。
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
を使用して、ジェネリックスを使用し、コードを型ファミリーに制限できます。これにより、クラスが特定の型を拡張するジェネリック型でインスタンス化されることが保証されます。
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>();
リテラルでのジェネリクス
#Map
、Set
、およびList
リテラルは、型引数を受け入れることができます。これは、Dartが型を推論できない場合や、型を正しく推論できない場合に役立ちます。
たとえば、List
クラスにはジェネリック定義class List<E>
があります。型パラメーターE
は、リストの内容の型を指します。通常、この型は自動的に推論され、一部のList
クラスのメンバー型で使用されます。(たとえば、最初のゲッターはE
型の値を返します。)List
リテラルを定義するときは、次のようにジェネリック型を明示的に定義できます。
// 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>
)。
// 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つのフォワードスラッシュ(///
)を使用することで、ドキュメントコメントを定義します。
/// The number of characters in this chunk when unsplit.
int get length => ...
次のステップ
#このガイドでは、DartとJavaScriptの主な違いを紹介しました。この時点で、Dartのドキュメントを読むことを検討してください。Flutterドキュメントを読むこともできます。Dartで構築されたFlutterは、単一のコードベースからネイティブにコンパイルされたマルチプラットフォームアプリケーションを構築するためにDartを使用するオープンソースフレームワークです。これらのドキュメントでは、言語に関する詳細な情報と、開始するための実用的な方法を提供しています。
いくつかの可能な次のステップ
- 言語ツアーで、Dart言語についてさらに詳しく学ぶ
- コアライブラリのドキュメントで、Dartのコアライブラリについて学ぶ
- Dartのチュートリアルで、さまざまなトピックを網羅した実践的な練習を行う
- Effective Dartで、Dartコードを記述する際の一般的な規約とガイドラインについて学ぶ
特に明記されていない限り、このサイトのドキュメントはDart 3.5.3を反映しています。ページ最終更新日: 2024-10-22。 ソースを表示 または 問題を報告する。