OC

Dart语法简介

Posted by sunzhongliang on February 15, 2023

main

// main函数入口
main() {
    print("你好, dart");
}

// 表示main函数没有返回值
// 在这里写方法,也能运行
void main() {
    print("你好, dart");
}

变量与常量

变量

使用var来定义变量,可以自动推断变量类型

var str = "this is a string";

也可以使用具体的类型关键词来定义变量

// String
String str = "this is a string";

// int
int myNum = 123;

常量

使用finalconst修饰符, 它们之间的主要区别

  • const值不变,定义变量的时候就得赋值
  • final可以定义变量的时候不赋值,但只能赋值一次;final不仅有const的编译常量的特性,最重要的它是运行时
const str; // ❌,const常量定义的时候就需要赋值

const str = "123";
str = "231"; // ❌,const定义的常量无法修改

final str;
str = "123"; // ✅,可以定义变量的时候不赋值
str = "232"; // ❌,final只能赋值一次

final str = "123";
str = "232"; // ❌,final只能赋值一次

final time = new DateTime.now(); // ✅,final是运行时(使用时才初始化)
print(time);

const time = new DateTime.now(); // ❌,const不具备运行时, 而DateTime是运行函数

数据类型

String

// 使用String类型关键词定义
String name1 = "jack";
String name2 = 'jack';

// 当有换行文本出现时,使用多个单引号或者多个双引号
String name3 = '''jack
hanse
eric
''';

String name4 = """jack
hanse
eric
""";

字符串拼接
使用$符号拼接

String str1 = "hello";
String str2 = "dart";
print("$str1 $str2, this is a string"); // 输出:hello dart, this is a string

也可以使用+拼接

String str1 = "hello";
String str2 = "dart";
print(str1 +" "+ str2 + ", this is a string");

数值类型

int value = 123;
double price = 123.11;

布尔类型

bool check = true;

// dart 有类型匹配特性
var a = 123;
var b = "123";

if (a == b) { // false
    print("相等");
}

集合类型

var l1 = ["张三", "李四", false];
var name = l1[0];
int len = l1.length;

/// 通过泛型方式,定义集合
var l2 = <String>["张三", "aaa", "bbb"];

/// 定义空集合,通过add添加数据
var l3 = [];
l3.add("123");
l3.length = 0; // 通过设置length = 0设置长度为0,来移除所有元素

/// 创建一个固定长度的集合
var l4 = List.filled(4, "");	
l4[0] = "111";
l4[1] = "222";
var l5 = List<String>.filled(4, ""); // 泛型方式

List常用属性与方法:

  • 常用属性
    • length 长度
    • reversed 翻转, xxx.reversed.toList()
    • isEmpty 是否为空
    • isNotEmpty 是否不为空
  • 常用方法
    • add(value) 增加一个元素
    • addAll([]) 增加数组
    • indexOf(value) 查找值所在的索引
    • remove(value) 删除元素,传入具体值
    • removeAt(index) 删除元素,传入索引
    • fillRange(int start, int end, [E? fillValue]) 批量修改指定位置的元素值
    • insert(index, value) 指定位置插入
    • insertAll(index, list) 指定位置插入list
    • toList() 其他类型转换为list
    • join() List拼接为字符串
    • split() 字符串转换为List
    • forEach 遍历
    • map 遍历
    • where 筛选, 返回list
    • any 判断是否有特定数据,返回true/false
    • every

Set: Set没有顺序且不能重复的集合,类似于OC当中的NSSet

var list = new Set();
list.add('aaa');

Maps

通过{}方式定义对象

/// 与JSON对象相似
var maps = {
    "name": "张三",
    "age": 18,
};
print(maps['name']);

通过new方式定义对象

var p = new Map();
p["name"] = "张三";
p["age"] = 19;
print(p['name']);

类型判断

通过is关键词判断类型

var str = "1234";
if (str is String) {
  print("字符串类型");
}

运算符

Dart中运算符与其他语言基本保持一致,以下这些有特殊:

// 取整
print(12~/5); // 输出:2
print(12/5);  // 输出:2.4

// ??= 表示如果后面值为空,就赋值给前面的值, 与OC当中?:相似
var name = null;
name ??= 'James';
print(name); // 输出James

类型转换

Numberint之间的转换

// String 转 int 通过 int.parse()
String str = "123";
var age = int.parse(str);
// var age = double.parse(str); // 转double
print(age);

// int 转  String 通过toString()
int str = 123;
var age = str.toString();
print(age);

// 通过try catch来捕获转换过程中的异常情况
try {
  int str = 123;
  var age = str.toString();
  print(age);
} catch (e) {
    
}
// 判断是否为空字符串
String str = "";
if (str.isEmpty) {
  print("空"); // 输出:空
}

// 判断是否为null
var str = null;
if (str == null) {
  print("空");
}

// 判断是否是NaN
var age = 0/0;
if (age.isNaN) {
  print("NaN");
}

函数

返回类型 方法名称 (参数1, 参数2, ...) {
  return 返回值;
}

void sss(int a) {
  print(a);
}
sss(123);

方法中的可选参数:

void sss(int a, [String? age, String? dd]) {
  print("$a----$age---$dd");
}

sss(123, "aaa", "bbb"); // 123----aaa---bbb
sss(123); // 123----null---null

命名参数方法:

// 带有命名的参数, 用{}表示
void sss(int a, {String? age, String? dd}) {
  print("$a----$age---$dd");
}

sss(123, age: "111", dd: "222"); // 123----111---222
sss(123); // 123----null---null

// 带required表示age参数是必填
void ddd(int a, {required String age, String? dd}) {
  print("$a----$age---$dd");
}

方法当做参数传递:

int fn1() {
  return 1;
}

fn2(fn) {
  print(fn);
}

fn2(fn1());

class Person {
  var name;
  var age;

  // 构造方法
  Person(int age, String name) {
    this.age = age;
    this.name = name;
  }

  // getter 方法
  get come {
    return "abc";
  }
  // setter 方法
  set come(value) {
    this.age = value;
  }

  String getInfo() {
    return "name is ${this.name}, age is ${this.age}";
  }
}

main() {
  var person = new Person(11, "jack");
  person.age = 33;

  print(person.come); // 调用getter
  print(person.getInfo()); // 调用方法
}

命名构造方法:

class Person {
  var name;
  var age;

  // 命名构造方法
  Person.now(int age, String name) {
	this.age = age;
	this.name = name;
  }

  getInfo() {
	print("name is ${this.name}, age is ${this.age}");
  }
}

// 通过这样调用
var person = new Person.now(11, "jack");

默认构造函数只能写一个, 而命名构造函数可以写多个

访问修饰符

Dart当中并没有其他语言当中的private, public等关键词来修饰访问权限, 但可以使用_修饰,把一个方法或者属性定义成私有(需要将这个类抽离成一个单独的文件)

class Person {
  var _name;
  var age;

  // 构造方法
  Person.now(int age, String name) {
    this.age = age;
    this._name = name;
  }

  getInfo() {
    print("name is ${this._name}, age is ${this.age}");
  }
}

抽象类

通过abstract来定义抽象类,抽象类不能被直接实例化,只有继承它的子类才可以

abstract class Person {
  String strName();
}

// 在抽象类方法中定义的抽象方法,其子类必须要实现
class Child extends Person {
  @override
  String strName() { // strName方法必须要实现
    return "123";
  }
}

main() {
  var child = new Child();
  print(child.strName());
}

extends、implements、with

Dart当中没有多继承, 继承后子类重写或者调用父类的方法,也可以获取父类的属性等.

extends
通过extends继承父类,可以拥有父类的属性,方法, 可以不重写父类的属性方法

class Animal {
  var name = "Animal";
  eat () {
    print("eat");
  }
}

class Dog extends Animal {
  Dog() {
    super.name = "Dog";
  }
  @override
  eat() {
    print("${super.name} eat");
  }
}

main() {
  var s = new Dog();
  s.eat();
}

implements
implements代表实现, 用implements修饰后,需要重写所有的属性和方法

class Animal {
  var name = "Animal";
  eat() {
    print("eat");
  }
}

class Cow {
  drink() {
    print("drink");
  }
}

class Dog implements Animal, Cow {
  @override
  String name = "";

  @override
  drink() {
    print("dog drink");
  }

  @override
  eat() {
    print("dog eat");
  }
}

main() {
  var s = new Dog();
  s.eat();
}

with
with代表混入, 通过with关键词可以间接的实现多继承, 但继承的必须是
如果A类混入了B类,那么A类就可以直接调用B类里面的方法,且不需要实例化B类,不需要B类做单例,也不需要静态被调用的方法,还能混入多个类,这对方法复用带来的极大的便利性,破除了众多限制


class Animal {
  var name = "Animal";
  eat() {
    print("eat");
  }
}

class Cow {
  drink() {
    print("drink");
  }
}

class Dog with Animal, Cow {
  
}

main() {
  var s = new Dog();
  s.eat();
  s.drink();
}

使用with后,不能有构造函数

泛型

T getData<T>(T value) {
  return value;
}

var d = getData<String>("str");

async和await

async是把方法变成异步,await是等待异步方法执行完成
只有async方法才能使用await关键词
如果要调用别的async方法,必须使用await关键词

identical

通过identical判断两个对象内存地址是否一致

var s = 1;
var d = 2;
print(identical(s, d));

Future

FutureDart中提供的一个抽象类、泛型类,它用于封装一段在将来会被执行的代码逻辑,与promise很相似

创建一个延迟执行的 Future

//延迟三秒执行
Future.delayed(Duration(seconds: 3), () {
  print("future delayed");
});

创建一系列的Future,并且会按顺序执行这些Future

Future.forEach([1,2,3], (item) {
  return Future.delayed(Duration(seconds: 2),() {
    print(item);
  });
});

多个Future执行结束后,再通知回调

Future.wait([
  Future(() {
    return "任务1";
  }),
  Future(() {
	return "任务2";
  })
]).then((value) {
  print(value[0]+value[1]);
});

多个Future执行,返回最先执行完成的Future的结果

Future.any([
  Future.delayed(Duration(seconds: 3), () {
	return 333;
  }),
  Future.delayed(Duration(seconds: 1), () {
	return 111;
  }),
]).then((value) {
  print(value); // 111
});

Future.cathcError
注册一个回调,来处理有errorFuture
onError只能处理当前的错误Future,而不能处理其他有错误的Future。catchError可以捕获到Future链中抛出的所有错误。

new Future.error('boom!').catchError(print);

Future.whenComplete

Future.whenComplete总是在Future完成后调用,不管Future的结果是正确的还是错误的

scheduleMicrotask

多个异步下载任务,其中一个任务需要优先执行

/// 任务优先执行
getData() {
  print('getData 开始执行');
  Future(() {
    print('任务 1 开始执行');
    return '任务1';
  }).then((value) {
    sleep(const Duration(seconds: 2));
    print('任务 2 开始执行');
    return '任务2';
  });
  print('任务 C');
  sleep(const Duration(seconds: 2));
  scheduleMicrotask(() {
    print('任务 X');
  });
}

无论是事件队列还是微任务队列,都遵循3个原则:

  • [微任务事件]级别高于[事件队列]
  • 对于队列里添加新队列,新队列要在原先队列执行完之后才执行
  • 对于同一类型的队列任务,先进先出

多线程Isolate

Dart单线程语言,但有时候难免会需要用到多线程的功能,为解决这个困扰,Dart提供了Isolate来替代多线程
Isolate【隔离】的意思,即多个Isolate之间不共享内存,每个Isolate有各自的存储空间,也就是说Dart的并发实际上是通过运行多个Isolate产生的结果

int a = 10;
getData() async {
  a++;
  print('line230 = $a');
  sleep(const Duration(seconds: 2));
  Isolate.spawn(func, 'message');
  sleep(const Duration(seconds: 2));
  print('line234 = $a');
}

void func(String message) {
  print('line238 --- $a');
  a = a + 3;
}

// 执行结果:
// line230 = 11
// line238 --- 10
// line234 = 11

本文首次发布于 孙忠良 Blog, 作者 [@sunzhongliang] , 转载请保留原文链接.