dart3.x 语法学习

271 阅读6分钟

关于dart的数据类型和数据值

  • dart支持数据类型定义,但不强制要求进行数据类型定义,能自行推导数据类型
  • dart中一切值都是对象

var, const, final

  • dart建议局部变量通过var定义,var表示可改变值的变量
  • final 和 const 都表示值不可改的变量
  • const 变量的变量值在编译时就得确定, 只能初始化一次,且在定义时就得初始化,不可以用可变变量对其进行初始化,后续完全不可改
  • final 变量定义时可以不被初始化,但只能初始化一次,可以用可变变量对其进行初始化,可变变量改变,不影响final变量,后续不可改
  • 当一个函数调用被const修饰,那么该函数入参应该都是常量,若参数也为函数调用,那么函数参数的参数也应该是常量。否则就会出现标题中的错误。解决方式,可以将const移除掉
String name = '张三';
// 这里会报错,因为name是值可变变量, const要求变量值在编译时就得确定,但根据语法name的值是可变的,会导致tmp值无法确定,因此报错
const String tmp = '姓名${name}';
print(tmp);
const String name = '张三';
// 这里不会报错, 因为name也是const, 索引tmp值在编译时,即可确定
const String tmp = '姓名${name}';
print(tmp);
const list = ['a'];
// 语法不会报错,但运行时会报错,因为const变量是完全不可改的变量,即使变量值是List类型
list.add('b');
print(list);
String name = '张三';
// 可以用可变变量对final变量进行初始化,后续可变变量的修改不会影响final变量
final String tmp = '姓名${name}';
print(tmp); // 输出: 姓名张三

name = '王五';
print(tmp); // 输出:姓名张三
String name = '张三';
// 只定义final变量,不进行初始化
final String tmp;
// 对final变量进行一次初始化. P.S. 只能进行一次初始化
tmp = '姓名${name}';
print(tmp);

// 这里会语法报错,因此 final 变量只能进行一次初始化
tmp = '你好'
final list = ['a'];
// 如果final变量的实际类型是List这类引用数据类型,那内容是可以改变的
list.add('b');
print(list);

关于数组

dart中和其他编程语言不同,没有直接的数组类型,只有List

基本数据类型

Number

又分两种子类型: intdouble

intdouble 之间无法相互转换,但都可以转换为num

var x = 1;
print(x.runtimeType);

// 这里语法会报错,因为int无法与double相互转换
double y = x;
print(y.runtimeType);
var x = 1;
print(x.runtimeType);

// 这里没问题,因为int和double都可以转换为num类型
num y = x;
print(y.runtimeType);

整数类型的数值,会自动识别为 int

var x = 1;
print(x.runtimeType); // 输出: int

小数类型的数值,会自动识别为 double

var x = 1.0;
print(x.runtimeType); // 输出: double

字符串转int

String str = '113';
int x = int.parse(str);
print(x); // 输出: 113
print(x.runtimeType); // 输出: int

字符串转double

String str = '113';
double x = double.parse(str);
print(x); // 输出: 113.0
print(x.runtimeType); // 输出: double

String

  • Dart 字符串是一组 UTF-16 单元序列。 字符串通过单引号或者双引号创建。可以通过${expression}方式内嵌表达式
  • 可以通过'''内容'''"""内容"""创建多行文本
String str = '年龄:12';
print(str);

str = '张三,${str}';
print(str);

str = '''你好
这是多行文本
''';
print(str);

判断字符串是否为空

String str = '你好';
print(str.isEmpty); // 输出: false
str = '';
print(str.isEmpty); // 输出: true

Boolean

  • dart只有true和false是布尔值
  • if判断和assert只能使用布尔值,不能用非布尔值判断(这一点和其他编程语言有很大区别)
// 检查空字符串。 
var fullName = ''; 
assert(fullName.isEmpty); 

// 检查 0 值。 
var hitPoints = 0; 
assert(hitPoints <= 0); 

// 检查 null 值。 
var unicorn; 
assert(unicorn == null); 

// 检查 NaN 。 
var iMeantToDoThis = 0 / 0; 
assert(iMeantToDoThis.isNaN);
String str;
// 这里会语法报错, dart的if语句只能使用布尔值判断
if (str) {
    print(1);
}

List

dart中没有直接意义上的数组,替代品是List

List<int> arr = [1, 3, 5];
print(arr);
print(arr.runtimeType); // 输出: List<int>
// 可通过类型数组下标的方式获取指定位置的值
print(arr[2]); // 输出: 5
// 可以通过下标方式赋值
arr[3] = 10;
// 获取长度
print(arr.length);
// 判断是否为空
print(arr.isEmpty);

Set

元素唯一且无序的集合

Set<int> arr = {3, 1, 5};
print(arr);
arr.forEach((int item) {
    print(item);
});
print(arr.runtimeType); // 输出: _Set<int>
// 定义set集合
var arr = <int>{3, 1, 5};
print(arr);
arr.forEach((int item) {
    print(item);
});
print(arr.runtimeType); // 输出: _Set<int>
// 定义一个空的set集合
var elements = <String>{}; 
elements.add('fluorine'); 
print(elements.runtimeType);// 输出: _Set<string>

是 Set 还是 Map ?  Map 字面量语法同 Set 字面量语法非常相似。 因为先有的 Map 字母量语法,所以 {} 默认是 Map 类型。   如果忘记在 {} 上注释类型或赋值到一个未声明类型的变量上,   那么 Dart 会创建一个类型为 Map<dynamic, dynamic> 的对象。

Map

Map 是用来关联 keys 和 values 的对象。 keys 和 values 可以是任何类型的对象。在一个 Map 对象中一个 key 只能出现一次。 但是 value 可以出现多次。

var gifts = { 
 // Key: Value 
'first': 'partridge', 
'second': 'turtledoves', 
'fifth': 'golden rings' 
}; 
var nobleGases = { 2: 'helium', 10: 'neon', 18: 'argon', };
Map<String, dynamic> tmp = {'name': '张三', 'age': 12};
print(tmp);
print(tmp.runtimeType); // 输出: _Map<String, dynamic>
var tmp = {'name': '张三', 'age': 12};
print(tmp);
print(tmp.runtimeType); // 输出: _Map<String, Object>

是 Set 还是 Map ?  Map 字面量语法同 Set 字面量语法非常相似。 因为先有的 Map 字母量语法,所以 {} 默认是 Map 类型。   如果忘记在 {} 上注释类型或赋值到一个未声明类型的变量上,   那么 Dart 会创建一个类型为 Map<dynamic, dynamic> 的对象。

Rune

在 Dart 中, Rune 用来表示字符串中的 UTF-32 编码字符。

Unicode 定义了一个全球的书写系统编码, 系统中使用的所有字母,数字和符号都对应唯一的数值编码。 由于 Dart 字符串是一系列 UTF-16 编码单元, 因此要在字符串中表示32位 Unicode 值需要特殊语法支持。

表示 Unicode 编码的常用方法是, \uXXXX, 这里 XXXX 是一个4位的16进制数。 例如,心形符号 (♥) 是 \u2665。 对于特殊的非 4 个数值的情况, 把编码值放到大括号中即可。 例如,emoji 的笑脸 (�) 是 \u{1f600}。

String 类有一些属性可以获得 rune 数据。 属性 codeUnitAt 和 codeUnit 返回16位编码数据。 属性 runes 获取字符串中的 Rune 。

下面是示例演示了 Rune 、 16-bit code units、 和 32-bit code points 之间的关系。

main() { 
    var clapping = '\u{1f44f}'; 
    print(clapping); 
    print(clapping.codeUnits); 
    print(clapping.runes.toList()); 
    Runes input = new Runes( '\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); 
    print(new String.fromCharCodes(input)); 
}

Symbol

一个 Symbol 对象表示 Dart 程序中声明的运算符或者标识符。 你也许永远都不需要使用 Symbol ,但要按名称引用标识符的 API 时, Symbol 就非常有用了。 因为代码压缩后会改变标识符的名称,但不会改变标识符的符号。 通过字面量 Symbol ,也就是标识符前面添加一个 # 号,来获取标识符的 Symbol 。

#radix 
#bar

运算符

流程控制

异常

函数

返回值

所有函数都会返回一个值。 如果没有明确指定返回值, 函数体会被隐式的添加 return null; 语句。

foo() {}
assert(foo() == null);

main() 函数

任何应用都必须有一个顶级 main() 函数,作为应用服务的入口。 main() 函数返回值为空,参数为一个可选的 List<String>

void main() {
    print('hello');
}
// 这样运行应用: 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'); 
}

匿名函数与命名函数

main() {
  var list = ['apples', 'bananas', 'oranges'];
  // forEach调用匿名函数
  list.forEach((item) {
    print('$item');
  });
  print('');
  // forEach调用命名函数
  list.forEach(fun);
}

void fun(String item) {
  print('$item');
}

函数体只有一个返回语句的函数语法糖

main() {
  var fun = (String name) => '姓名:$name';
  print(fun('张三'));
}

命名参数与命名可选参数

void main() {
  test(age: 22);
  test2();
  test2(str1: '张三', str2: '里斯');
  test2(str1: '张三');
}

/**
 * 使用命名参数,且age为必传, name如果不传则使用默认值
 */
void test({String name = '张三', required int age}) {
  print('${name} ${age}岁');
}

/**
 * 使用命名参数,且str1, str2都不是必填的
 */
void test2({String? str1, String? str2}) {
  var str = StringBuffer();
  print(str.runtimeType);
  if (str1 != null) {
    str.write(str1);
  }
  if (str2 != null) {
    str.write(str2);
  }
  print(str.toString());
}

位置参数与位置可选参数

void main() {
  test('张三');
  test('里斯', 12);
  test('王五', 21, false);
}

/**
 * 使用命名参数,且age为必传, name如果不传则使用默认值
 */
void test(String name, [int? age, bool male = true]) {
  var str = StringBuffer();
  str.write(name);
  if (male) {
    str.write(' 性别: ${male ? '男' : '女'}');
  }
  if (age != null) {
    str.write(' ${age}岁');
  }
  print(str);
}
main() {
  // 这个调用会导致doStuff内部报错,因为list默认值被声明成了不可改的const类型
  // doStuff();
  // 这里不会报错,因为此时list是普通的List<int>
  doStuff(list: [44]);
}

void doStuff(
    {List<int> list = const [1, 2, 3],
    Map<String, String> gifts = const {
      'first': 'paper',
      'second': 'cotton',
      'third': 'leather'
    }}) {
  print(list.runtimeType);
  // list参数使用的是默认值,则修改这个list会报错,否则不会报错
  list.add(233);
  print('list:  $list');
  print('gifts: $gifts');
}