Flutter之Dart语法(一)

266 阅读4分钟

一.定义变量

1.1 明确声明(Explicit)

明确声明变量的方式,格式如下:

变量类型 变量名称 = 赋值;

示例代码:

String name = 'dart';
int age = 18;
double height = 1.8;
print('${name},${age},${height}');

注意事项:定义的变量可以修改值,但是不能赋值其他类型;

String content = 'hello dart';
content = 'hello world';  //正确的
content = 111;  //错误的,将一个int值赋值给一个String变量

1.2类型推导(Type Inference)

类型推导声明变量的方式,格式如下:

var / dynamic / const / final 变量名 = 赋值;

1.2.1 var的使用

var的正确用法:

var name = 'dart';
name = 'oy';
print(name.runtimeType);  //String(解释:runtimeType用于获取变量当前的类型)

var的错误用法:

var name = 'dart';
name = 111; //不可以将String赋值给一个int类型

1.2.2 dynamic的使用

如果确实希望这样做,可以使用dynamic来声明变量: 但是在开发中,通常情况下不使用dynamic,因为类型的变量会带来潜在的危险。

dynamic name = 'dart';
print(name.runtimeType);  //String
name = 18;
print(name.runtimeType);  //int

1.2.3 final&const的使用

final和const都是用于定义常量的,也就是定义之后值都不可以修改。

final name = 'dart';
name = 'oy';  //错误做法

const age = 18;
age = 20;   //错误用法

const sex;
sex = '女';  //错误用法

final a;
a = 1;   //正确

final和const有什么区别?

  1. const赋值时,必须给个明确的值;final可以事后赋值;
  2. final赋值时,可以动态获取,比如赋值一个函数;
String getName(){
    return 'dart';
}
main(List<String> args){
    const name = getName();  //错误的做法;因为要执行函数才能获取到值;
    final name = getName();  //正确的做法
}

final和const的小案例:

  1. 首先,const是不可以赋值为DateTime.now()
  2. 其次,final一旦被赋值后就确定了结果,不能再次赋值
const time1 = DateTime.now();   //错误的赋值方式
final time2 = DateTime.now();
print(time2); //2019-04-05 09:02:54.052626
sleep(Duration(seconds:2));
print(time); //2019-04-05 09:02:54.052626

const 放在赋值语句的右边,可以共享对象,提高性能:

  • 这里可以暂时先做了解,后面讲解类的常量构造函数时,会再次提到这个概念
class Person{
    const Person();
}

main(List<String> args){
    final a = const Person();
    final b = const Person();
    print(indetical(a,b));  //true
    
    final m = Person();
    final n = Person();
    print(indetical(m,n));  //false
}

二.数据类型

2.1数字类型

对于数值来说,只要记住整数用int,浮点数用double就行了。

不过,要说一下Dart中的int和double可表示的范围并不是固定的,它取决于运行Dart的平台。

//1.整数类型int
int age = 18;
int hexAge = 0x12;
print(age);  //结果:18
print(hexAge);  //结果:18

//2.浮点类型double
double height= 1.8;
print(height);  //结果1.8

字符串和数字之间的转换:

1.字符串转数字
var one = int.parse('111');
var two = doouble.parse('12.22');
print('$one ${one.runtimeType}');  //结果:111 int
print('$two ${two.runtimeType}');  //结果:12.22 double

2.数字转字符串
var num1 = 123;
var num2 = 123.113;
var num1Str = num1.toString();
var num2Str = num2.toString();
var num2StrD = num2.toStringAsFixed(2);  //保留两位小数
print('${num1Str} ${num1Str.runtimeType}');  //结果:123 String
print('${num2Str} ${num2Str.runtimeType}');  //结果:123.113 String
print('${num2StrD} ${num2StrD.runtimeType}'); //结果:123.11 String

2.2布尔类型

布尔类型中,Dart提供了一个bool的类型,取值为true和false

//布尔类型
var isFlag = true;
print('$isFlag ${isFlag.runtimeType}');   //结果:true bool

注意:Dart中不能判断非0即真,或者非空即真

Dart的类型安全性意味着您不能使用if(非booleanvalue)或assert(非booleanvalue)之类的的代码。

var message = 'hello';
//错误的写法
if(message){
    print(message);
}

2.3字符串类型

Dart字符串是UTF-16编码单元的序列。您可以使用单引号或双引号创建一个字符串:

//1.定义字符串的方式
var s1 = 'hello world';
var s2 = 'hello dart';
var s3 = 'hello\'Flutter';
var s4 = "hello'Flutter";

print(s1);
print(s2);
print(s3);
print(s4);

结果:
hello world
hello dart
hello'Flutter
hello'Flutter

可以使用三个单引号或者双引号表示多行字符串:

//2.表示多行字符串的方式
var message = '''
哈哈
嘿嘿
呵呵''';
print(message);
结果:
哈哈
嘿嘿
呵呵

字符串和其他变量或表达式拼接:使用${expression},如果表达式是一个标识符,那么{}可以省略

//3.拼接其他变量
var name = 'dart';
var age = 18;
print('my name is ${name},age is $age');  //my name is dart,age is 18

2.4集合类型

2.4.1集合类型的定义

对于集合类型,Dart内置了最常用的三种:List / Set /Map

其中,List可以这样定义:

//1.使用类型推导定义:
var letters = [1,2,3,4];
print('$letters ${letters.runtimeType}');   //[1, 2, 3, 4] List<int>

//2.明确类型
List<String> str = ['2','a'];
print('$str ${str.runtimeType}');   //[2, a] List<String>

其中,Set可以这样定义:

  • 其实,也就是把[]换成{}.
  • Set 和 List最大的区别就是:Set是无序的,并且元素不可重复。
//1.使用类型推导定义:
var lettersSet = {1, 2, 4};
print('$lettersSet ${lettersSet.runtimeType}');  //{1, 2, 4} _CompactLinkedHashSet<int>

//2.明确类型
Set<int> numberSet = {1, 2, 3};
print('$numberSet ${numberSet.runtimeType}');  //{1, 2, 3} _CompactLinkedHashSet<int>

最后,Map是我们常说的字典类型。定义是这样的:

//1.类型推导
var infoMap1 = {'name': 'dart', 'age': 18};
print('$infoMap1 ${infoMap1.runtimeType}'); //{name: dart, age: 18} _InternalLinkedHashMap<String, Object>

//1.明确类型
Map<String, Object> infoMap2 = {'name': 'oooo', 'age': 20};
print('$infoMap2 ${infoMap2.runtimeType}');  //{name: oooo, age: 20} _InternalLinkedHashMap<String, Object>
  

2.2.2 集合的常见操作

了解了这三个集合的定义方式后,我们来看看一些最基础的公共操作

第一类,是所有集合都支持的获取长度的属性length:

//获取集合的长度
print(letters.length);  //4
print(numberSet.length);  //3
print(infoMap2.length);  //2

第二类,是添加/删除/包含操作

对于 List来说,由于元素是有序的,它还提供了一个删除指定索引位置上元素的方法。

//添加 / 删除 / 包含 元素
letters.add(5);
numberSet.add(4);
print(letters);  //[1, 2, 3, 4, 5]
print(numberSet);  //{1, 2, 3, 4}

letters.remove(1);  
numberSet.remove(1);  
print(letters);  //[2, 3, 4, 5]
print(numberSet);  //{2, 3, 4}

letters.contains(1);
numberSet.contains(1);
print(letters);  //[2, 3, 4, 5]
print(numberSet);  //{2, 3, 4}

//List 根据index删除元素
letters.removeAt(2);
print(letters);    //[2, 3, 5]

第三类,是Map的操作

由于它有key 和 value。因此无论是读取值,还是操作,都要明确是基于key的,还是基于value的,或者是基于key/value对的。

// Map的操作
// 1.根据key获取value
  print(infoMap1['name']); //dart

// 2.获取所有的entries
  print('${infoMap1.entries} ${infoMap1.entries.runtimeType}'); // (MapEntry(name: dart), 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}'); // (dart, 18) _CompactIterable<Object>

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

// 6.根据key删除元素
  infoMap1.remove('age');
  print('${infoMap1}'); // {name: dart}

三.函数

3.1 函数的基本定义

Dart是一种真正的面向对象语言,所以即使函数也是对象,所以也有类型,类型就是Function。 这也是意味着函数可以作为变量定义或者作为其他函数的参数或者返回值。

函数的定义方式:

返回值 函数名(参数){
    函数体
    return 返回值
}

按照上面的定义方式,定义一个完整的函数:

int sum(int a, int b){
    return a+b;
}

Effective Dart 建议对公共的API,使用类型注解,但是如果我们省略掉了类型,依然是可以正常工作的

sum(a,b){
    return a+b;
}

另外,如果函数体中只有一个表达式,那么可以使用箭头函数

注意,这里只能是个表达式,不能是一个语句。

sum(a,b) => a+b;

3.2 函数的参数问题

函数的参数可以分成两类:必填参数和可选参数

前面讲的都是必填参数

3.2.1 可选参数

可选参数分为:命名可选参数 和 位置可选参数

定义方式:

命名可选参数:{param1,parma2,...}
位置可选参数:[param1,param2,...]

命名可选参数

printInfo(String name, {int? age, double? height}) {
    print('name=$name age=$age height=$height');
}

// 调用printInfo1函数
printInfo('why'); // name=why age=null height=null
printInfo('why', age: 18); // name=why age=18 height=null
printInfo('why', age: 18, height: 1.88); // name=why age=18 height=1.88
printInfo('why', height: 1.88); // name=why age=null height=1.88

位置可选参数的演示

// 定义位置可选参数 
printInfo2(String name, [int age, double height]) { 
    print('name=$name age=$age height=$height'); 
} 

// 调用printInfo2函数 
printInfo2('why'); // name=why age=null height=null 
printInfo2('why', 18); // name=why age=18 height=null printInfo2('why', 18, 1.88); // name=why age=18 height=1.88

命名可选参数,可以指定某个参数是必传的(使用@required,有问题)

printInfo(String name,{int? age, double? height, @required String? address}) {
    print('name=$name age=$age height=$height');
}

// 调用printInfo1函数
printInfo('why', address: 'shanghai');
printInfo('why', age: 18, address: 'shanghai');
printInfo('why', age: 18, height: 1.88, address:'shanghai');
printInfo('why', height: 1.88, address: 'shanghai');

3.3 函数是一等公民

在很多语言中,函数并不能作为一等公民来使用,比如Java/OC.这种限制让编程不够灵活,所以现代的编程语言基本都支持函数作为一等公民来使用,Dart也支持。

这就意味着你可以将函数赋值给一个变量,也可以将函数作为另外一个函数的参数或者返回值来使用。

//1.将函数赋值给一个变量
foo() {
return '将函数赋值给一个变量';
}
var bar = foo;
print(bar());     //将函数赋值给一个变量

// 2.将函数作为另一个函数的参数
fn() {
return '将函数作为另一个函数的参数';
}

foo1(fn1) {
return fn1();
}
print(foo1(fn));

//3.将函数作为另一个函数的返回值
fn1() {
return '将函数作为另一个函数的返回值';
}
getFunc() {
return fn1;
}
var func = getFunc();
func();

3.4 匿名函数的使用

大部分我们定义的函数都会有自己的名字,比如前面定义的foo,test函数等等。

但是某些情况下,给函数命名太麻烦了,我们可以使用没有名字的函数,这种函数可以被称之为匿名函数(anonymous function),也可以叫lambda或者closure

// 1.定义数组
var movies = ['盗梦空间', '星际穿越', '少年派', '大话西游'];

// 2.使用forEach遍历: 有名字的函数
printElement(item) {
    print(item);
}
movies.forEach(printElement);

// 3.使用forEach遍历: 匿名函数
movies.forEach((item) {
    print(item);
});

// 4.使用forEach遍历: 匿名函数,箭头函数
movies.forEach((item) => print(item));

3.5 词法作用域

dart的词法有自己明确的作用域范围,它是根据代码的结构({})来决定作用域范围的。

优先使用自己作用域中的变量,如果没有找到,则一层层向外查找。

var name = 'global'; 
main(List<String> args) { 
    // var name = 'main'; 
    void foo() { 
        // var name = 'foo'; 
        print(name); 
    } 
    foo(); 
}

3.6 词法闭包

闭包可以访问其词法范围内的变量,即使函数在其他地方被使用,也可以正常的访问。

main(List<String> args) {
  makeAdder(int addBy) {
    return (int i) {
      return i + addBy;
    };
  }

  var adder2 = makeAdder(2);
  print(adder2(10)); // 12
  print(adder2(6)); // 8

  var adder5 = makeAdder(5);
  print(adder5(10)); // 15
  print(adder5(6)); // 11
}

3.7 返回值问题

所有函数都返回一个值。如果没有指定返回值,则语句返回null,隐式附加到函数体。

main(List<String> args) {
  print(foo());   //null
}
foo() {
  print(111);  //111
}