diff --git a/src/_guides/language/coming-from/js-to-dart.md b/src/_guides/language/coming-from/js-to-dart.md index 24ac408134..870ce5ef7d 100644 --- a/src/_guides/language/coming-from/js-to-dart.md +++ b/src/_guides/language/coming-from/js-to-dart.md @@ -3,25 +3,25 @@ title: Learning Dart as a JavaScript developer description: Leverage your JavaScript knowledge when learning Dart. --- -This guide aims to leverage your JavaScript (JS) +This guide aims to leverage your JavaScript programming knowledge when learning Dart. It showcases key similarities and differences in both languages, and introduces Dart concepts -that are not present in vanilla JavaScript. +that are not present in JavaScript. As a JavaScript developer, Dart should feel quite familiar, -as both languages share many concepts. +as both languages share many concepts. Like JavaScript, Dart runs on an event loop, so both languages execute code in a similar way. For example, asynchronous concepts like futures -(promises in JS) and the `async/await` syntax are very similar. +(promises in JavaScript) and the `async/await` syntax are very similar. Dart is strongly typed, unlike JavaScript. If you are also familiar with TypeScript or tooling that adds static typing (like Flow), then learning Dart probably won't be much of an extra step for you. -However, if you've mostly worked with vanilla JavaScript, +However, if you've mostly worked with JavaScript, it might take a bit more effort to adjust. The good news is that since Dart is strongly typed, many errors that might exist in JavaScript code are @@ -34,7 +34,7 @@ but the trade-off is better protection against null reference exceptions that are detected even before compiling Dart code. (Thereby avoiding those dreaded `TypeError`s that occur when doing operations -on a JS variable that turns out to be null.) +on a JavaScript variable that turns out to be null.) ## Conventions and linting @@ -48,56 +48,57 @@ a linter to make compliance effortless. To customize the lint rules for your project, follow the [Customizing static analysis][] instructions. -{{site.alert.secondary}} - **Pro tip:** Dart provides [`dart fix`][], - which finds and fixes errors found by the analyzer. -{{site.alert.end}} +Dart provides [`dart fix`][] to find and fix errors that the analyzer finds. -Dart also provides a code formatter, -which is similar to JS tools like [Prettier][]. -Automatically format any Dart project by running -[`dart format`](/tools/dart-format) on the command line. -For Flutter, `flutter format` acts as an alias for this command. -(Note that the IDE plugins for Dart and Flutter -also provide this functionality.) +Dart also provides a code formatter similar to JavaScript tools like [Prettier][]. +To format code in any Dart project, run [`dart format`](/tools/dart-format) on +your command line. In Flutter, use `flutter format`. +The IDE plugins for Dart and Flutter also provide this ability. -{{site.alert.secondary}} - **Pro tip:** Dart supports optional trailing - commas for any comma-separated values, - such as function parameters or list items. - This causes the formatter to place each item - onto its own line, which helps with readability, - especially when you have a lot of nested code - (as can happen in Flutter layout code). -{{site.alert.end}} +Dart supports trailing commas for comma-separated lists of collections, +parameters, or arguments. When you add the trailing comma, +the formatter places each list item on its own line. +When you believe your list may have more items at a later date, +add the trailing comma. Avoid adding the trailing comma for the formatting +benefit alone. -For more information on using commas to make your code -read more like HTML, check out -[Using trailing commas][] on flutter.dev. +JavaScript supports trailing commas in list and map literals only. -For more information about Dart conventions and linting, -check out [Effective Dart][] and [Linter rules][]. +{{site.alert.secondary}} + To learn more about: + + * Using commas to make your code read more like HTML, read + [Using trailing commas][] on flutter.dev. + * Linting Dart, read [Linter rules][]. + * Writing good Dart code, read [Effective Dart][]. +{{site.alert.end}} [Customizing static analysis]: /guides/language/analysis-options [`dart fix`]: /tools/dart-fix -[Effective Dart]: /guides/language/effective-dart +[Effective Dart]: /guides/language/effective-dart [Linter rules]: /tools/linter-rules [Prettier]: https://prettier.io/ [Using trailing commas]: {{site.flutter-docs}}/development/tools/formatting#using-trailing-commas - ## Built-in types -While both JavaScript and Dart have types, -only Dart is a strongly typed language. -This means that all Dart types have to be -inferrable by the analyzer, explicitly defined, -or assigned as `dynamic`, which disables static type -checking for that identifier. +Both JavaScript and Dart categorize their data into _types_. +Every variable has an associated type. +The type determines the kind of value the variable can store and +what operations can be performed on these values. +Dart differs from JavaScript in that it assigns a static type to every +variable and to every expression. +In Dart, the static type predicts the runtime type of the values of a +variable, or of the value of an expression. +Dart assigns a static type to every expression and variable. +In Dart, the static type predicts the runtime type of the value of an +expression. This means that Dart apps have sound static typing. -Dart supports nullable and non-nullable versions -of the the following built-in types: +JavaScript provides primitive types `num`, `string`, and `boolean` +and the `null` value as well as *arrays* and a `Map` type. + +Dart supports built-in types include: * Numbers (`num`, `int`, `double`) * Strings (`String`) @@ -108,52 +109,46 @@ of the the following built-in types: * Symbols (`Symbol`) * The value `null` (`Null`) -For more information, check out -[Built-in types][] in the [Dart Language Tour][]. +To learn more, see [Built-in types][] in the [Dart Language Tour][]. -In Dart, all types are reference types, -meaning that all variables refer to an object -(an instance of a class). However, the types -`int`, `double`, `String`, and `bool` -are implemented to be immutable (unchanging) -and are canonicalized, -which means they behave as if they are value types. +All non-`Null` types in Dart are subtypes of Object. +All values are also objects. +Dart doesn't use “primitive types” like JavaScript. +By contrast, Dart normalizes or _canonicalizes_ number, boolean +and `null` values. +This means only one `int` value with the numerical value `1` exists. {{site.alert.note}} - JavaScript has two equality operators, - `==` and `===`. The `==` operator performs - the equality test after doing any necessary - type conversions. The `===` operator doesn't - perform type conversions. Dart doesn't have - an equivalent to `===`. + JavaScript has two equality operators, `==` and `===`. + The `==` operator performs the equality test after doing any necessary + type conversions on or to primitive values. + The `===` operator doesn't perform type conversions. + Dart uses the `identical` function to check if two values are the + same object, and the `==` operator to check whether the objects + consider themselves as equal. {{site.alert.end}} -For more information on these JavaScript operators, -check out [this question on Stack Overflow][]. - [Built-in types]: /guides/language/language-tour#built-in-types [Dart Language Tour]: /guides/language -[this question on Stack Overflow]: https://stackoverflow.com/questions/359494/which-equals-operator-vs-should-be-used-in-javascript-comparisons/359509#359509 -For example, the equals operator `==` and the `identical()` -method are guaranteed to return true for the -same values of these types, as shown in the following code: +For example: +The equals operator `==` and the `identical()` method return `true` +for the same values of number types. Review the example shown in the +following code: {:.include-lang} ```dart -var a = 1; -var b = 1; +var a = 2; +var b = 1 + 1; print(a == b); // Prints true -print(identical(a, b)); // Prints true +print(identical(a, b)); // Prints true; only one "2" object exists ``` -The next section covers basic types. -Other types are covered later in this page, -including collection types (lists, maps) -and types that aid asynchrony (futures, streams). +### Primitive Types -### Numbers +This section covers how Dart represents primitive types from JavaScript. +#### Numbers Dart has three data types for holding numbers: @@ -161,90 +156,102 @@ Dart has three data types for holding numbers: : The equivalent to the generic number type in JavaScript. `int` -: Any numeric value without a decimal point. +: A numeric value without a fractional part. `double` -: Any numeric value, including those with a decimal point. +: Any 64-bit (double-precision) floating point number. -All these types are also classes within the Dart API. +The Dart API includes all these types as classes. Both the `int` and `double` types share `num` as their parent class: num subclasses Object and int and double each subclass num -As number values are technically class instances, -they have the convenience of exposing their own -utility functions. Because of this, a `double` can, -for example, be rounded up as follows: +As Dart considers numbers as objects, numbers can expose their +own utility functions as object methods. +You don't need to use an additional object to apply a function to a number. + +For example, to round a `double` to an integer: -{:.include-lang} -```dart -var rounded = 2.5.round(); -``` {:.include-lang} ```js let rounded = Math.round(2.5); ``` -### Strings -Strings in Dart work similarly to strings in JavaScript. -String literals are defined using single quotation marks (`'`), -but can also be defined using double quotation marks -to enable use of single quotation marks within the string -without escaping. However, single quotes are preferred. +{:.include-lang} +```dart +var rounded = 2.5.round(); +``` + + + +#### Strings + +Strings in Dart work like strings in JavaScript. +To write a string literal, enclose it in single (`'`) or double (`"`) +quotation marks. +The majority of Dart developers use single quotes, +but the language enforces no standard. +Use double quotation marks if you don't want to escape +single quotes within the string. {:.include-lang} ```dart var a = 'This is a string.'; ``` -#### Escaping special characters +##### Escaping special characters -Escaping special characters in Dart is similar -to JavaScript (and most other languages). -To include special characters escape them -using the backslash character. +To include a character with another meaning in a string, +like a `$` used for string interpolation, you must escape that character. +Escaping special characters in Dart works like JavaScript +and most other languages. +To escape special characters, +precede that character with the backslash character (`\`). -The following code shows some examples. +The following code shows some examples. {:.include-lang} ```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 ``` -Note that 4-digit hexadecimal values can also be used directly -(for example, `\u2665`), however, curly braces also work. -For more information on working with unicode characters, -check out [Runes and grapheme clusters][]. +{{site.alert.note}} + You can use four-digit hexadecimal characters with or without curly braces. + To learn more about working with unicode characters, + see [Runes and grapheme clusters][]. +{{site.alert.end}} [Runes and grapheme clusters]: /guides/language/language-tour#characters -#### String interpolation +##### String interpolation + +JavaScript supports template literals. +These use backtick (`` ` ``) character delimiters for the following reasons: -JavaScript supports template literals, -which are literals delimited with backtick (`` ` ``) characters, -allowing for multiline strings, -for string interpolation with embedded expressions, -and for special constructs called tagged templates. -Dart supports string concatenation and interpolation -with embedded expressions as part of standard string literals, -meaning that you aren't required to enclose the string in -backticks to enable that functionality. -For more information, -check out [Strings][] in the [Dart Language Tour][]. +* To allow for multiline strings +* To interpolate strings with embedded expressions +* To create special constructs called tagged templates + +In Dart, you don't need to enclose a string in backticks to concatenate +strings or use interpolations within string literals. + +To learn more, see [Strings][] in the [Dart Language Tour][]. [Strings]: /guides/language/language-tour#strings As in JavaScript template literals, -insert expressions into the string literal -using the `${}` syntax. -Dart expands on this by allowing the curly braces -to be omitted when the expression is a single identifier: +you can use the `${}` syntax to insert expressions into +a string literal. +Dart uses this syntax and allows you to omit the curly braces +when the expression uses a single identifier. {:.include-lang} ```dart @@ -257,12 +264,12 @@ var str = 'I eat ${food}'; // I eat bread In JavaScript, you can define multiline strings using template literals. -Dart has two ways to define multiline strings: +Dart has two ways to define multiline strings.
  1. Using implicit string concatenation: - Any neighboring string literals are automatically - concatenated, even when they are spread over multiple lines: + Dart concantenates any neighboring string literals, + even when spread over multiple lines: {:.include-lang} ```dart @@ -274,15 +281,18 @@ final s1 = 'String '
  2. Using a multi line string literal: When using three quotation marks (either single or double) -on either side of the string, -the literal is allowed to span multiple lines: +on either side of the string, the literal can span multiple lines. + {:.include-lang} ```dart -final s2 = '''You can create -multiline strings like this one.'''; +final s2 = ''' +You can create +multiline strings like this one. +'''; -final s3 = """This is also a +final s3 = """ +This is also a multiline string."""; ```
  3. @@ -290,10 +300,9 @@ multiline string."""; #### Equality -As in JavaScript, use the equal-to operator (`==`) -to determine if two strings are equal. -Two strings are equal if they contain the same -sequence of code units. +Dart considers two strings equal when they contain the same sequence +of code units. To determine if two strings have the same sequences, +use the equal-to operator (`==`). {:.include-lang} ```dart @@ -305,88 +314,97 @@ assert(s1 == 'line breaks.'); ``` -### Booleans +#### Booleans -Both Dart and JavaScript booleans represent -a binary value. Each language has two objects -that hold this type: the boolean literals `true` and `false`, -which are compile-time constants in both languages. +Boolean values in both Dart and Javascript express a binary condition. +These two values represent whether a value or expression is +`true` or `false`. +You can return the values using the literals `true` and `false`, +or produced them using expressions like `x < 5` or `y == null`. {:.include-lang} -```dart -var a = true; +```js +let isBananaPeeled = false; ``` + {:.include-lang} -```js -let a = true; +```dart +var isBananaPeeled = false; ``` ## Variables -Variables in Dart are similar to variables in JavaScript, -with the following caveats: +Variables in Dart work like variables in JavaScript, +with two exceptions: -* As Dart is a statically typed language, - when declaring variables the type has to be - inferrable, or you must explicitly define the - type when initializing the variable. +1. Each variable has a type. +1. Dart scopes all variables at the block level, + like `let` and `const` variables in JavaScript. -* All variables in Dart are block scoped, - as you would expect with `let` or `const` in JavaScript. +A Dart variable gets its type in one of two ways: -Declaring and initializing variables in Dart works -almost identically to JavaScript: +1. A type written in the declaration +1. An expression used to initialize the variable -{:.include-lang} -```dart -// Declare a variable -var name; -// Initialize the variable -name = 'bob'; -// Declare and initialize a variable at once -var name = 'bob'; -``` +If you don't provide an initial value or explicit type, +Dart sets the variable's type to the catch-all type `dynamic`. {:.include-lang} ```js // Declare a variable -let name; +let name; // Initialize the variable name = "bob"; // Declare and initialize a variable at once let name = "bob"; ``` -Note that you can replace var in Dart with -an explicit type. However, by convention, -var is recommended when the analyzer can -implicitly infer the type. {:.include-lang} ```dart -String name = 'bob'; // This is the same as 'var', - // since Dart infers the type to be String. +// Declare a variable without a type +// and Dart infers the 'dynamic' type +var name; +// Declare a variable with a specific type +// when you don't provide an initial value +String name; +// Initialize the variable +// and the type remains `dynamic` +name = 'bob'; +// Declare and initialize a variable +// at the same time and Dart infers +// the type +var name = 'bob'; ``` -When a variable without an explicit type -is initialized after its declaration, -its type is inferred as the catch-all `dynamic` type. -Likewise, when a type cannot be automatically inferred, -it defaults back to the `dynamic` type. +In the preceding example, Dart infers the last variable's type as `String` +because you initialized the variable. By convention, use `var` when the +analyzer can infer the type. -Unlike JavaScript, a Dart variable's type can't be changed -after initialization: +{:.include-lang} +```dart +var name = 'bob'; +// Dart understands the variable's type to be 'String'. +// Dart can infer that type from the variable's +// initial value. +``` + +As noted before, a Dart associates a type with each variable. +Variables can only accept values of that type. +Dart variables that use the `dynamic` type +can be assigned any value, like JavaScript variables. {:.include-lang} ```dart // Variable initialized later, `name` has type `dynamic`. -var name; +var name; name = 'bob'; name = 5; // Allowed, as `name` has type `dynamic`. -// Variable initialized immediately, `name` has type `String`. +// Variable initialized on creation, +// `name` has type `String`. var name = 'bob'; name = 5; // Forbidden, as `name` has type `String`. @@ -394,76 +412,89 @@ name = 5; // Forbidden, as `name` has type `String`. ### Final and const -The final modifier in Dart acts like the const -modifier in JavaScript: +Both JavaScript and Dart use variable modifiers. Both use `const`, but +differ in how `const` works. Where JavaScript would use `const`, +Dart uses `final`. -
      -
    1. The variable must be initialized - immediately upon declaration. +When Dart uses `const`, it refers to special values that it creates +when compiling. +When creating these immutable values, Dart uses limited expressions. +These expressions _cannot_ have side effects. +Under these conditions, the compiler can then predict the precise value +of a constant variable or expression, not just its static type. -{{site.alert.note}} - One exception to this is with class fields, - which might be initialized in the class constructor. - Learn more in the [Classes](#classes) section. -{{site.alert.end}} -
    2. +When Dart adds `final` or JavaScript adds `const`, +you must initialize the variable before _reading_ it. +When Dart adds `const`, you must initialize the variable +before _compiling_ it. -
    3. Once the variable has been initialized, - its reference can't be changed later. -
    4. -
    +{:.include-lang} +```dart +final String name; +// Cannot read name here, not initialized. +if (useNickname) { + name = "Bob"; +} else { + name = "Robert"; +} +print(name); // Properly initialized here. +``` -The `const` modifier in Dart acts the same as its -`final` modifier, except that its value must -be known at compile time. Since JavaScript is an -interpreted language requiring no compilation step, -it doesn’t have an equivalent. +After you initialize a `final` variable in Dart or +a `const` variable in JavaScript, you can't change its reference. + +{{site.alert.note}} +When you create an object, the class constructor must initialize the +`final` instance variables. +This ensures that these variables have a value before anyone can read them. -Although you can't modify a `const` object in JS, -you can modify its fields. In comparison, -you can't change a Dart `const` object or its fields: -they're _immutable_ (they can't be changed). +Learn more in the [Classes](#classes) section. +{{site.alert.end}} In Dart, **constant variables must contain constant values**. -However, non-constant variables can still contain -constant values, and values themselves can also be marked `const`. +Non-constant variables can contain constant values that +you can also mark as `const`. {:.include-lang} ```dart -var foo = const []; // foo is not constant, but the value it points to is. +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. + // but its current list value cannot be altered. const baz = []; // Equivalent to `const []` ``` Likewise, classes can have their own `const` constructors -that produce immutable instances. For more information, -check out the [Classes](#classes) section. +that produce immutable instances. + +You can't modify a `const` variable in JavaScript or Dart. +JavaScript does allow you to modify a `const` object's fields, but +Dart does not. + +To learn more, see the [Classes](#classes) section. -## Null safety +## Null safety -Unlike vanilla JavaScript, -Dart supports null safety, making any type non-nullable -by default (as of Dart 2.12). One key benefit of this is -that null reference exceptions are caught when writing code, -so they are unlikely to occur at runtime. +Unlike JavaScript, Dart supports null safety. +As of Dart 2.12, all types default to non-nullable. +This benefits Dart developers because Dart catches null reference +exceptions when writing code, rather than at runtime. ### Nullable vs non-nullable types -For example, all the variables in the following code -are non-nullable: +None of the variables in the following code example can be `null`. {:.include-lang} ```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(); +final b = Foo(); // Foo() invokes a constructor ``` To indicate that a variable might have the value `null`, -just add `?` to its type declaration: +add `?` to its type declaration: {:.include-lang} ```dart @@ -486,13 +517,13 @@ String returnsNonNullable() { } ``` -### Null-aware operators +### Null-aware operators Dart supports several operators to deal with nullability. As in JavaScript, the null assignment operator (`??=`), null coalescing operator (`??`), and optional chaining operator (`?.`), -are all available in Dart and operate the same as in JavaScript. +are all available in Dart and operate the same as in JavaScript. #### ! Operator @@ -541,9 +572,9 @@ whether at the top level, as a class field, or in the local scope. {:.include-lang} -```dart +```js // On the top level -multiply(a, b) { +function multiply(a, b) { return a * b; } @@ -555,19 +586,19 @@ class Multiplier { } // In a local scope -main() { - multiply(a, b) { +function main() { + function multiply(a, b) { return a * b; } - print(multiply(3, 4)); + console.log(multiply(3, 4)); } ``` {:.include-lang} -```js +```dart // On the top level -function multiply(a, b) { +int multiply(a, b) { return a * b; } @@ -579,12 +610,12 @@ class Multiplier { } // In a local scope -function main() { - function multiply(a, b) { +main() { + multiply(a, b) { return a * b; } - console.log(multiply(3, 4)); + print(multiply(3, 4)); } ``` @@ -611,15 +642,13 @@ bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null; ### Parameters -In JavaScript, -all parameters are _optional_ positioned parameters. -In Dart, this is not the case. -By default, all standard parameters are _required_ -positional parameters and must be provided when calling a function. +In JavaScript, all parameters _can_ be positional parameters. +By default, Dart _requires_ functions to provide all parameters +as positional parameters. {:.include-lang} ```dart -multiply(int a, int b) { +int multiply(int a, int b) { return a * b; } @@ -634,17 +663,16 @@ This can change in two situations: 1. The positional parameters are marked as optional. 1. The parameters are named and not marked as required. -Define optional positional parameters by enclosing them -in square brackets following the required positional -parameters, if they exist. You can't define required -parameters after an optional parameter. +To define optional positional parameters, enclose them +in square brackets following any required positional parameters. +You can't follow optional parameters with required parameters. Due to null safety, optional positional parameters must have a default value or be marked as nullable. -Learn more in the preceding section about [null safety](#null-safety). +To learn more, see the preceding section about [null safety](#null-safety). The following code has one valid and two invalid examples -of functions that define optional positional parameters: +of functions that define optional positional parameters. {:.include-lang} ```dart @@ -656,13 +684,14 @@ multiply(int a, [int b = 5, int? c]) { multiply(int a, [int b = 5], int c) { ... } -// Invalid: Neither positional parameter has a default value or has been flagged as nullable. +// Invalid: Neither positional parameter has a default +// value or has been flagged as nullable. multiply(int a, [int b, int c]) { ... } ``` -Here are some examples of calling a function that has optional parameters: +The following example shows how to call a function with optional parameters: {:.include-lang} ```dart @@ -692,20 +721,19 @@ pass values by prefixing the passed value with the name of the parameter, separated by a colon. For example, `f(namedParameter: 5)`. -Again, with null safety, -named parameters that are not flagged as required -either need to have a default value or be flagged as nullable. +Again, with null safety, named parameters that are not flagged as +required either need to have a default value or be flagged as nullable. The following code defines a function with named parameters: {:.include-lang} ```dart -// Valid: +// Valid: // - `a` has been flagged as required -// - `b` has a default value of 5 +// - `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}) { +multiply(bool x, {required int a, int b = 5, int? c}) { ... } ``` @@ -714,7 +742,7 @@ The following examples call a function with named parameters: {:.include-lang} ```dart -// All are valid function calls. +// 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` @@ -723,9 +751,8 @@ multiply(false, c: 9, a: 3, b: 2); // Provide all named parameters out of order ### First-class functions -As in JavaScript, -functions are first-class citizens in Dart, -which means that they're treated as any other object. +JavaScript and Dart treat functions as first-class citizens. +This means that Dart treats functions as any other object. For example, the following code shows how to pass a function as a parameter to another function: @@ -757,81 +784,102 @@ JavaScript has two ways to declare an anonymous function: 1. Use a standard function expression 2. Use arrow syntax -For example: +Likewise, Dart also has two ways to declare anonymous functions. +Both work in a similar manner to the JavaScript arrow expression. +Dart's anonymous functions do not support the extra functionality +that comes with regular function expressions. +For example, JavaScript's support for a function expression acting +like a constructor, or creating a custom binding to this. + +To learn more, see the [Classes](#classes) section. {:.include-lang} ```js -// A regular function expression, assigned to a variable -let funcExpr = function(a, b) { return a * b; } -// The same anonymous function as an arrow function expression, 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. +// 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; ``` -Likewise, Dart also has two ways to declare -anonymous functions, though both are functionally -similar to the arrow expression in JavaScript. -The extra functionality that comes with regular -function expressions (for example, -JavaScript's support for a function expression acting -like a constructor, or creating a custom binding to this), -aren't supported in Dart's anonymous functions. -(For more information, -check out the [Classes](#classes) section). - {:.include-lang} ```dart -// Assigning an anonymous function to a variable. -var blockFunc = (int a, int b) { - var c = a * b; - return c; -} +// Assign an anonymous function +// to a variable. +var blockFunc = + optionalCallback ?? (int a, int b) { + return a * b; +}; -// In the case of a single returned expression, -// you can shorten the syntax: +// For single returned expression, +// you can use the arrow syntax: var singleFunc = (int a, int b) => a * b; ``` -As with JavaScript, anonymous functions -can also be passed to other functions. -This is commonly used (in both languages) -when using the `map` function +As with JavaScript, you can pass anonymous functions to other functions. +Developers often pass anonymous functions when using the `map` function for arrays and lists: {:.include-lang} ```js -[1, 2, 3].map(e => e + 3); // [4, 5, 6] -[1, 2, 3].map(e => { +// 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; -}); // [5, 7, 9] +}); ``` {:.include-lang} ```dart -[1, 2, 3].map((e) => e + 3).toList(); // [4, 5, 6] -[1, 2, 3].map((e) { +// 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(); // [5, 7, 9] +}).toList(); ``` {{site.alert.note}} - The `map` function in these examples returns + The `map` function in the previous examples returns an `Iterable, rather than a `List`. - Therefore, the `toList` function converts the - returned `Iterable` back to a `List`. + The `toList` function converts the returned + `Iterable` back to a `List`. + + A list literal could achieve the same goal. + + {:.include-lang} + ```dart + // These two statements are equivalent: + print([for (var e in [1, 2, 3]) e + 3]); + print([1, 2, 3].map((e) => e + 3).toList()); + ``` {{site.alert.end}} ### Generator functions -Dart supports [_generator functions_] -that return an iterable collection of items -that are lazily built to improve the UI’s performance. -Convert a function to a generator function by adding -the `sync*` keyword after the function parameters, -and modify it to return an `Iterable`. +Both languages support [_generator functions_]. +These functions return an iterable collection of items +computed to avoid unncessary work. + +To write a generator function in Dart, +add the `sync*` keyword after the function parameters, +and return an `Iterable`. Add items to the final iterable using the `yield` keyword, or add whole sets of items using `yield*`. @@ -840,30 +888,67 @@ Add items to the final iterable using the The following example shows how to write a basic generator function: + +{:.include-lang} +```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); +} + +``` + + {:.include-lang} ```dart Iterable naturalsTo(int n) sync* { int k = 0; - while (k < n) { + while (k < n) { yield k++; } } -print(naturalsTo(5)); // Returns an iterable with [0, 1, 2, 3, 4]. +// Returns an iterable with [0, 1, 2, 3, 4] +print(naturalsTo(5).toList()); +``` +{:.include-lang} +```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); +} +``` + +{:.include-lang} +```dart Iterable doubleNaturalsTo(int n) sync* { int k = 0; - while (k < n) { - yield* [k, k]; + while (k < n) { + yield* [k, k]; k++; } } -print(doubleNaturalsTo(3)); // Returns an iterable with [0, 0, 1, 1, 2, 2]. +// Returns an iterable with [0, 0, 1, 1, 2, 2] +print(doubleNaturalsTo(3)); ``` -This is a synchronous generator function, -and is not available in JavaScript. You can also define asynchronous generator functions, which return streams instead of iterables. Learn more in the upcoming [Asynchrony](#asynchrony) section. @@ -873,33 +958,24 @@ Learn more in the upcoming [Asynchrony](#asynchrony) section. This section describes differences in statements between JavaScript and Dart. -### Control flow (if/else, for, while, switch) +### Control flow (if/else, for, while, switch) -Nearly all control statements work similarly -to their JavaScript counterparts, -although some have additional uses when it comes -to collections -(discussed in more detail in the [Collections](#collections) section). +Most control statements work like their JavaScript counterparts. +Some have additional uses for [Collections](#collections). #### Iteration While both JavaScript and Dart have `for-in` loops, -their behavior is different. +their behaviors differ. -JavaScript's `for-in` loop iterates over an object's properties, -so to iterate over an iterable object's elements, -you must instead `for-of` or `Array.forEach()`. -Dart's `for-in` loop does this natively. +JavaScript's `for-in` loop iterates over an object's properties. +To iterate over a JavaScript iterable object's elements, +you must use `for-of` or `Array.forEach()`. +Dart's `for-in` loop works like JavaScripts `for-of`. The following example shows iterating over a collection and printing out each element: -{:.include-lang} -```dart -for (final element in list) { - print(element) -} -``` {:.include-lang} ```js @@ -908,13 +984,20 @@ for (const element of list) { } ``` +{:.include-lang} +```dart +for (final element in list) { + print(element) +} +``` + #### Switch {{site.alert.note}} One key difference with the `switch` statement in JavaScript and Dart: when a case has no `break`, `continue`, or `return` statement, - JS allows execution to fall through and continue + JavaScript allows execution to fall through and continue with the next statement. However, Dart only allows this when a case's body is empty. {{site.alert.end}} @@ -938,30 +1021,28 @@ switch (testEnum) { ### Operators Both Dart and JavaScript contain predefined operators. -Adding new operators is not supported in either language, -but Dart allows you to overload existing operators +Neither language supports adding new operators. +Dart supports overloading some existing operators with the `operator` keyword. For example: {:.include-lang} ```dart -class Box { - Box({ - required this.length, - required this.width, - required this.height, - }); - - final double length; - final double width; - final double height; - - Box operator +(Box x) { - return Box( - length: length + x.length, - width: width + x.width, - height: height + x.height, - ); - } +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, + ); + // maybe even add: + Vector operator *(double scalar) => Vector( + x * scalar, + y * scalar, + z * scalar, + ); } ``` @@ -970,19 +1051,19 @@ class Box { The equality and relational operators of both languages are almost identical, as shown in the following table: -| Meaning | Dart operator | JS equivalent | -|---------------------------------------------------|-----------------|---------------| -| Add | `+` | `+` | -| Subtract | `-` | `-` | -| Unary minus, also known as negation | `-expr` | `-expr` | -| Multiply | `*` | `*` | -| Divide | `/` | `/` | -| Divide returning an integer result | `~/` | | -| Get the remainder of an integer division (modulo) | `%` | `%` | -| `var = var + 1` (expression value is `var + 1`) | `++var` | `++var` | -| `var = var + 1` (expression value is `var`) | `var++` | `var++` | -| `var = var - 1` (expression value is `var - 1`) | `--var` | `--var` | -| `var = var - 1` (expression value is `var`) | `var--` | `var--` | +| Meaning | JavaScript operator | Dart operator | +|---------------------------------------------------|---------------------|---------------| +| Add | `+` | `+` | +| Subtract | `-` | `-` | +| Unary minus, also known as negation | `-expr` | `-expr` | +| Multiply | `*` | `*` | +| Divide | `/` | `/` | +| Divide returning an integer result | | `~/` | +| Get the remainder of an integer division (modulo) | `%` | `%` | +| `x = x + 1` (expression value is `x + 1`) | `++x` | `++x` | +| `x = x + 1` (expression value is `x`) | `x++` | `x++` | +| `x = x - 1` (expression value is `x - 1`) | `--x` | `--x` | +| `x = x - 1` (expression value is `x`) | `x--` | `x--` | {:.table .table-striped} @@ -1030,21 +1111,19 @@ assert(25 == 51.6 ~/ 2); The equality and relational operators of both languages work in the same way: -| Meaning | Dart operator | JS equivalent | -|-------------------------------|---------------|---------------| -| Strict equal | `==` | `===` | -| Abstract equal | | `==` | -| Strict not equal | `!=` | `!==` | -| Abstract not equal | | `!=` | -| Greater than | `>` | `>` | -| Less than | `<` | `<` | -| Greater than or equal to | `>=` | `>=` | -| Less than or equal to | `<=` | `<=` | +| Meaning | JavaScript operator | Dart operator | +|---------------------------|---------------------|------------------| +| Strict equal | `===` | `==` | +| Abstract equal | `==` | `==` | +| Strict not equal | `!==` | `!=` | +| Abstract not equal | `!=` | `!=` | +| Greater than | `>` | `>` | +| Less than | `<` | `<` | +| Greater than or equal to | `>=` | `>=` | +| Less than or equal to | `<=` | `<=` | {:.table .table-striped} -Unlike JavaScript, -Dart doesn’t have the concept of abstract equality, -so the `==` and `!=` JavaScript operators have no equivalent. +The `==` and `!=` JavaScript operators have no equivalent. For example: @@ -1061,25 +1140,22 @@ assert(2 <= 3); #### Type test operators The implementation of test operators is a bit -different between the two languages: +different between the two languages: -| Meaning | Dart operator | JS equivalent | -|----------------------------------------------------|---------------|--------------------| -| Typecast (described below) | `var as T` | | -| True if the object has the specified type | `var is T` | `typeof var === T` | -| True if the object doesn’t have the specified type | `var is! T` | `typeof var !== T` | +| Meaning | JavaScript operator | Dart operator | +|-------------------------------------|---------------------|---------------| +| Typecast | | `x as T` | +| True if object has specified type | `x instanceof T` | `x is T` | +| True if object lacks specified type | `!(x instanceof T)` | `x is! T` | {:.table .table-striped} The result of `obj is T` is true if `obj` implements the interface specified by `T`. For example, `obj is Object?` is always true. -Since JavaScript makes use of type coercion, -it doesn't have an equivalent. - -Use the typecast operator to cast an object -to a particular type if—and only if—you -are sure that the object is of that type. +Use the typecast operator (`as`) to ensure that a value +has a particular type. The compiler can use that, +if you _know_ that the object will have that type. For example: @@ -1088,12 +1164,12 @@ For example: (person as Employee).employeeNumber = 4204583; ``` -If you aren't sure that the object is of type `T`, -then use `is T` to check the type before using the object. +If you don't _know_ that the object is of type `T`, +then use `is T` to check the type _before_ using the object. In Dart, the types of local variables update within the scope of the if statement. -This is not the case for instance variables. +This is not the case for instance variables. {:.include-lang} ```dart @@ -1108,22 +1184,22 @@ You can invert or combine boolean expressions using logical operators. The logical operators of both languages are identical. -|--------------------------------------------------------------------------+---------------+---------------| -| Meaning | Dart operator | JS equivalent | -|--------------------------------------------------------------------------|---------------|---------------| -| Inverts the following expression (changes false to true, and vice versa) | `!var` | `!var` | -| Logical OR | `||` | `||` | -| Logical AND | `&&` | `&&` | +|----------------------------------------------------------------+---------------------|---------------+ +| Meaning | JavaScript operator | Dart operator | +|----------------------------------------------------------------|---------------------|---------------| +| Inverts next expression (changes false to true and vice versa) | `!x` | `!x` | +| Logical OR | `||` | `||` | +| Logical AND | `&&` | `&&` | {:.table .table-striped} -Dart does not have the concept of "truthy" or "falsy" -values—only actual booleans. Because of this, -Logical OR and Logical AND expressions always resolve -to a boolean, not one of the two values like these -operators do in JavaScript. +JavaScript allows any value to be used where you need a Boolean value. +It then converts those values to either `true` or `false`. +JavaScript considers empty strings and the number `0` to be "falsy" values. +Dart allows `bool` values in conditions and as operands of logical operators. For example: +{:.include-lang} ```dart if (!done && (col == 0 || col == 3)) { // ...Do something... @@ -1137,19 +1213,19 @@ by using bitwise and shift operators with integers. The operators of both languages are almost identical, as shown in the following table: -|-------------------------------------------------------+---------------+---------------| -| Meaning | Dart operator | JS equivalent | -|-------------------------------------------------------|---------------|---------------| -| Bitwise AND | `&` | `&` | -| Bitwise OR | `|` | `|` | -| Bitwise XOR | `^` | `^` | -| Unary bitwise complement (0s become 1s; 1s become 0s) | `~expr` | `~expr` | -| Shift left | `<<` | `<<` | -| Shift right | `>>` | `>>` | -| Unsigned shift right | `>>>` | `>>>` | +|-------------------------------------------------------+---------------------+---------------| +| Meaning | JavaScript operator | Dart operator | +|-------------------------------------------------------|---------------------|---------------| +| Bitwise AND | `&` | `&` | +| Bitwise OR | `|` | `|` | +| Bitwise XOR | `^` | `^` | +| Unary bitwise complement (0s become 1s; 1s become 0s) | `~expr` | `~expr` | +| Shift left | `<<` | `<<` | +| Shift right | `>>` | `>>` | +| Unsigned shift right | `>>>` | `>>>` | {:.table .table-striped} -For example: +For example: {:.include-lang} ```dart @@ -1167,69 +1243,94 @@ assert((value >>> 4) == 0x02); // Unsigned shift right assert((-value >>> 4) > 0); // Unsigned shift right ``` -#### Ternary operator +#### Conditional operator + +Both Dart and JavaScript contain a conditional operator (`?:`) +for evaluating expressions. +Some developers refer to this operator as a ternary operator +as it takes three operands. +Dart has another operator (`[]=`) that takes three operands. +To limit ambiguity, refer to `?:` as the conditional operator. +This operator works for expressions like [if-else][] does for statements: -Both Dart and JavaScript contain a (`?:`) -ternary operator for evaluating expressions -that might otherwise require [if-else][] statements: {:.include-lang} -```dart -final visibility = isPublic ? 'public' : 'private'; +```js +let visibility = + isPublic ? "public" : "private"; ``` + {:.include-lang} -```js -let visibility = isPublic ? "public" : "private"; +```dart +final visibility = + isPublic ? 'public' : 'private'; ``` + + [if-else]: /guides/language/language-tour#if-and-else ### Assignment operators -As mentioned previously, -you can assign values using the (`=`) operator: +Assign values using the (`=`) operator: +{:.include-lang} ```dart // Assign value to a a = value; ``` -This operator also has a null-aware variant. -For more information, -check out the [null-assignment](#null-aware-operators) operator section. +This operator also has a null-aware variant (`??=`). -Here are other assignment operators that directly -assign the result of an operation on a variable -back to that same variable: +To learn more, +see the [null-assignment](#null-aware-operators) operator section. -| `=` | `*=` | `%=` | `>>>=` | `^=` | -| `+=` | `/=` | `<<=` | `&=` | `|=` | -| `-=` | `~/=` | `>>=` | | | -{:.table} +JavaScript and Dart include operators that calculate and assign +new values to the variable in the expression. +These assignment operators use the right-side value and +the variable initial value as operands. + +The following table lists these assignment operators: + +| `=` | Assignment | +| `+=` | Addition assignment | +| `-=` | Subtraction assignment | +| `*=` | Multiplication assignment | +| `/=` | Division assignment | +| `~/=` | Truncating division assignment | +| `%=` | Remainder (modulo) assignment | +| `>>>=` | Unsigned right shift assignment | +| `^=` | Bitwise XOR assignment | +| `<<=` | Left shift assignment | +| `>>=` | Right shift assignment | +| `&=` | Bitwise AND assignment | +| `|=` | Bitwise OR assignment | +{:.table} + +JavaScript does not use the `~/=` assignment operator. {:.include-lang} ```dart var a = 5; -a *= 2; // Multiply `a` by 2 and assign the value back to a. +a *= 2; // Multiply `a` by 2 and assign the result back to a. print(a); // `a` is now 10. ``` -### Cascades (`..` operator) +### Cascades (`..` operator) -Unlike JavaScript, -Dart supports cascading with the cascades operator. -This allows you to chain multiple method calls -or property assignments on a single object. +Dart allows you to chain multiple method calls, property assignments, +or both on a single object. Dart refers to this as _cascading_ and +uses the cascade syntax (`..`) to perform this action. + +JavaScript lacks this syntax. -The following example shows setting the value -of multiple properties, then calling multiple methods -on a newly constructed object, all within a single chain -using the cascade operator: +The following example shows chaining multiple methods +on a newly constructed object using the cascade syntax: {:.include-lang} ```dart -var animal = Animal() +var animal = Animal() // Sets multiple properties and methods ..name = "Bob" ..age = 5 ..feed() @@ -1239,17 +1340,26 @@ print(animal.name); // "Bob" print(animal.age); // 5 ``` +To make the first cascade syntax null-aware, write it as `?..`. + +{:.include-lang} +```dart +var result = maybePerson + ?..employment = employer + ..salary = salary; +``` + +Dart ignores the entire cascade if the `maybePerson` value is `null`. + ## Collections -This section covers some collection types -in Dart and how they compare to their equivalents -in JavaScript. +This section covers some collection types in Dart and compare them +to similar types in JavaScript. ### Lists -List literals are defined the same way in Dart as -arrays are defined in JavaScript, -using square brackets and separated by commas: +Dart writes list literals as JavaScript arrays. +It encloses them in square brackets and separate values with commas: {:.include-lang} ```dart @@ -1317,9 +1427,9 @@ fruits.removeLast(); 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); +fruits.removeRange(1, 3); // Removes all elements from the list that match the given predicate. -fruits.removeWhere((fruit) => fruit.contains('p')); +fruits.removeWhere((fruit) => fruit.contains('p')); ``` Use `length` to obtain the number of values in the `List`: @@ -1348,24 +1458,28 @@ assert(fruits.isNotEmpty); #### Filled -One handy feature of Dart's `List` class is the -`filled` constructor; use `filled` to quickly -create a list of size `n` with the specified default value. +Dart's `List` class includes a way to create a List with +each item having the same value. +This `filled` constructor creates a fixed-length list of size `n` with +one default value. The following example create a list of 3 items: {:.include-lang} ```dart -// Creates: [ 'a', 'a', 'a' ] -final list1 = List.filled(3, 'a'); +final list1 = List.filled(3, 'a'); // Creates: [ 'a', 'a', 'a' ] ``` +* You cannot add or remove elements from this list by default. + To permit this list to add or remove elements, add `, growable: true` + to the end of the parameter list. +* You can access and update elements of this list using their index value. + #### Generate -The `List` class also provides a `generate` -constructor to quickly create a list of -size `n` with a default value builder -that creates elements. -The value builder takes the index as a parameter. +The Dart `List` class includes a way to create a List of incrementing values. +This `generate` constructor creates a fixed-length list of size `n` +with a template to build element values. +This template takes the index as a parameter. {:.include-lang} ```dart @@ -1387,13 +1501,13 @@ need hash values to be stored in a `Set`. {{site.alert.note}} In Dart, the hash value defaults to the instance of an object but you can override - it to use a set of properties. For more information, - check out the [`hashCode`][] property page. + it to use a set of properties. To learn more, + see the [`hashCode`][] property page. {{site.alert.end}} [`hashCode`]: {{site.dart-api}}/dart-core/Object/hashCode.html -The following code snippet shows how to initialize a `Set`: +The following code snippet shows how to initialize a `Set`: {:.include-lang} ```dart @@ -1416,7 +1530,7 @@ final names = {}; The following examples provide an overview of the basic actions that you can perform on a Dart `Set`. -Add a value to the `Set` using the `add` method. +Add a value to the `Set` using the `add` method. Use the `addAll` method to add multiple values: {:.include-lang} @@ -1466,26 +1580,26 @@ assert(fruits.isNotEmpty); ### Maps -The `Map` type in Dart is similar to the `Map` type +The `Map` type in Dart resembles the `Map` type in JavaScript. Both types associate keys with values. -A key can be any object type, so long as all keys have -the same type; the same is true for values. +A key can be any object type if all keys have +the same type. This rule applies to values as well. Each key occurs once at most, but you can use the -same value multiple times. +same value multiple times. -In Dart, the dictionary is based on a hash table, -which means that keys need to be hashable. In Dart, -every object contains a unique hash. +Dart bases the dictionary on a hash table. +This means that keys need to be hashable. +Every Dart object contains a hash. {{site.alert.note}} - In Dart, the hash value defaults to an instance - of an object but you can override it to resemble - a data class. For more information, - check out the [`hashCode`][] property page. +In Dart, the hash value of an object defaults to a value +derived from the object's identity, and being compatible +with an equality where the object can only equal itself. +To introduce an equality based on the _contents_ of the object, +override `hashCode` and `operator==`. {{site.alert.end}} -Here are a couple of simple `Map` examples, -created using literals: +Consider these simple `Map` examples, created using literals: {:.include-lang} ```dart @@ -1515,6 +1629,8 @@ final gift = gifts['first']; {{site.alert.note}} The index operator (`[]`) returns a **nullable** value. + The operator returns `null` if the map does not include + the lookup key. {{site.alert.end}} Use the `containsKey` method to check if a key @@ -1611,7 +1727,7 @@ collections like arrays, sets, or dictionaries immutable: * Create a final version of your collection type using the `unmodifiable` constructor (as shown in the following example). - This creates a collection that cannot change its size or content: + This creates a collection that cannot change its size or content: {:.include-lang} ```dart @@ -1650,7 +1766,7 @@ var set2 = {'foo', 'baz', ...set1}; // {foo, baz, bar} ### Collection if/for In Dart, the `for` and `if` keywords have additional -functionality when it comes to collections. +functionality when it comes to collections. A collection `if` statement includes items from a list literal only when the specified condition is met: @@ -1695,15 +1811,28 @@ to their JavaScript counterparts. ### Futures `Future` is Dart's version of a `Promise`: -an asynchronous operation that resolves at a later point. +an asynchronous operation that resolves at a later point. Functions in Dart (or in packages that you use) might return a `Future`, rather than the value they represent -directly, as the value might not be available until later. +directly, as the value might not be available until later. The following example shows that handling a future works in the same way in Dart as a promise works in JavaScript: + +{:.include-lang} +```js +const httpResponseBody = func(); + +httpResponseBody.then(value => { + console.log( + `Promise resolved to a value: ${value}` + ); +}); +``` + + {:.include-lang} ```dart Future httpResponseBody = func(); @@ -1713,42 +1842,45 @@ httpResponseBody.then((String value) { }); ``` -{:.include-lang} -```js -const httpResponseBody = func(); -httpResponseBody.then(value => { - console.log(`Promise resolved to a value: ${value}`); -}); -``` Similarly, futures can fail like promises. Catching errors works the same as well: + {:.include-lang} -```dart +```js httpResponseBody .then(...) - .catchError((err) { - print('Future encountered an error before resolving.'); + .catch(err => { + console.log( + "Promise encountered an error + before resolving." + ); }); ``` + {:.include-lang} -```js +```dart httpResponseBody .then(...) - .catch(err => { - console.log("Promise encountered an error before resolving."); + .catchError((err) { + print( + 'Future encountered an error + before resolving.' + ); }); ``` + + You can also create futures manually. The easiest way to create a `Future` is by defining and calling an `async` function, which is discussed below. However, when you have a value that needs to be a `Future`, -you can convert it as follows: +you can convert it as follows: {:.include-lang} ```dart @@ -1756,7 +1888,7 @@ String str = 'String Value'; Future strFuture = Future.value(str); ``` -#### Async/await +#### Async/Await If you're familiar with promises in JavaScript, you’re likely also familiar with the `async`/`await` syntax. @@ -1769,24 +1901,32 @@ it returns `Future`. The following example shows how to write an `async` function: -{:.include-lang} -```dart -// Returns a future of a string, as the method is async -Future fetchString() async { - // Typically some other async operations would be done here. - return 'String Value'; -} -``` {:.include-lang} ```js -// Returns a Promise of a string, as the method is async +// Returns a Promise of a string, +// as the method is async async fetchString() { - // Typically some other async operations would be done here. + // Typically some other async + // operations would be done here. return "String Value"; } ``` + +{:.include-lang} +```dart +// Returns a future of a string, +// as the method is async +Future fetchString() async { + // Typically some other async + // operations would be done here. + return 'String Value'; +} +``` + + + Call this `async` function as follows: {:.include-lang} @@ -1810,14 +1950,14 @@ The following example shows how to await a future for its value: {:.include-lang} ```dart // We can only await futures within an async context. -asyncFunction() async { +Future asyncFunction() async { var str = await fetchString(); print(str); // 'String Value' } ``` -For more information about `Future`s and the -`async`/`await` syntax, check out the +To learn more about `Future`s and the +`async`/`await` syntax, see the [Asynchronous programming][] codelab. [Asynchronous programming]: /codelabs/async-await @@ -1829,7 +1969,7 @@ While JavaScript has its own concept of streams, Dart's are more akin to `Observable`s, as found in the commonly used `rxjs` library. If you happen to be familiar with this library, -Dart's streams should feel familiar. +Dart's streams should feel familiar. For those not familiar with these concepts: `Stream`s basically act like `Future`s, @@ -1840,8 +1980,8 @@ and it can either complete or reach a fail state. #### Listening To listen to a stream, call its `listen` method -and provide a callback method. This method is -called whenever the stream emits a value: +and provide a callback method. Whenever the stream emits a value, +Dart calls this method: {:.include-lang} ```dart @@ -1851,7 +1991,7 @@ stream.listen((int value) { }); ``` -The `listen` method also has some optional callbacks +The `listen` method includes optional callbacks for handling errors or for when the stream completes: {:.include-lang} @@ -1897,8 +2037,8 @@ Future sumStream(Stream stream) async { When an error occurs when listening to a stream in this way, the error is rethrown at the line -containing the `await` keyword, -which you can handle with a `try-catch` statement: +containing the `await` keyword. +You can handle this error with a `try-catch` statement: {:.include-lang} ```dart @@ -1916,20 +2056,19 @@ you have several different ways to create a stream. The `Stream` class has utility constructors for creating streams from `Future`s or `Iterable`s, or for creating streams that emit values at a timed interval. -For more information, check out the [`Stream`][] API page. +To learn more, see the [`Stream`][] API page. [`Stream`]: {{site.dart-api}}/dart-async/Stream-class.html ##### StreamController -Another common way to create streams is -by using a [`StreamController`][], -a utility class that builds streams. -A `StreamController` contains a `stream` property -that exposes the stream it controls, -and multiple methods for controlling the stream, -such as emitting new items using the `add` method, -or completing the stream using the `close` method. +The utility class [StreamController][] can create and control streams. +Its stream property exposes the stream it controls. +Its methods provides ways to add events to that stream. + +For example, the `add` method can emit new items and +the `close` method completes the stream. + The following example shows basic usage of a stream controller: {:.include-lang} @@ -1956,11 +2095,9 @@ stream.listen((int value) { ##### Async generators -Another way to create streams is by using async generator -functions—these have the same syntax as a -synchronous generator function, but use the `async*` -keyword instead of `sync*`, -and always return a `Stream` instead of an `Iterable`. +Async generator functions can create streams. +These functions resemble a synchronous generator function +but use the `async*` keyword and return a `Stream`. In an async generator function, the `yield` keyword emits the given value to the stream. The `yield*` keyword, @@ -1976,10 +2113,10 @@ Stream asynchronousNaturalsTo(int n) async* { while (k < n) yield k++; } -Stream stream = asynchronousNaturalsTo(5); +Stream stream = asynchronousNaturalsTo(5); // Prints each of 0 1 2 3 4 in succession. -stream.listen((int value) => print(value)); +stream.forEach(print(value)); ``` Learn more about futures, streams, @@ -1988,14 +2125,14 @@ and other asynchronous functionality in the [asynchronous programming]: /tutorials/language/streams -## Classes +## Classes On the surface, classes in Dart are similar to classes in JavaScript, although JavaScript classes are technically more of a wrapper around prototypes. In Dart, classes are a standard feature of the language. This section covers defining and using classes in Dart -and how they differ from JavaScript. +and how they differ from JavaScript. ### “this” context @@ -2018,25 +2155,40 @@ a JavaScript constructor. In Dart, the `constructor` keyword is replaced by the full class name, and all parameters must be explicitly typed. In Dart, the `new` keyword was once required for creating class instances, -but is now optional and its use is no longer recommended. +but is now optional and its use is no longer recommended. {:.include-lang} ```dart class Point { - double x = 0; - double y = 0; + final double x; + final double y; - Point(double x, double y) { - // There's a better way to do this in Dart, stay tuned. - this.x = x; - this.y = 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); ``` +#### Initializer lists + +Use initializer lists to write your constructor. +Insert the initializer list between the constructor's parameters +and body. + +{:.include-lang} +```dart +class Point { + ... + Point.fromJson(Map json) + : x = json['x']!, + y = json['y']! { + print('In Point.fromJson(): ($x, $y)'); + } + ... +} +``` + #### Constructor parameters Writing code to assign class fields in the constructor @@ -2047,8 +2199,8 @@ so Dart has some syntactic sugar, called {:.include-lang} ```dart class Point { - double x = 0; - double y = 0; + double x; + double y; // Syntactic sugar for setting x and y // before the constructor body runs. @@ -2080,26 +2232,6 @@ class Point { } ``` -#### Initializer lists - -You can also use initializer lists, -which run after any fields that aren't set -using initializing parameters, -but run before the constructor body: - -{:.include-lang} -```dart -class Point { - ... - Point.fromJson(Map json) - : x = json['x']!, - y = json['y']! { - print('In Point.fromJson(): ($x, $y)'); - } - ... -} -``` - #### Named constructors Unlike JavaScript, Dart allows classes to have @@ -2127,10 +2259,8 @@ class Point { #### Const constructors -When your class instances are always immutable, -you can enforce this by using a `const` constructor. -Defining your constructor as `const` requires all -non-static fields in your class to be flagged as `final`: +To enable immutable class instances, use a `const` constructor. +A class with a `const` constructor can have `final` instance variables only. {:.include-lang} ```dart @@ -2143,9 +2273,8 @@ class ImmutablePoint { #### Constructor redirection -You can call constructors from other constructors, -for example to prevent code duplication or -to add additional defaults for parameters: +You can call constructors from other constructors to prevent code +duplication or to add additional defaults for parameters: {:.include-lang} ```dart @@ -2164,21 +2293,21 @@ class Point { You can use a factory constructor when you don't need to create a new class instance. -One example would be when returning a cached instance: +One example would be when returning a cached instance: {:.include-lang} ```dart class Logger { static final Map _cache = {}; - + 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, () => Logger._internal(name)); + name, () => _cache[name] ??= Logger._internal(name); } // Private constructor for internal use only @@ -2188,8 +2317,22 @@ class Logger { ### Methods -In both Dart and JavaScript, methods are -functions that provide behavior for an object. +In both Dart and JavaScript, methods serve as functions that provide +behavior for an object. + +{:.include-lang} +```js +function doSomething() { // This is a function + // Implementation.. +} + +class Example { + doSomething() { // This is a method + // Implementation.. + } +} +``` + {:.include-lang} ```dart @@ -2204,37 +2347,26 @@ class Example { } ``` -{:.include-lang} -```js -doSomething() { // This is a function - // Implementation.. -} -class Example { - doSomething() { // This is a method - // Implementation.. - } -} -``` ### Extending classes Dart allows classes to extend another class, -in the same way that JavaScript does. +in the same way that JavaScript does. {:.include-lang} ```dart class Animal { int eyes; - + Animal(this.eyes); - + makeNoise() { print('???'); } } class Cat extends Animal { - + Cat(): super(2); @override @@ -2252,7 +2384,7 @@ use the `@override` annotation. While this annotation is optional, it shows that the override is intentional. The Dart analyzer shows a warning if the method -is not actually overriding a superclass method. +is not actually overriding a superclass method. The parent method that is being overridden can still be called using the `super` keyword: @@ -2315,7 +2447,8 @@ class Cat implements Consumer { @override consume() { print('Eating mice...'); - super.consume(); // Invalid, because there’s no superclass. + super.consume(); + // Invalid. The superclass `Object` has no `consume` method. } } ``` @@ -2356,27 +2489,68 @@ consumer = Cat(); consumer.consume(); // Eating mice... ``` -### Mixins +### Mixins Mixins are used to share functionality between classes. You can use the mixin's fields and methods in the class, using their functionality as if it were part of the class. -A class can use multiple mixins, which is useful -when multiple classes share the same functionality, +A class can use multiple mixins. This helps when multiple classes share the +same functionality, without needing to inherit from each other or share a common ancestor. -A mixin is declared like a regular class, -as long as it doesn't extend any class other -than `Object` and has no constructors. -Use the `with` keyword to add one or more -comma-separated mixins to a class. -Although JavaScript doesn’t have an equivalent -for this keyword, the effect is similar to using -`Object.assign` to merge additional objects into -an existing object, after instantiating. +Use the `with` keyword to add one or more comma-separated mixins to a class. + +JavaScript has no keyword equivalent. JavaScript can use `Object.assign` +to merge additional objects into an existing object, after instantiating. + +The following examples show how JavaScript and Dart achieve similar behavior: + + +{:.include-lang} +```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 +``` -The following example shows how similar behavior -is replicated in JavaScript and how it's achieved in Dart: {:.include-lang} ```dart @@ -2387,9 +2561,9 @@ class Flyer { fly() => print('Flaps wings'); } class Walker { - walk() => print('Walks legs'); + walk() => print('Walks on legs'); } - + class Bat extends Animal with Flyer {} class Goose extends Animal with Flyer, Walker {} class Dog extends Animal with Walker {} @@ -2397,43 +2571,14 @@ class Dog extends Animal with Walker {} // Correct calls Bat().fly(); Goose().fly(); -Goose().walk(); +Goose().walk(); Dog().walk(); // Incorrect calls Bat().walk(); // Not using the Walker mixin Dog().fly(); // Not using the Flyer mixin ``` -{:.include-lang} -```js -class Animal {} -// Defining the mixins -class Flyer { - fly = () => console.log('Flaps wings'); -} -class Walker { - walk = () => console.log('Walks 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` does not have the `walk` method -dog.fly(); // `dog` does not have the `fly` method -``` Alternatively, you can replace the `class` keyword with `mixin` to prevent the mixin from being used @@ -2453,7 +2598,7 @@ they can have overlapping methods or fields with each other when used on the same class. They can even overlap with the class that uses them, or that class's superclass. -The order in which they are added to a class matters. +The order in which they are added to a class matters. To give an example: @@ -2478,7 +2623,7 @@ However, sometimes it's useful to extend a class that already exists or is part of another library or the Dart SDK. In these cases, Dart offers the ability to write extensions -for existing classes. +for existing classes. As an example, the following extension on the `String` class from the Dart SDK allows parsing of integers: @@ -2509,6 +2654,7 @@ var age = '42'.parseInt(); // Use the extension method. Getters and setters in Dart work exactly like their JavaScript counterparts: + {:.include-lang} ```js class Person { @@ -2520,7 +2666,9 @@ class Person { set age(value) { if (value < 0) { - throw new Error('age cannot be negative'); + throw new Error( + 'age cannot be negative' + ); } this._age = value; } @@ -2531,18 +2679,21 @@ person.age = 10; console.log(person.age); ``` + {:.include-lang} ```dart class Person { int _age = 0; - + int get age { return _age; } - + set age(int value) { if (value < 0) { - throw ArgumentError('Age cannot be negative'); + throw ArgumentError( + 'Age cannot be negative' + ); } _age = value; } @@ -2555,26 +2706,25 @@ void main() { } ``` + + ### Public and private members Like JavaScript, Dart has no access modifier keywords: -all class members are public by default. +all class members are public by default. -While private class members are not yet officially -part of JavaScript because they are not part of any -published EcmaScript standard, -a proposal for this has been completed and is ready -to be included in the next publication of the standard. +JavaScript will include private class members in the next +practical revision of the EcmaScript standard. As such, implementations for this have been available in -various browsers and runtimes for a while already. +various browsers and runtimes for some time. -In JavaScript, you can indicate that a class member is private -by adding a pound symbol (`#`) as a prefix to its name: +To make a class member private in JavaScript, +prefix its name with a pound (or hash) symbol (`#`). {:.include-lang} ```js class Animal { - eyes; // Public field + eyes; // Public field #paws; // Private field #printEyes() { // Private method @@ -2587,14 +2737,13 @@ class Animal { } ``` -Similarly, Dart allows developers to make a -class member private by prefixing its name -with an underscore (`_`): +To make a class member private in Dart, prefix its name +with an underscore (`_`). {:.include-lang} ```dart class Animal { - int eyes; // Public field + int eyes; // Public field int _paws; // Private field void _printEyes() { // Private method @@ -2607,113 +2756,87 @@ class Animal { } ``` -In JavaScript, this is a convention, -but in Dart this is a full language feature -enforced by the compiler. +JavaScript uses the hash as a convention. +Dart's compiler enforces use of the underscore for this feature. -A difference that should be noted is that in Dart, -private members are not private to the class, -but are private to the library, -meaning that private members can still be accessed -from code that is considered part of the same library. -By default, this is possible anywhere in the same file, -so private class members can still be accessed from code -in the same file. While you can expand this library scope -beyond a single file using the `part` directive, -it's generally advised to [avoid doing so][] and is usually -reserved for code generators. +Dart makes private members private to the library, not the class. +This means that you can access private members from code in the same library. +By default, Dart limits access to private class members to code in the same file. +To expand the scope of a library beyond one file, add the `part` directive. +When possible, [avoid using `part`][]. Reserve using `part` for code generators. -[avoid doing so]: /guides/libraries/create-library-packages#organizing-a-library-package +[avoid using `part`]: /guides/libraries/create-library-packages#organizing-a-library-package ### Late variables -Assign the `late` keyword to class fields to -indicate they are initialized at a later point, -while remaining non-nullable. This is useful -for cases where a variable is never observed -before being initialized, allowing it to be initialized later. -This has several advantages over just labeling the field as nullable: +To indicate that Dart initializes class fields at a later point, +assign the `late` keyword to those class fields. +Those class fields remain non-nullable. +Do this when a variable cannot be observed before being initialized. +This keyword allows the variable to be initialized later. +This differs from labeling the field as nullable. * (Non-nullable) late fields cannot have null assigned at a later point. * (Non-nullable) late fields throw a runtime error when - accessed before they are initialized. + accessed before they initialize. This should be avoided. {:.include-lang} ```dart -// Using null safety: -class Coffee { - late String _temperature; - - void heat() { _temperature = 'hot'; } - void chill() { _temperature = 'iced'; } - - String serve() => _temperature + ' coffee'; +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); } ``` -In this case, `_temperature` is only initialized -after calling `heat()` or `chill()`. -If `serve()` is called before either are called, -a runtime exception occurs. -The `_temperature` field can never be assigned `null`. - -You can use the `late` keyword to make initialization _lazy_, -when combined with an initializer: +Use `late` for local variables only if unclear code results +in the compiler being unable determine if the code initialized the variable. {:.include-lang} ```dart -class Weather { - late int _temperature = _readThermometer(); +doSomething(int n, bool capture) { + late List captures; + if (capture) captures = []; + for (var i = 0; i < n; i++) { + var foo = something(i); + if (capture) captures.add(foo); + } } ``` -In this example, `_readThermometer()` only runs -when the field is first accessed, -rather than on initialization. - -Lastly, use the `late` keyword to delay initialization -of `final` variables. While you don't need to immediately -initialize the final variable when marking it as `late`, -it still allows the variable to be initialized only once. -A second assignment results in a runtime error. - -{:.include-lang} -```dart -late final int a; -a = 1; -a = 2; // Throws a runtime exception `a` is already initialized. -``` +In the preceding example, the compiler does not know to assign +`captures` if `capture` is true. Using `late` delays the normal +"assigned" checks until runtime. -## Generics +## Generics -While Vanilla JavaScript doesn’t offer generics, -they are available in Dart to improve type safety -and reduce code duplication. +While JavaScript doesn’t offer generics, +Dart does to improve type safety and reduce code duplication. -### Generic methods +### Generic methods You can apply generics to methods. -To define a generic type, place it between `< >` -symbols after the method name. -This type can then be used within the method -(as the return type), or within the method’s parameters: +To define a generic type parameter, place it between angle brackets `< >` +after the method name. +You can then use this type within the method +as the return type or within the method’s parameters: {:.include-lang} ```dart -// Defining a method that uses generics. -T transform(T param) { - // E.g. doing some transformation on `param`... - return param; -} -// Calling the method. Variable `str` is of type String. -var str = transform('string value'); +Map _cache = {}; +T cache(T value) => (_cache[value] ??= value) as T; ``` -In this case, passing `String` to the `transform` method -ensures that it returns a `String`. Likewise, -if an `int` is provided, the return value is an `int`. - Define multiple generic types by separating them with a comma: {:.include-lang} @@ -2731,11 +2854,10 @@ transform(5, 'string value'); ### Generic classes Generics can also be applied to classes. -You can include the type to use when calling a constructor, -which allows you to tailor reusable classes to specific types. +You can include the type to use when calling a constructor. +This allows you to tailor reusable classes to specific types. -In the following example, the `Cache` class is for -caching specific types: +In the following example, the `Cache` class caches specific types: {:.include-lang} ```dart @@ -2749,8 +2871,8 @@ stringCache.setByKey('Foo', 'Bar'); // Valid, setting a string value. stringCache.setByKey('Baz', 5); // Invalid, int type does not match generic. ``` -If the type declaration is omitted, -the runtime type will be `Cache` +If you omit the type declaration, +the runtime type becomes `Cache` and both calls to `setByKey` are valid. ### Restricting generics @@ -2766,20 +2888,19 @@ class NumberManager { ... } // Valid. -var manager = NumberManager(); -var manager = NumberManager(); +var manager = NumberManager(); +var manager = NumberManager(); // Invalid, String nor its parent classes extend num. -var manager = NumberManager(); +var manager = NumberManager(); ``` ### Generics in literals -`Map`, `Set`, and `List` literals can explicitly -declare generic types, which is useful when the -type isn’t inferred or is incorrectly inferred. +`Map`, `Set`, and `List` literals can accept type arguments. +This helps when Dart cannot infer the type or infer the type correctly. For example, the `List` class has a generic definition: -`class List`. Generic type `E` refers to the type of +`class List`. The type parameter `E` refers to the type of the list’s contents. Normally, this type is automatically inferred, which is used in some `List` class’s member types. (For example, its first getter returns a value of type `E`.) @@ -2842,13 +2963,13 @@ int get length => ... This guide has introduced you to the major differences between Dart and JavaScript. At this point, -you might consider moving to the general documentation -for Dart or [Flutter]({{site.flutter}}) -(an open-source framework that -uses Dart for building beautiful, natively compiled, -multi-platform applications from a single codebase), -where you'll find in-depth information about the -language and practical ways of getting started. +consider reading the Dart documentation. +You could also read the [Flutter]({{site.flutter}}) docs. +Built with Dart, Flutter is an open-source framework that +uses Dart for building natively compiled, +multi-platform applications from a single codebase. +These docs provide in-depth information about the +language and practical ways of getting started. Some possible next steps: