02 - 03 Dart語法精講-基礎語法

zeqinjie發表於2021-08-19

最近和移動小夥伴一起重新學習 Dart ,主要是看 CoderWhy 視訊教程 講得挺不錯的,順便做了個筆記

變數宣告

/***************  宣告變數 ******************/
void defineVariable() {
  // 變數宣告(var/final/const)
  // var 宣告變數
  var age = 20;
  // age = "abc";
  age = 30;
  // final宣告常量
  final height = 1.88;

  // const宣告常量
  const address = "深圳市";

  // final和const的區別
  // const date1 = DateTime.now(); 寫法錯誤
  final date2 = DateTime.now();

  // ? 在Dart 2.0 之後, const可以省略
  const p1 = const Person("hello");
  const p2 = const Person("world");
  const p3 = const Person("zhengzeqin");

  print(identical(p1, p2)); // true
  print(identical(p2, p3)); // false

  // ? 在Dart 2.12 之後, 預設可選型別需要加 ? 以前預設不加
  String? optionName = null;
  optionName = "zhengzeqin";
}
複製程式碼

final 和 const 的區別

  • const 必須賦值常量值(編譯期間需要有一個確定的值)
  • final 可以通過計算/函式獲取一個值(執行期間來確定一個值)

runtimeType

runtimeType : 用於獲取變數當前的型別

var name = 'zhengzeqin';
print(name.runtimeType); // String
複製程式碼

dynamic的使用

類似 swift Any 宣告動態變數

question

dynamic 與 Object 區別?

// 知識點: Object和dynamic的區別
// 父類應用指向子類物件
// Object和dynamic
// Object呼叫方法時, 編譯時會報錯
// dynamic呼叫方法時, 編譯時不報錯, 但是執行時會存在安全隱患

// 錯誤示例
Object obj = "why";
print(obj.substring(1));

// 明確宣告
dynamic obj2 = 123;
print(obj2.substring(1));
複製程式碼

identical

判斷兩個變數型別是否一致 補充:const 修飾的常量物件,建立是同一個物件(共享物件),注意類需要定義 const 建構函式

// 在Dart2.0之後, const可以省略
const p1 = const Person("hello");
const p2 = const Person("world");
const p3 = const Person("zhengzeqin");

print(identical(p1, p2)); // true
print(identical(p2, p3)); // false

class Person {
  final String name;
  const Person(this.name);
}
複製程式碼

資料型別

數字型別

int double

字串

/***************  字串 ******************/
void stringFunction() {
  // 1.定義字串的方式
  var s1 = 'Hello World';
  var s2 = "Hello Dart";
  var s3 = 'Hello\'Fullter';
  var s4 = "Hello'Fullter";

  // 2.表示多行字串的方式
  var message1 = '''
  哈哈哈
  呵呵呵
  嘿嘿嘿''';

  print('my name is ${message1}');

  // 3.拼接其他變數
  var name = 'zhengzeqin';
  var age = 18;
  var height = 1.88;
  print('my name is ${name}, age is $age, height is $height');
}
複製程式碼

布林型別

不存在非零即真

dart 宣告變數不存在非零即真情況

var flag = "abc";
if (flag) { // 錯誤示例
  print("執行程式碼");
}
複製程式碼

字串

  • 支援 "", '', """ """ 三種方式
  • """ """ 支援換行
  • 強調: ${變數}, 那麼{}可以省略,如果是函式或者表示式計算則需要不可以省略
// 字串
void stringFunction() {
  // 1.定義字串的方式
  var s1 = 'Hello World';
  var s2 = "Hello Dart";
  var s3 = 'Hello\'Fullter';
  var s4 = "Hello'Fullter";

  // 2.表示多行字串的方式
  var message1 = '''
  哈哈哈
  呵呵呵
  嘿嘿嘿''';

  print('my name is ${message1}');

  // 3.拼接其他變數
  var name = 'zhengzeqin';
  var age = 18;
  var height = 1.88;
  print('my name is ${name}, age is $age, height is $height');
}
複製程式碼

集合

Dart則內建了最常用的三種:List / Set / Map

  • Set和List最大的兩個不同就是:Set是無序的,並且元素是不重複的。
void collectFunction() {
  // List定義
  // 1.使用型別推導定義
  var letters = ['a', 'b', 'c', 'd'];
  print('$letters ${letters.runtimeType}');

  // 2.明確指定型別
  List<int> numbers = [1, 2, 3, 4];
  print('$numbers ${numbers.runtimeType}');

  // Set的定義 Set和List最大的兩個不同就是:Set是無序的,並且元素是不重複的。
  // 1.使用型別推導定義
  var lettersSet = {'a', 'b', 'c', 'd'};
  print('$lettersSet ${lettersSet.runtimeType}');

  // 2.明確指定型別
  Set<int> numbersSet = {1, 2, 3, 4};
  print('$numbersSet ${numbersSet.runtimeType}');

  numbers = Set<int>.from(numbers).toList();
  print(numbers);

  // 新增/刪除/包含元素
  numbers.add(5);
  numbersSet.add(5);
  print('$numbers $numbersSet');

  numbers.remove(1);
  numbersSet.remove(1);
  print('$numbers $numbersSet');

  print(numbers.contains(2));
  print(numbersSet.contains(2));

  // List根據index刪除元素
  numbers.removeAt(3);
  print('$numbers');

  // Map的定義
  // 1.使用型別推導定義
  var infoMap1 = {'name': 'harden', 'age': 18};
  print('$infoMap1 ${infoMap1.runtimeType}');

  // 2.明確指定型別
  Map<String, Object> infoMap2 = {'height': 1.88, 'address': '深圳市'};
  print('$infoMap2 ${infoMap2.runtimeType}');

  // Map的操作
  // 1.根據key獲取value
  print(infoMap1['name']); // harden

  // 2.獲取所有的entries
  print('${infoMap1.entries} ${infoMap1.entries.runtimeType}'); // (MapEntry(name: why), MapEntry(age: 18)) MappedIterable<String, MapEntry<String, Object>>

  // 3.獲取所有的keys
  print('${infoMap1.keys} ${infoMap1.keys.runtimeType}'); // (name, age) _CompactIterable<String>

  // 4.獲取所有的values
  print('${infoMap1.values} ${infoMap1.values.runtimeType}'); // (harden, 18) _CompactIterable<Object>

  // 5.判斷是否包含某個key或者value
  print('${infoMap1.containsKey('age')} ${infoMap1.containsValue(18)}'); // true true

  // 6.根據key刪除元素
  infoMap1.remove('age');
  print('${infoMap1}'); // {name: harden}
}
複製程式碼

question

  1. 下面寫法有錯嗎?
  var info<String, Object> = {
    "name": "why",
    "age": 18
  };
複製程式碼
  1. var names = ["abc", "cba", "nba", "cba"]; 這個如果需要指定型別怎麼寫呢?

函式

返回值 函式的名稱(引數列表) {
  函式體
  return 返回值
}
複製程式碼

可選引數

可選引數可以分為 命名可選引數位置可選引數

  • ? 位置可選引數:對應的順序不可以錯
  • dart 中沒有函式的過載
命名可選引數: {param1, param2, ...}
位置可選引數: [param1, param2, ...]

// 可選引數: 位置可選引數 - 命名可選引數
// 注意: 只有可選引數才可以有預設值
// 位置可選引數: [int age, double height]
// 實參和形參在進行匹配時, 是根據位置的匹配
void sayHello1(String name, [int age = 10, double height = 2, int money = 0]) {
  print("sayHello1 name: $name  age: $age age: $height money: $money");
}

// 命名可選引數
void sayHello2(String name, {int age = 10, double height = 3.14}) {
  print("sayHello2 name: $name  age: $age age: $height");
}

複製程式碼

question

位置可選引數可選引數,一定要按順序才能傳參嗎?如果只想傳 money 引數,不想傳其他引數怎麼辦呢?

函式是一等公民

可以將函式賦值給一個變數, 也可以將函式作為另外一個函式的引數或者返回值來使用

void firstFunction() {
  // 1.直接找到另外一個定義的函式傳進去
  // test(bar);

  // 2.匿名函式 (引數列表) {函式體};
  translationFunc(() {
    print("匿名函式被呼叫");
    return 10;
  });

  var movies = ['無間道1', '無間道2', '無間道3'];

  //  使用forEach遍歷: 有名字的函式
  printElement(item) {
    print(item);
  }

  movies.forEach(printElement);

  // 使用forEach遍歷: 匿名函式
  movies.forEach((item) {
    print(item);
  });
  movies.forEach((item) => print(item));

  // 3.箭頭函式: 條件, 函式體只有一行程式碼
  translationFunc(() => print("箭頭函式被執行"));

  // 4. 使用別名定義引數 要求: 傳入一個函式 & 匿名函式
  calculateFunc((num1, num2) {
    return num1 * num2;
  });

  // 5. 宣告函式,呼叫
  var add = addFunc();
  print(add(20, 30));
}

// 函式可以作為另外一個函式的引數
void translationFunc(Function foo) {
  foo();
}

void bar() {
  print("bar函式被呼叫");
}

// 使用別名定義函式型別
typedef Calculate = int Function(int num1, int num2);

void calculateFunc(Calculate calc) {
  calc(20, 30);
}

Calculate addFunc() {
  return (num1, num2) {
    return num1 * num2;
  };
}
複製程式碼

question

箭頭函式支援多行嗎?

運算子

條件

  • ??= : 當變數為 null 時,使用後面的內容進行賦值,否則不賦值
  • ?? : 當變數為 null 時,則使用後面的值,否則使用變數的值
void conditionalOperatorFunc() {
  // 1.??=: 
  // 當原來的變數有值時, 那麼??=不執行
  // 原來的變數為null, 那麼將值賦值給這個變數
  var name = null;
  name ??= "harden";
  print(name);

  // ??:
  // ??前面的資料有值, 那麼就使用??前面的資料
  // ??前面的資料為null, 那麼就使用後面的值
  var name2 = null;
  var temp = name2 ?? "harden";
  print(temp);
}

複製程式碼

級聯

  • ... : 類似鏈式呼叫
class Animal {
  String? name;

  Animal(this.name);

  void run() {
    print("running");
  }

  void eat() {
    print("eating");
  }
}

// 級聯運算子
void cascadeOperatorFunc() {
  // 級聯運算子
  var p = Animal("zhengzeqin")
    ..name = "harden"
    ..eat()
    ..run();
}
複製程式碼

流程控制

 if else
 for
 while
 do-while
 break
 continue
 switch-case
複製程式碼
void processControlFunc() {
  for (var i = 0; i < 5; i++) {
    print(i);
  }

  var names = ['harden', 'kobe', 'curry'];
  for (var name in names) {
    print(name);
  }

  var direction = 'east';
  switch (direction) {
    case 'east':
      print('東面');
      break;
    case 'south':
      print('南面');
      break;
    case 'west':
      print('西面');
      break;
    case 'north':
      print('北面');
      break;
    default:
      print('其他方向');
  }
}
複製程式碼

類和物件

構造方法

  • 當類中沒有明確指定構造方法時,將預設擁有一個無參的構造方法。
    • ? 在Dart 2.12 之後, 預設可選型別需要加 ? 以前預設不加 否則預設的無參建構函式不能編譯通過
  • ? 當有了自己的構造方法時,預設的構造方法將會失效,不能使用
    • 當然,你可能希望明確的寫一個預設的構造方法,但是會和我們自定義的構造方法衝突;
    • 這是因為Dart本身 不支援函式的過載(名稱相同, 引數不同的方式)
  • 命名構造方法:自定義命名的建構函式
  • 初始化列表:建構函式體執行之前初始化例項變數,可以使用初始化列表
  • 重定向構造方法:希望在一個構造方法中去呼叫另外一個構造方法, 這個時候可以使用重定向構造方法
  • 常量構造方法:傳入相同值時,我們希望返回同一個物件,這個時候,可以使用常量構造方法 參考 final 部分
  • 工廠構造方法:Dart 提供了 factory 關鍵字, 用於通過工廠去獲取物件
void classFunc() {
  var hunman = Human(12);
  print("hunman: $hunman");
}

class Runner {
  // ? 在Dart 2.12 之後, 預設可選型別需要加 ? 以前預設不加 否則預設的無參建構函式不能編譯通過
  String? name;
  int? age;
}

class Human {
  // ? 在Dart 2.12 之後, 預設可選型別需要加 ? 以前預設不加 否則預設的無參建構函式不能編譯通過
  String? name;
  int? age;

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

  // ? 預設是無慘建構函式,宣告引數建構函式,則系統預設無參建構函式就不存在
  // Human(this.name, this.age); // 語法糖等同註釋部分

  // 初始化列表頁
  Human(this.age) : name = "zhengzeqin age is $age";

  // 命名構造方法
  Human.withArgments(String name, int age) {
    this.name = name;
    this.age = age;
  }

  // 命名一個重定向建構函式
  Human.form(int age) : this(age);

  // 常量構造方法 

  // 工程構造方法, ? 如果存在了同名建構函式,則無法再實現工廠建構函式
  // factory Human(Int age) {
  //   return Human.withArgments("zhengzeqin", age);
  // }

  // setter && getter
  String? get getName {
    return name;
  }

  set setName(String name) {
    this.name = name;
  }

  @override
  String toString() {
    return 'name=$name age=$age';
  }
}

複製程式碼

抽象類

  • 抽象方法,必須存在於抽象類中
  • 抽象類是使用abstract宣告的類
  • ? 如果有實現體,實現的類可以不實現該抽象方法
// 抽象類
abstract class Shape {
  double getArea();
  // ? 如果有實現體,實現的類可以不實現該抽象方法
  void getName() {}
}

class Circle extends Shape {
  double? r;

  Circle(this.r);

  @override
  getArea() {
    final rr = r ?? 0;
    return rr * rr * 3.14;
  }
}

class Reactangle extends Shape {
  double w = 0;
  double h = 0;

  Reactangle(this.w, this.h);

  @override
  getArea() {
    return w * h;
  }
}
複製程式碼

隱式介面

  • Dart 中的介面比較特殊, 沒有一個專門的關鍵字來宣告介面.
  • 預設情況下,定義的每個類都相當於預設也宣告瞭一個介面,可以由其他的類來實現(因為Dart不支援多繼承)
  • 通過extends 繼承類,而 implements 來實現介面
abstract class Player {
  play();
}

abstract class Flyer {
  fly();
}

class SuperManer implements Player, Flyer {
  @override
  play() {
    print('超人在玩');
  }

  @override
  fly() {
    print('超人在飛');
  }
}
複製程式碼

Mixin

  • 除了可以通過 class 定義類之外,也可以通過 mixin 關鍵字來定義一個類。
  • 只是通過 mixin 定義的類用於被其他類混入使用,通過 with 關鍵字來進行混入,等同於多繼承
// implements 的方式要求必須對其中的方法進行重新實現, with 則不需要
// 使用 with 方式混入 mixin 類,等同多繼承
// on 關鍵字限定指定類的子類可以使用當前混入(不加預設限定為 Object)
mixin Runing {
  var name = "";
  run() {
    print('在奔跑');
  }
}

mixin Flying {
  fly() {
    print('在飛翔');
  }
}

class SuperWoman with Flying, Runing {
  void sayHello() {
    print("hello my name is $name");
  }
}
複製程式碼

泛型

List和Map的泛型

使用者希望使用的是 int 型別,還是double型別, 甚至是一個字串, 這個時候如何定義呢?

  • 一種方案是使用Object, dynamic 型別, 但是在之後使用時, 非常不方便
  • 另一種方案就是使用泛型.
void genericFunc() {
    // 限制型別
  var names1 = <String>['harden', 'kobe', 'james']; 
  List<String> names2 = ['harden', 'kobe', 'james']; 

 // 對型別進行顯示
  Map<String, dynamic> infos1 = {'name': 'harden', 'age': 18};
  var infos2 = <String, dynamic>{'name': 'harden', 'age': 18}; 
}

// 指定 T 是繼承 num 類
class Location<T extends num> {
  T x;
  T y;

  Location(this.x, this.y);
}
複製程式碼

庫的使用

  • import '庫所在的url';
  • dart: 字首表示Dart的標準庫,如 dart:io、dart:html、dart:mathimport 'dart:io';
  • 當然,你也可以用相對路徑或絕對路徑的 dart 檔案來引用 import 'lib/student/student.dart';
  • 庫中內容和當前檔案中的名字衝突使用 as 起名稱空間作用 import 'lib/student/student.dart' as Stu;
  • 如果希望只匯入庫中某些內容,或者刻意隱藏庫裡面某些內容,可以使用show和hide關鍵字
    • import 'lib/student/student.dart' show Student, Person;
    • import 'lib/student/student.dart' hide Person;

export

  • 通過 export 匯入多個庫

代理示例

參考