Dart基础知识
写了挺长一段时间的flutter,却没有系统地学习dart,因此这里整理一下dart语言的基础知识。
相关代码可以直接在dartpad上运行,参考
- dart中文文档,大部分概念都是从官网上整理,建议直接阅读官方教程。
数据类型
dart中,所有能够使用变量引用的都是对象, 每个对象都是一个类的实例。在 Dart 中 甚至连 数字、方法和 null 都是对象
内置类型
num
数字,包含int和double子类
double a = 1.2;
int b = 2;
print(a.floor()); // 数字也是对象,包含对应的方法
num res = a + b;
print(res);
String
字符串,支持在字符串中使用变量和表达式
String name = 'shymean'; // 可以用单引号或双引号
String greet = '$name say hello'; // 可以直接使用$+变量名的形式快速插入变量
String exp = '1 + 1 = ${1 + 1}'; // 可以使用计算表达式
print(greet + '; ' + exp); // 可以通过+号进行字符串拼接
// 可以使用'''或"""符号创建多行字符串
String lines = '''
ひとつぶの涙は
那滴眼泪
何を伝えようとしてこぼれ落ちたの
似要传递什么一般滑下脸颊
'''; // 网易云音乐刚好随机到这首歌~~
// 使用r前缀创建原始字符串,此时\n不会被转义成换行符
String raw = r"In raw string, \n doesn't work.";
bool
布尔值
bool flag = true;
// 条件判断的表达式只接收布尔值true或false,不会将其他表达式转换为布尔值
// 当 Dart 需要一个布尔值的时候,只有 true 对象才被认为是 true。 所有其他的值都是 flase
if(flag) {
print("yes");
}else {
print("noe");
}
List
列表
List list1 = [1,2,3]; // 直接初始化
List list2 = List(3); // 构造函数初始化
for(int i = 0; i < list2.length; ++i){
list2[i] = i;
}
// list常用的一些方法
list1.add(4);
list1.addAll([5,6]);
print(list1); [1,2,3,4,5,6];
print(list1.indexOf(1)); // 0 查询元素的位置,索引从0开始
list1.remove(2); // 将值为2的元素移除
print(list1); // [1,3,4,5,6]
list1.removeAt(2);//将索引值为2的元素移除
print(list1); // [1,3,5,6]
list1.sort((a, b){
return b-a;
});
print(list1); // [6,5,3,1]
Map
字典
var code = "code"; // key可以是任何类型的对象
Map response = {
code: 0,
"message": "success",
"data": {
"list": [1,2,3] // value也可以是任何类型的对象
},
};
print(response[code]); // 根据key去访问value
print(response['error']); // 访问不存在的key,返回null
var key1 = "key1";
response[key1] = 'add key'; // 新增key
response['key2'] = 'add key2';
Function
函数
// 常规的函数声明方式
bool fn1(num i) {
return i > 10;
}
// 可以不用显式声明参数类型和返回值
fn2(i) {
return i * 2;
}
// 箭头函数
Function fn3 = (i) => i * 2;
// 函数作为一等公民,可以通过表达式声明函数,且可以将函数赋值给其他变量
Function fn4 = () {
print("hello fn4");
};
Function fn5 = fn4;
// IIFE
(num i){
print(i>2);
}(10);
// 可选位置参数,将可选参数放在参数列表最后一个位置的[]中
fn6(x, [y]){
if(y == null){
return x;
}else {
return x*y;
}
}
fn6(10, 2); // 20
fn6(10); // 10
// 可选命名参数
fn7({int x, int y}){
print("x:$x, y:$y");
}
fn7(x:10, y:2); // x:10, y:2
fn7(x:10); // x:10, y:null
fn7(y:2); // x:null, y:2
// 默认参数
fn8([x=1,y = 2]){
print("x:$x, y:$y");
}
fn8(10); // x:10, y:2
fn9({x= 1, y=2}){
print("x:$x, y:$y");
}
fn9(y:10); // x:1, y:10
表达式
除了JS中常见的表达式,还新增了一些新的比较有用的表达式
print(5/2); // 2.5,除
print(5~/2); // 2 整除
num a = 10;
print(a is int); // true
print(a is num); // true
print(a is double); // fase
print(a as int); // 类型转换,注意强制类型转换会报错
var s1 = '123';
var s2 = s1 ?? "default"; // 123,如果s1为null,则将其赋值为默认值
print(s2);
var m1 = 1;
var name = m1?.toString(); // 只有当m1不为null时调用toString
assert(10 > 9); // 断言,如果条件表达式不满足条件,则会打断代码的执行
assert((){
// 断言只在检查模式下运行有效,因此可以通过断言来完成特定环境的判断,如增加测试逻辑等
return true;
}());
作用域
常规作用域
Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了,基本上大括号里面定义的变量就 只能在大括号里面访问。
由于函数可以通过传参或者返回值的形式在其他地方调用,根据其作用域的限制,也会产生闭包。不管该函数在何处被调用, 它都可以访问其作用域内 的变量。
int a = 1;
nest1(){
print(a);// 可以访问到其作用域内的变量,此时打印1
nest2(){
int a = 100; // 内部作用域声明的同名变量会覆盖外部作用域声明的变量
nest3(){
print(a); // 闭包会维持当前的作用域
}
return nest3; // 将函数通过返回值的形式导出
}
var fn = nest2();
fn(); // 函数在其定义作用域外部调用,仍旧可以访问其作用域内的变量,此时打印100
}
nest1();
// 无法在作用域外访问到nest2和nest3
此外注意dart内不存在JavaScript中声明提前的问题。
库
每个 Dart 文件 都可以当做一个库。使用 import 和 library 指令可以帮助你创建 模块化的可分享的代码。
库不仅仅提供 API, 还是一个私有单元:以下划线 (_) 开头的标识符只有在库 内部可见。
// a.dart
int _count = 0;
const VERSION = "1.0";
void hello(){
print("hello ${++_count} times from a.dart");
}
class Util {
void test(){
print("test in ...");
}
}
然后在其他文件中就可以引入该库了
import 'a.dart';
print(VERSION); // 直接访问库的变量
hello(); // 直接访问库的方法
Util u = Util(); // 访问库中的类
// print(_count); // 无法访问到库中的私有变量
// 如果多个库存在相同的变量或方法,则可能造成命名冲突,可以在引入时指定命名空间
import 'a.dart' as moduleA;
print(moduleA.VERSION);
moduleA.hello();
moduleA.Util u = moduleA.Util();
类
// 包含抽象方法,需要使用abstract定义为抽象类
abstract class Animal {
num age;
String name;
// 定义了一个与类名相同的方法就相当于定义了构造函数
// Animal(age, name){
// this.age = age; // 只有当名字冲突的时候才使用 this
// this.name = name; // dart推荐忽略new和this
// }
// 由于上面通过构造函数初始化数据的场景十分常见,因此提供了下面的两种构造函数语法糖
// Animal(this.age, this.name);// 位置参数
Animal({this.age, this.name}); // 可选命名参数
// 命名构造函数
Animal.fromJson(Map json) {
age = json['age'];
name = json['name'];
}
walk() {
print("$name walking.");
}
// 抽象函数,由子类实现
void eat();
}
// 定义一个类继承 Object,该类没有构造函数, 不能调用 super ,则该类就是一个 mixin
class Pet {
String color;
void show() {
print("I have $color color");
}
}
// 继承Animal,注入Pet混合
class Cat extends Animal with Pet {
String food; // 扩展父类属性
static int dieAge = 100; // 静态属性
// 构造函数不会被继承,需子类自己实现,通过super可调用父类的构造函数
Cat({color, age, name, this.food}) : super(age: age, name: name) {
print("parent constructor");
this.color = color;
}
Cat.fromInit(Map json) : food = json['food'] {
this.color = json['color'];
print("init");
}
// 子类覆盖父类的方法
walk() {
print("I‘m a cat");
super.walk(); // 调用父类的方法
print("oh~~");
}
// 子类实现父类的抽象方法
eat() {
print("$name eat food: $food");
}
// 静态方法
static die(){
// 静态方法中不能调用实例属性和方法
print("cat die in age:$dieAge");
}
}
// Animal animal = new Animal.fromJson({"name":"xx", "age":12}); // 抽象类无法被实例化
Cat cat = Cat(age: 1, color: "red", name: "Tom", food: "fish"); // 调用构造函数,可以省略new
cat.walk(); // 调用实例方法
cat.eat(); // 调用实现的父类抽象方法
cat.show(); // 调用混合的方法
cat.name; // 每个属性都包含了一个默认的getter
cat.name = "Jimmy"; // 如果变量不是final,则可以调用setter
异步支持
除了回调函数之外,dart也支持通过Future控制异步流程,Future与JavaScript中的Promise非常相似。
print("start testAsync");
Future.delayed(new Duration(seconds: 1),(){
return true;
// throw AssertionError("Error");
}).then((data){
//执行成功会走到这里
print(data);
}).catchError((e){
//执行失败会走到这里
print(e);
}).whenComplete((){
//无论成功或失败都会走到这里
print("done");
});
此外,dart也支持asyn和await控制异步,其使用方式与JavaScript也基本类似
void test() async{
String res = await Future.delayed(new Duration(seconds: 2),(){
return "finish";
});
print(res);
}
小结
dart除了flutter之外,还有很多其他的应用,如服务端、脚本、web上都可以使用,这是一门非常有趣的语言,可以进一步深入一下,多学点东西总是没坏处的~
你要请我喝一杯奶茶?
版权声明:自由转载-非商用-保持署名和原文链接。
本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。