Dart语法精选一:基础知识

461 阅读8分钟

在Vscode环境下需要安装的依赖

提示所需要的插件

编译代码时可以用到的插件

image.png

使用coderrunner我们就无需在控制台输入 dart xx.dart运行代码。可以右键 image.png 来执行代码

基本知识快速入门

Hello World

void main(){  //main函数作为函数的入口
  print("Hello World"); //每行末尾必须加上分号;dart打印信息使用print函数;
}

在VScode中我们输入main会自动帮助我们生成main函数

void main(List<String> args) { //这里List<String>用到了泛型的知识,后面我们会讲到,args是传递的参数
  print("Hello Wrold");
}

注释语法

  • 采用//进行语句注释
  • 采用 /* */进行块级注释
  • 采用///进行文档注释
void main(List<String> args) {
  print("Hello Wrold");  //这是一个注释
}
/**
 * 这是块级注释
 */

///这是一个文档注释
///
///
///

变量的基本概念

  • 变量是一个引用,Dart万物皆为对象,变量存储的是对象的引用。

  • 声明变量

    • 明确指定类型: int age = 18;
    • 不明确类型 var age = 18; dynamic age = 18;
  • Dart中的变量大小写敏感

  • Dart中变量默认值是null

  • Dart变量的值不会进行隐式转换, null不会转换成false

案例一,声明变量

void main(List<String> args) {
  // 声明变量

  var age1 = 18; //不明确类型
  num age2 = 18; //明确为num类型
  int age3 = 18; //明确为int类型
  print('$age1,$age2,$age3'); //这里使用到了模板字符串,后文会将,这里就是将age1,age2,age3的值打印出来。
}

案例二:变量的默认值为null,大小写敏感,变量不会做隐式转换

void main(List<String> args) {
  //dart中变量的默认值是null
  int num;
  print(num); //null

  //dart中变量区分大小写
  // print(Num); //报错

  //dart中变量的值不会做隐式转换
  bool flag;
  print(flag); //null 
  // if(flag){  //不能这么写 一定要flag == false
  //   print('hello');
  // }
}

模板字符串

在js中模板字符串需要在反引号中才能使用,da

class Person{
  int age = 18;
  Person(this.age);
}
void main(List<String> args) {
  int age = 18;
  Person p = Person(20);
  print('在main函数里age是$age,在类中这里的age是${p.age}'); //在main函数里age是18,在类中这里的age是20
}

常量的概念

  • 常量是一旦定义了就无法修改的值
  • final与const都可以定义常量

我们需要区分的就是const与final的区别。const要声明编译时能取到的值,final是运行时能取到的值。比如获取当前时间常量,你11点和1点获取的事件是不同的,你就不能用const来定义。

void main(List<String> args) {
  
  var age = 20;
  print(age);
  
  var Age = 10;
  print(Age);

  const String name = 'harry';
  // name = 'zhangsan';
  final  food = 'rice';
  print(name);
  print(food);
  var defaultVale;
  print(defaultVale);
}

变量的几个基本类型

数字类型number

数字类型有 int 和 double 还有 num。我们可以把num看成是int和double的父类。

void main(List<String> args) {
  int num1 = 10;
  double num2 = 2.56;
  num num3 = 3.56;
  num num4 = 15;
  print('$num1,$num2,$num3,$num4'); //10,2.56,3.56,15
}

关于数字类型的一些功能性函数

void main(List<String> args) {
  //1.类型转换
  double price = 3.15;
  print(price.toInt()); //向下取整

  //2.四舍五入
  print(3.1415926.round()); //3
  print(3.6415926.round()); //4
  print(3.1415926.toStringAsFixed(4)); //保留四位小数 3.1416

  //3.返回余数
  print(10.remainder(4)); //2 (remain在英文中是余数的意思,remainder是余数的意思)
  
  //4.数字比较 0:相同  1:大于  -1:小于
  print(10.compareTo(12.5)); //-1

  //返回最大公约数
  print(12.gcd(15)); //3

  //科学计数法表示
  print(1000.toStringAsExponential(1)); //1.0e+3
}

字符串类型String

字符串类型在Dart中用 String 表示,数据用单引号或双引号扩起来。

void main(List<String> args) {
  var str1 = "Hello World";
  String str2 = '你好世界\n';
  
  String str3 = '''
    Hello 

    World
  '''; //三引号可以表示换行的字符串
  print('$str1,$str2 $str3');
}

dart中常用的字符串功能性函数

void main(List<String> args) {
  var str1 = "Hello World";
  String str2 = '你好世界\n';

  //字符串的拼接
  print(str1 + str2); //Hello World你好世界
  print('$str1$str2'); //Hello World你好世界

  //字符串的分隔
  print(str1.split(' ')); //以空格进行分隔 [Hello, World]

  //字符串的裁切
  print('   abc   '.trim()); //abc默认帮我们把两边的空格全都删除了,trimeLeft可只删除左边的,trimRight可以只删除右边的

  //判断字符串是否为空
  print(''.isEmpty); //true
  print(''.isNotEmpty); //false

  //字符串的替换
  print(str1.replaceAll('World', 'Dart')); //Hello Dart

  //使用正则表达式进行替换
  print('h1k2c45u89'.replaceAll(RegExp(r'\d+'),'_')); //h_k_c_u_

  //查找字符串
  print(str1.contains('e')); //true

  //定位字符串 indexOf从左往右边找(没有找到返回-1,找到返回第一个找到的下标)
  print(str1.indexOf('d')); //10
  print(str1.indexOf('0')); //-1

  //定位字符串lastIndexOf从右往左边找
  print(str1.lastIndexOf('e')); //1



}

Boolean

布尔类型比较简单,它的值只有 true 或 false,在 Dart 中用 bool 表示。

void main(List<String> args) {
  //声明布尔值
  bool flag1 = true;
  print(flag1);

  bool flag2 = false;
  print(flag2);

  //显示地进行判断
  var flag3;
  if(flag3 == null){ //没有三个等号
    print('真');
  }else{
    print('假');
  }
}

bool的一些有用的功能性函数

void main(List<String> args) {
  var n1 = 0 / 0;
  print(n1); //NaN(非数字,这个跟js是一模一样的)
  print(n1.isFinite); //是否有限 false
  print(n1.isInfinite); //是否无限
  print(n1.isNaN); //是否非数字  true
  print(12.isNegative); //是否是负数 false
}

List数组

列表简单来说就是存放着排成一列的数据,可以理解为一个存放数据的容器,列表中的每个数据称为元素,在 Dart 中用 List 表示,数据用 "[]" 括起来,元素之间用 "," 隔开,如果不指定 List 中存放的数据类型的话是可以存放任意类型的数据的,

void main(List<String> args) {
  //声明List - 字面量
  List l1 = ['a','b','c',1]; //不限制数字类型的一种方式
  print(l1);

  //泛型(限制元素的类型)
  List l2 = <int> [1,2,3]; //<>要加到前面
  print(l2);
  //通过构造函数的声明方式
  // var l3 = new List();  已经弃用了
  var l3 = new List.empty();
  print(l3);  //[]

  var l31 = new List.empty(growable: true);
  l31.add(1);
  print(l31); //[1]
  
  var l4 = new List.filled(3, 6);
  print(l4);  //[6, 6, 6]

  //扩展运算符
  var l5 = [0,...l31,...l4];
  print(l5); //[0, 1, 6, 6, 6]
  
  var l6;
  var l7 = [7,...?l6]; //不加?会警告,因为null类型不能用于扩展运算符
  print(l7); //[7]

  //返回列表的长度
  print(l1.length); //4

  //列表的翻转
  print(l1.reversed); //(1, c, b, a)  ()里面是一个可迭代的内容,严格意义上来说已经不是列表了
  print(l1.reversed.toList()); //[1, c, b, a]

  //在制定的位置添加元素
  l7.insert(1, 3);
  print(l7); //[7, 3]

  //清空
  l31.clear();
  print(l31); //[]
  print(l31.isEmpty); //true

  //合并元素
  List words = ['hello','world'];
  print(words.join('-'));//join可以看成split的逆向操作 hello-world
}

list的一些实用功能性函数

void main(List<String> args) {
  var nums = [1,2,3];
  //forEach
  nums.forEach((element) {
    print(element);
  });

  //map
  var newNums1 = nums.map((e) => e * e ); //这里的箭头函数后面会介绍到
  print(newNums1); //(1, 4, 9)

  var newNums2 = nums.map((e){
    return e * e;
  });
  print(newNums2.toList()); //[1, 4, 9]

  bool isOdd(n) => n % 2 ==1;
  bool isEven(n){
    return n % 2 ==0;
  }

  //where筛选出符合条件的元素,可以用map替代
  var oddNum = nums.where(isOdd);
  print(oddNum); //(1, 3)

  //返回布尔值:有任意一个满足条件
  print(nums.any(isOdd)); //true
  print(nums.every(isEven)); //false

  var pairs = [[1,2],[3,4]];
  //expand条件降阶函数
  var flattened = pairs.expand((element) => element);
  print(flattened); //(1, 2, 3, 4)

  //fold函数,类似js的reduce
  int result = nums.fold(0, (previousValue, element) => previousValue + element);
  print(result); //6
}

Set集合

  • Set是一个无序的,元素唯一的集合。要注意它无法通过下标取值。
void main(List<String> args) {
  var nums = <int>{1, 2, 3}; //集合的三个性质:确定性、互异性、无序性
  print(nums);

  //构造函数
  var fruits = new Set();
  fruits.add('bannana');
  fruits.add('apple');
  fruits.add('橘子');
  print(fruits); //{bannana, apple, 橘子}
  print(fruits.toList()); //转成列表 [bannana, apple, 橘子]

  //如果列表转换为集合,会去重
  List myNums = [1,2,2,3,3,4,5];
  print(myNums.toSet()); //{1, 2, 3, 4, 5}

  //集合特有的操作
  var caocao = new Set();
  //addAll批量添加元素
  caocao.addAll(['曹仁','夏侯惇','关羽']);
  var liubei = new Set();
  liubei.addAll(['张飞','关羽','诸葛亮']);
  //求交集
  print(caocao.intersection(liubei)); //{关羽}
  //求并集
  print(caocao.union(liubei)); //{曹仁, 夏侯惇, 关羽, 张飞, 诸葛亮}
  //求差集
  print(caocao.difference(liubei));//{曹仁, 夏侯惇}
  //返回第一个元素
  print(caocao.first); //曹仁
  //返回最后一个元素
  print(caocao.last); //关羽
  //集合无法通过下标取值
  // print(caocao[1]); //报错
}

Map

  • Map是一个无序的键值对(key-value)映射。通常被称之为哈希或字典。
  • 声明方式
    • var map = {key:value1,key2:value2};
    • var map = new Map();
    • map['key'] = value;

我们通过案例来演示常用api

void main(List<String> args) {
  var person = {
    'name':'张三',
    'age':20
  };
  print(person); //{name: 张三, age: 20}

  //通过构造函数来创建Map
  var p = Map();
  p['name'] = '李四';
  p['age'] = 22;
  print(p); //{name: 李四, age: 22}

  //访问map的属性
  print(p['name']); //李四

  //判断Map中的key与value是否存在
  print(p.containsKey('name')); //true
  print(p.containsValue(22)); //true

  //赋值
  //如果key不存在,我们才赋值,如果key已经存在了,则不赋值
  p.putIfAbsent('gender', () => '男');
  p.putIfAbsent('gender', () => '女');
  
  //获取Map中所有的Key
  print(p.keys); //(name, age, gender)

  //获取Map中所有的value
  print(p.values); //(李四, 22, 男)

  //根据条件删除map中的值
  p.removeWhere((key, value) =>  key == 'gender');
  print(p); //{name: 李四, age: 22}

}

Dart中的一些特殊运算符

除了 + - * / % ?等基本运算符以外,Dart也给我们提供了一些特殊的运算符,如下:


void main(List<String> args) {
  //地板除
  print(7 ~/ 4); //1.75 向下取整得到1

  //类型判断运算符
  List list = [];
  if(list is List){
    print('list is list');
  }else{
    print('list is not List');
  }
  print('Hello' is! String); //false;
  print(12 is int); //true

  //逻辑空运算符??(如果不是空返回表达式前面的,如果是空返回表达式后面的)
  print(1 ?? 3); //1
  print(null ?? 12); //12

  var foo;
  print(foo ?? 0); //0

  //空赋值语句
  var a;
  a ??= 3;
  print(a); //3
  a ??= 6;
  print(a); //3

  //条件属性运算符(保护可能为空的属性)
  var obj;
  print(obj?.length); //null

  //级联运算符
  Set s = new Set();
  s..add('a')
   ..add('b')
   ..add('c');
  print(s); //{a, b, c}
  
}

Dart中的函数的创建

函数的声明

Dart声明函数跟Java是比较类似而与js不同,它是不需要function关键字的。

void printInfo(){
  print("Hello World");
}

//返回值要与函数声明的类型一直
int getNum(){
  return 1;
}


void main(List<String> args) {
  printInfo(); //调用函数
  print(getNum());
}

箭头函数(lamda函数)

  • Dart中的箭头函数,函数体只能写一行,不能带有结束的分号。
  • Dart中的箭头函数,只是函数的一种简写形式(不会修改this指向)
  • Dart中的箭头函数如果参数只有一个,是不能省略()的,这和js不一样
void main(List<String> args) {
  List fruits = ['apple','banaana','orange'];
  fruits.forEach((element) => print(element));
  
}

匿名函数

所谓匿名函数就是让一个变量接收一个函数。

void main(List<String> args) {
  var myPrint = (value) => print(value);
  myPrint(12);

}

立即执行函数

void main(List<String> args) {
  //立即执行函数
  ((int n){
    print(n);
  })(17);
}

Dart中函数参数的问题

可选参数与命名参数

代码1 必填参数(如果不传参数就会报错)

void main(List<String> args) {
  String userInfo(String name){
    //必填参数
    return '你好$name';
  }
  String name = userInfo('lebro');
  print(name);
}

代码2 可选参数,格式为[类型 变量 = 默认值]

void main(List<String> args) {
  //可选参数
  String userInfo(String name,[int age = 10]){ //[类型 变量 = 默认值]
    return '你好我是$name,我的年龄是$age';
  }
  String name1 = userInfo('张三');
  String name2 = userInfo('李四',20);
  print(name1);
  print(name2);
}

代码3 命名参数 格式为 {类型 变量 = 默认值} ,与可选参数不同的是,命名参数调用函数的时候必须以键值对的形式跟上变量名和值。

void main(List<String> args) {
  //可选参数
  String userInfo(String name,{int age = 10}){ //[类型 变量 = 默认值]
    return '你好我是$name,我的年龄是$age';
  }
  String name1 = userInfo('张三');
  String name2 = userInfo('李四',age:21);
  print(name1);
  print(name2);
}

Dart函数的闭包

  • Dart中闭包的方式与JS完全一致。它有两个好处,一个是能重用变量,第二个是能保护变量不被污染。
  • 闭包的实现原理:外层函数被调用后,外层函数的作用域对象(AO)被内层函数应用者,导致外层函数的作用域对象无法释放,从而形成闭包。

作用域

image.png 我们只要看这个图就明白了,里层函数能够访问外层函数的变量,但是翻过来不可以,外层函数访问不到里层函数的变量。


var globalNum = 100;
void main(List<String> args) {
  printInfo(){
    var localNum = 10;
    print(localNum);  //10
    print(globalNum); //100
  }
  print(localNum); //访问不了,会报错

  printInfo();
}

闭包案例

void main(List<String> args) {
  parent(){
    var money = 1000;
    return (){
      money -= 100;
      print(money);
    };
  }

  var p = parent();
  p();
}

这里由于一直要用到money这个变量,所以内存中的指针并不会移除,变量能够一直被存储着。不过闭包也会带来内存泄漏的问题,如果我们滥用闭包会对代码的性能产生一些影响。

异步函数

  • Javascript中,异步函数调用Promise来实现异步。或者也可以用async与await函数,async函数返回一个Promise。await用于等待Promise。
  • Dart中,异步调用通过Future来实现,async函数返回一个Futrue,await用于等待Future。
import 'package:http/http.dart' as http;
import 'dart:convert';

// https://httpbin.org/ip 返回ip地址
Future getIPAddress(){
  final url = 'https://httpbin.org/ip';
  return http.get(url).then((response){
    print(response.body);
    String ip = jsonDecode(response.body)['origin'];
    print(ip);
  });
}

void main(List<String> args) {
  //调用getIPAdress
  getIPAddress()
    .then((ip) => print(ip))
    .catchError((error) => print(error));
}


import 'package:http/http.dart' as http;
import 'dart:convert';

// https://httpbin.org/ip 返回ip地址
Future getIPAddress() async{
  final url = 'https://httpbin.org/ip';
  final response = await http.get(url);
  print(response.body);
  String ip = jsonDecode(response.body)['origin'];
  print(ip);
  
}

void main (List<String> args) async {
  //调用getIPAdress
  try {
    final ip = await getIPAddress();
    print(ip);
  } catch (e) {
    print(e);
  }
}