30分鐘學會Dart語言

Dart語言是google開發的語言,目前對外主要用於flutter開發,Google內部使用Dart開發大型應用。Dart語言還是很受歡迎且很有發展潛力。它是一個純面向對象的語言,其語法比較靈活,也比較簡單。Dart語法跟java相似度比較高,但是又有很多不同。

Dart官方教程:http://dart.goodev.org/guides/language/language-tour
https://dart.dev/guides/language/language-tour

搭建Dart開發環境

安裝Dart SDK:https://dart.dev/get-dart
安裝IDE:推薦使用Visual Studio Code: https://code.visualstudio.com/

一、數據類型

  • int (一般佔8個字節)
  • double (一般佔8個字節)
  • num(int和double的父類型,一般佔8個字節)
  • String
  • bool
  • List
  • Map

用法示例:

var year = 1977;  //定義int類型,注意dart沒有long類型。

int year2=1977; //同上,dart可以顯式聲明類型也可以自動推導類型。

var antennaDiameter = 3.7;//定義double類型,注意dart沒有float類型。

var name = 'Voyager I'; ////定義String類型

var flybyObjects = List();var flybyObjects = new List();

var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];

var image=new Map();

var image = {
  'tags': ['saturn'],
  'url': '//path/to/saturn.jpg'
};

 var map={"a":"1","b":2,"c":3,4:5};
 
 print(map[4]);//打印key=4的元素
  
 map["f"]="aaa"; //添加元素“f”="aaa"
  
 print(map["f"]);//打印key=f的元素

 var list=new List();
  
 list.add("a");//添加元素:"a"
  
 list.add(1);//添加元素:1
  
 print(list); //打印所有元素

這裏沒有顯式的指定變量類型。也可以指定變量類型。


int year=1977; 

double antennaDiameter = 3.7;

String name = 'Voyager I'; 

List flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];

Map image = {
  'tags': ['saturn'],
  'url': '//path/to/saturn.jpg'
};

dart語法跟js和java比較想像,跟js的不同是,js只有隱式類型聲明,沒有顯式類型類型聲明。跟java不同的是,java必須顯示聲明類型。

例如js聲明變量:var year = 1977; js沒有這種聲明方式:int year = 1977;

dart與python變量聲明的不同是,python連var關鍵字都不需要,即year=1997,並且語句後面不需要分號結束。

跟java的不同:

1、dart聲明沒有初始化的變量都會被賦予默認值 null,包括int和double、bool。
2、Dart 沒有 public、private、protected 這些關鍵字,變量名以"_"開頭意味着對它的 lib 是私有的。

查看javascript數據類型
查看python數據類型

二、流程控制語句

跟js和java幾乎一樣。

if (year >= 2001) {
  print('21st century');
} else if (year >= 1901) {
  print('20th century');
}

for (var object in flybyObjects) {
  print(object);
}

for (int month = 1; month <= 12; month++) {
  print(month);
}

while (year < 2016) {
  year += 1;
}

三、普通函數(方法)

計算菲波拉契數列第n項:

int fibonacci(int n) {
  if (n == 0 || n == 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

var result = fibonacci(20);

可以看出定義函數部分跟java比較相似,而js需要添加function關鍵字修飾。

js聲明函數:

function functionname()
{
    // 執行代碼
}

Dart 是一個真正的面嚮對象語言,方法也是對象並且具有一種 類型。 這意味着,方法可以賦值給變量,也可以當做其他方法的參數。 也可以把 Dart 類的實例當做方法來調用。

下面是定義方法的實例

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

也可以忽略類型定義,自動推導類型。沒有返回值時爲void類型,void關鍵字可省。

isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

對於只有一個表達式的方法,你可以選擇 使用縮寫語法來定義。

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

這個 => expr 語法是 { return expr; } 形式的縮寫。=> 形式 有時候也稱之爲 胖箭頭 語法。

注意: 在箭頭 (=>) 和冒號; 之間只能使用一個 表達式 – 不能使用 語句。 例如:你不能使用 if statement,但是可以 使用條件表達式 conditional expression。

方法可以有兩種類型的參數:必需的和可選的。 必需的參數在參數列表前面, 後面是可選參數。

(1)可選參數

可選參數就是調用方法時實參是可選的,調用時可傳可不傳。可選參數分爲兩種,一種是是命名參數,一種是位置參數。

命名參數就是一對花括號{}括起來的參數,調用時實參必須指定參數的名字,必須以“name:value”這種形式傳遞。非命名參數就不能使用name:value形式傳遞。

位置參數就是中括號[]括起來的參數,裏面的參數可傳可不傳,但是傳的時候必須按照位置(順序)來傳。

但是這兩種參數不能同時當做可選參數,也就是一個方法不能同時有位置參數和命名參數。

沒有中括號或花括號的參數列表,就必須按參數位置(順序)傳遞並且是一個都不能少地傳遞。

Dart爲什麼定義這種可選參數呢?因爲Dart跟Java不同的是,Java可以使用多個同名的函數不同的參數來實現方法重載,而Dart就不允許一個類中有同名的函數,因此可選參數就類似於Java中的方法重載,最終的目的都是同一個方法,實現多種傳參。相對Java而言,Dart這種可選參數靈活度更高。

(1.1)命名參數

調用方法的時候,你必須使用這種形式 paramName: value 來指定命名參數。例如:

enableFlags("red",bold: true, hidden: false);

enableFlags("red"); //bold是null,hidden是null

在定義方法的時候,使用{param1, param2, …}的形式來指定命名參數:

/// Sets the [bold] and [hidden] flags to the values
/// you specify.
enableFlags(String color,{bool bold, bool hidden}) {
  // ...
}
(1.2)位置參數

把一些方法的參數放到 [] 中就變成可選 位置參數了:

String say(String from, String msg, [String device,String day]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device on $day';
  }
  return result;
}

下面是不使用可選參數調用上面方法 的示例

 assert(say('Bob', 'Howdy') == 'Bob says Howdy');

下面是使用可選參數調用上面方法的示例:

assert(say('Bob', 'Howdy', 'smoke signal',"2019/10/1") == 'Bob says Howdy with a smoke signal on 2019/10/1');
(1.3)可選參數默認值

在定義方法的可選參數時,可以使用 = 來指定可選參數的默認值。 默認值只能是編譯時常量。 如果沒有提供默認值,則默認值爲 null。

下面是設置命名參數默認值的示例:

/// Sets the [bold] and [hidden] flags to the values you
/// specify, defaulting to false.
void enableFlags({bool bold = false, bool hidden = false}) {
  // ...
}

// bold will be true; hidden will be false.
enableFlags(bold: true);

下面的示例顯示瞭如何設置位置參數的默認值:

String say(String from, String msg,[String device = 'carrier pigeon', String mood]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  if (mood != null) {
    result = '$result (in a $mood mood)';
  }
  return result;
}

assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');

還可以使用 list 或者 map 作爲默認值。 下面的示例定義了一個方法 doStuff(), 並分別爲 list 和 gifts 參數指定了 默認值。

void doStuff(
    {List<int> list = const [1, 2, 3],
    Map<String, String> gifts = const {
      'first': 'paper',
      'second': 'cotton',
      'third': 'leather'
    }}) {
  print('list:  $list');
  print('gifts: $gifts');
}
(2)入口函數

每個應用都需要有個頂級的 main() 入口方法才能執行。 main() 方法的返回值爲 void 並且有個可選的 List 參數。

下面是一個 web 應用的 main() 方法:

void main() {
  querySelector("#sample_text_id")
    ..text = "Click me!"
    ..onClick.listen(reverseText);
}

注意: 前面代碼中的 … 語法爲 級聯調用(cascade)。 使用級聯調用語法, 你可以在一個對象上執行多個操作。

下面是一個命令行應用的 main() 方法,並且使用了 方法參數作爲輸入參數:

// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
  print(arguments);

  assert(arguments.length == 2);
  assert(int.parse(arguments[0]) == 1);
  assert(arguments[1] == 'test');
}
(3)方法作爲參數調用另一個方法

可以把方法當做參數調用另外一個方法。例如:

printElement(element) {
  print(element);
}

var list = [1, 2, 3];

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

方法也可以賦值給一個變量

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');

上面的方法爲 下面即將介紹的匿名方法。

(4)匿名方法

大部分方法都帶有名字,例如 main() 或者 printElement()。 你有可以創建沒有名字的方法,稱之爲 匿名方法,有時候也被稱爲 lambda 或者 closure 閉包。 你可以把匿名方法賦值給一個變量, 然後你可以使用這個方法,比如添加到集合或者從集合中刪除。匿名方法的使用場景很多,例如List的一些方法常常需要傳一個匿名方法作爲參數。

匿名函數和命名函數看起來類似— 在括號之間可以定義一些參數,參數使用逗號 分割,也可以是可選參數。 後面大括號中的代碼爲函數體:

([[Type] param1[,]]) { 
  codeBlock; 
}; 

下面的代碼定義了一個參數爲i (該參數沒有指定類型)的匿名函數。 list 中的每個元素都會調用這個函數來 打印出來,同時來計算了每個元素在 list 中的索引位置。

var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
list.forEach((i) {
  print(list.indexOf(i).toString() + ': ' + i);
});

運行結果:

0: apples
1: oranges
2: grapes
3: bananas
4: plums
(5)靜態作用域

Dart 是靜態作用域語言,變量的作用域在寫代碼的時候就確定過了。 基本上大括號裏面定義的變量就 只能在大括號裏面訪問,和 Java 作用域 類似。

下面是作用域的一個 示例:

var topLevel = true;

main() {
  var insideMain = true;

  myFunction() {
    var insideFunction = true;

    nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

注意 nestedFunction() 可以訪問所有的變量, 包含頂級變量。

(6)詞法閉包

一個閉包是一個方法對象,不管該對象在何處被調用, 該對象都可以訪問其作用域內的變量。

方法可以封閉定義到其作用域內的變量。 下面的示例中,makeAdder() 捕獲到了變量 addBy。 不管你在哪裏執行 makeAdder() 所返回的函數,都可以使用 addBy 參數。

/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

main() {
  // Create a function that adds 2.
  var add2 = makeAdder(2);

  // Create a function that adds 4.
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}
(7)測試函數是否相等

下面是測試頂級方法、靜態函數和實例函數 相等的示例:

foo() {}               // A top-level function

class A {
  static void bar() {} // A static method
  void baz() {}        // An instance method
}

main() {
  var x;

  // Comparing top-level functions.
  x = foo;
  assert(foo == x);

  // Comparing static methods.
  x = A.bar;
  assert(A.bar == x);

  // Comparing instance methods.
  var v = new A(); // Instance #1 of A
  var w = new A(); // Instance #2 of A
  var y = w;
  x = w.baz;

  // These closures refer to the same instance (#2),
  // so they're equal.
  assert(y.baz == x);

  // These closures refer to different instances,
  // so they're unequal.
  assert(v.baz != w.baz);
}
(8)返回值

所有的函數都返回一個值。如果沒有指定返回值,則 默認把語句 return null; 作爲函數的最後一個語句執行。

四、類定義

class Spacecraft {
  String name;
  DateTime launchDate;
  int launchYear;

  // Constructor, including syntactic sugar for assignment to members.
  Spacecraft(this.name, this.launchDate) {
    // Pretend the following is something you'd actually want to run in
    // a constructor.
    launchYear = launchDate?.year;
  }

  // Named constructor that forwards to the default one.
  Spacecraft.unlaunched(String name) : this(name, null);

  // Method.
  void describe() {
    print('Spacecraft: $name');
    if (launchDate != null) {
      int years = new DateTime.now().difference(launchDate).inDays ~/ 365;
      print('Launched: $launchYear ($years years ago)');
    } else {
      print('Unlaunched');
    }
  }
}

可以這樣使用上面定義的類:

var voyager = new Spacecraft('Voyager I', new DateTime(1977, 9, 5));
voyager.describe();

var voyager3 = new Spacecraft.unlaunched('Voyager III');
voyager3.describe();

五、構造函數

(1)、默認構造函數

默認構造函數即不需要額外定義的構造函數

main() {
 Person person= new Person();
}
class Person {
  String name;
  int age;
}

也可以顯示指定構造函數

class Person {
  String name;
  int age;

  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
}

上面這個構造函數可以簡單寫成下面這樣。

class Person {
  String name;
  int age;
  Person(this.name, this.age);
}

dart不允許有同名的構造函數,普通函數之間也不能同名。例如下面的代碼會編譯不通過。

class Person {
  String name;
  int age;

  Person(){
   
  }
  Person(String name,int age){

  }
}

那麼如何實現構造函數重載呢?請往下看。

(2)、命名構造函數
main() {
  Person person1 = new Person("kitty",10);
  Person person2 = new Person.onlyName("Michale");
  Person person3 = new Person.onlyAge(12);
  print("person1: name=${person1.name},age=${person1.age}");
  print("person2: name=${person2.name},age=${person2.age}");
  print("person3: name=${person3.name},age=${person3.age}");
}

class Person {
  String name;
  int age;
  Person(this.name, this.age);
  Person.onlyName(this.name);
  Person.onlyAge(this.age);
}

上面Person.onlyName和Person.onlyAge就是命名構造函數。相當於是構造函數重載。當然還可以使用命名參數和位置參數實現構造函數重載。

運行結果:

person1: name=kitty,age=10
person2: name=Michale,age=null
person3: name=null,age=12
(3)、重定向構造函數

重定向構造函數,就是將函數的實現委託給另一個構造函數(不是父類的構造函數),並且自身不允許有實現。

main() {
  Person person1 = new Person("kitty",10);
  Person person2 = new Person.onlyName("Michale");
  print("person1: name=${person1.name},age=${person1.age}");
  print("person2: name=${person2.name},age=${person2.age}");
}

class Person {
  String name;
  int age;
  Person(this.name, this.age);
  Person.onlyName(String _name):this(_name,12); //不允許有函數體,否則編譯不通過。
}

運行結果:

person1: name=kitty,age=10
person2: name=Michale,age=12

跟java一樣,dart語言中,構造函數也是不能被子類繼承。
如果父類沒有無參構造函數,那麼子類必須顯式聲明一個構造函數,並且要顯式調用父類的構造函數。
這一點跟java是一樣的,語法上不同的是,子類只需要在函數簽名後加上":super(…)"。

main() {
  Employee employee1 = new Employee("kitty",10,8000);
  print("employee1: name=${employee1.name},age=${employee1.age},salary=${employee1.salary}");
}

class Person {
  String name;
  int age;
  
  Person(this.name, this.age) {
    print("super construted");
  }

}

class Employee extends Person {
  double salary;
  Employee(String name, int age, this.salary) : super(name, age) {
    print("child construted");
  }
}

運行結果:

super construted
child construted
employee1: name=kitty,age=10,salary=8000.0

下面代碼跟上面代碼等同。

class Person {
  String name;
  int age;
  Person(this.name, this.age);
}

class Employee extends Person {
  double salary;
  Employee(String name, int age, this.salary) : super(name, age);
}
(4)初始化列表

除了調用超類構造函數外,您還可以在構造函數主體運行之前初始化實例變量。用逗號分隔初始化代碼。

// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}

在開發過程中,您可以通過assert在初始化列表中使用來驗證輸入。

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}

初始化final字段時,初始化列表很方便。下面的示例在初始化列表中初始化三個final字段。

import 'dart:math';

class Point {
  final num x;
  final num y;
  final num distanceFromOrigin;

  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

main() {
  var p = new Point(2, 3);
  print(p.distanceFromOrigin);
}
(5)常量構造函數

如果您的類產生了永不改變的對象,則可以使這些對象具有編譯時常量。爲此,請定義一個const構造函數,並確保所有實例變量均爲final。

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

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

flutter中Image.asset源碼示例:

  Image.asset(
    String name, {
    Key key,
    AssetBundle bundle,
    this.frameBuilder,
    this.semanticLabel,
    this.excludeFromSemantics = false,
    double scale,
    this.width,
    this.height,
    this.color,
    this.colorBlendMode,
    this.fit,
    this.alignment = Alignment.center,
    this.repeat = ImageRepeat.noRepeat,
    this.centerSlice,
    this.matchTextDirection = false,
    this.gaplessPlayback = false,
    String package,
    this.filterQuality = FilterQuality.low,
  }) : image = scale != null
         ? ExactAssetImage(name, bundle: bundle, scale: scale, package: package)
         : AssetImage(name, bundle: bundle, package: package),
       loadingBuilder = null,
       assert(alignment != null),
       assert(repeat != null),
       assert(matchTextDirection != null),
       super(key: key);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章