Dart入门学习笔记

524 阅读12分钟

学习计划

  • Dart的概念,使用背景了解
  • Dart基础语法学习, 变量、内建类型、函数
  • Dart的重要语法 类、接口、mixins特性等
  • Dart的重要语法 泛型、类型定义
  • Dart的重要语法 库的实现、Dart常用库、异步支持的使用
  • Dart与JavaScript、TypeScript类型系统的异同对比

Dart概念

  • Dart 是一门新的面向对象编程语言,你可以将它当做 JAVA,亦可以当做 JavaScript;它比 JAVA 简单,易于理解,比JavaScript 更加规范,更加工程化
  • Google出品,Google 内部用 Dart 编写孵化了一个移动开发框架Sky,之后又被命名为 Flutter,在 Google 的未来操作系统 Fuchsia 中,Dart 被指定为官方的开发语言。
  • Dart 属于应用层编程语言,它有自己的 DartVM 引擎
  • Dart2.0以上成为更加安全的强类型语言
  • 使用Dart开发三个方向: 移动开发、DartVM 命令行程序(Server 端)、浏览器(前端)

Dart的特性

  • 单进程异步事件模型;
  • 强类型,可以类型推断;
  • DartVM,具有极高的运行效率和优秀的代码运行优化,根据早前的基准测试,性能比肩 Java7 的JVM;
  • 独特的隔离区( Isolate ),可以实现多线程;
  • 面向对象编程,一切数据类型均派生自 Object ;
  • 运算符重载,泛型支持;
  • 强大的 Future 和 Stream 模型,可以简单实现高效的代码;
  • Minix 特性,可以更好的实现方法复用;
  • Null 安全操作,如 NullableObject?.dosth();
  • 全平台语言,可以很好的胜任移动和前后端的开发。
  • 在语法上,Dart 提供了很多便捷的操作,可以明显减少代码量。比如字符连接,可以直接 "my name is name,ageisname, age is age",无需+号拼接,也无需做类型转换。
  • ...不再赘述

Dart语言基础

Dart 变量
dart是一个强大的脚本类语言,可以不预先定义变量类型 ,自动会类型推断
dart中定义变量可以通过var关键字、可以通过类型来申明变量如:

var str='this is var';

String str='this is var';

int str=123;
 
Object name = 'Bob';
dynamic name = 'Bob'; //如果对象不限定为单个类型,可以指定为 对象类型 或 动态类型

注意:var 后就不要写类型 ,写了类型 不要var  两者都写 
 var  a int  = 5;  报错

未初始化的变量默认值是 null

int lineCount;
assert(lineCount == null);
Dart 常量:final 和 const修饰符
// const值不变 一开始就得赋值

// final 可以开始不赋值 只能赋一次 ; 而final不仅有const的编译时常量的特性,最重要的它是运行时常量,并且final是惰性初始化,即在运行时第一次使用前才初始化
 
// 永远不改量的量,请使用final或const修饰它,而不是使用var或其他变量类型。 
 
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';
 
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
Dart数据类型
// String、Number(int、double)、Boolean 、List、Map、Set、Rune(字符)、Symbol(符号)(最后两种几乎用不到)

// 使用带有单引号或双引号的三重引号 ''' 表示多行字符串

// 用r前缀创建一个“原始”字符串(常用于正则表达式字符串): 
var s = r'In a raw string, not even \n gets special treatment.';

// $variableName (或 ${expression}) 字符串拼接
var str = "my name is $name, age is $age";

int a=123;
double b=23.5;
num c=123; // 其相当于 int 与 double 的联合类型

/*
List、Set里面常用的属性和方法:
    常用属性:
        length          长度
        reversed        翻转
        isEmpty         是否为空
        isNotEmpty      是否不为空
    常用方法:  
        add         增加
        addAll      拼接数组
        indexOf     查找  传入具体值
        remove      删除  传入具体值
        removeAt    删除  传入索引值
        fillRange   修改   
        insert(index,value);            指定位置插入    
        insertAll(index,list)           指定位置插入List
        toList()    其他类型转换成List  
        join()      List转换成字符串
        split()     字符串转化成List
        
映射(Maps)是无序的键值对:
    常用属性:
        keys            获取所有的key值
        values          获取所有的value值
        isEmpty         是否为空
        isNotEmpty      是否不为空
    常用方法:
        remove(key)     删除指定key的数据
        addAll({...})   合并映射  给映射内增加属性
        containsValue   查看映射内的值  返回true/false

List、Set、Map被称为集合类型,有以下共同的属性方法
        isEmpty         是否为空
        isNotEmpty      是否不为空
        foreach
        map
        where => 相当于JS的filter方法
        any => 相当于JS的some方法
        every
*/

Dart运算符、操作符、条件表达式、循环语句
Dart运算符、操作符、条件表达式、循环语句等与js的大同小异

is 运算符进行类型判断 ,使用 as 运算符将对象强制转换为特定类型
print(p is Object);

// 如果b为空时,将变量赋值给b,否则,b的值保持不变。(?? 、?.对象操作符Dart中也有,与JS相似)
b ??= value; 

// Number类型转换成String类型 toString()
// String类型转成Number类型  int.parse()

// == 、if(...)等表达式中 类型不会进行隐式转换, 这一点和JS中是完全不同的,需要明确的进行值检查
// 检查空字符串。
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);
函数
// void:没有显示声明返回值
void func(){}

// 如果一个函数没有显式的 return,那么它默认会返回 null。
fun(){}
assert(fun() == null)

// 可选参数可以是命名参数或者位置参数,但一个参数只能选择其中一种方式修饰
void enableFlags({bool bold, bool hidden}) {...}
enableFlags(bold: true, hidden: false);

void enableFlags(bool bold, [bool hidden]) {...}
enableFlags(true,false);


// 以下代码中的 .. 语法为 级联调用 (cascade)。 使用级联调用, 可以简化在一个对象上执行的多个操作(就像jquery中的链式调用)
void main() {
  querySelector('#sample_text_id')
    ..text = 'Click me!'
    ..onClick.listen(reverseText);
}

// 如果函数中只有一句表达式(只能是一条语句),可以使用简写语法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

Dart语言进阶

Dart所有的东西都是对象,所有的对象都继承自Object类。

Dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类

一个类通常由属性和方法组成,与es6的类概念大同小异

class Person{
/*
  Dart和其他面向对象语言不一样,Data中没有 public  private protected这些访问修饰符
  但是我们可以使用_把一个属性或者方法定义成私有。
 */
  String _sex;   //私有属性
  
  // 使用static 关键字来实现类级别的变量和函数
  // 静态方法不能访问非静态成员,非静态方法可以访问静态成员
  static int height = 170;
  static void show() {
     print(height);
  }
  
  String name;
  int age; 
  // 默认构造函数 以及初始化实例变量
  // Person(String name,int age):name='张三',age=10{
  //    this.name=name;
  //    this.age=age;
  // }
  //默认构造函数的简写
  Person(this.name,this.age);
  
  Person.now(){
    print('我是命名构造函数');
  }
  void printInfo(){   
    print("${this.name}----${this.age}");
  }
  void _run(){
    print('这是一个私有方法');
  }
  execRun(){
    this._run();  //类里面方法的相互调用 外部可通过调用公共方法间接的调用私有方法
  }
}
// Person p1= Person('张三', 20);   //默认实例化类的时候调用的是 默认构造函数
// Person p1= Person.now();   //调用命名构造函数

/*
 Dart中的类的继承:  
  1、子类使用extends关键词来继承父类
  2、子类会继承父类里面可见的属性和方法 但是不会继承构造函数
  3、子类能复写父类的方法 getter和setter
*/
class Person {
  String name;
  num age; 
  Person(this.name,this.age);
  Person.xxx(this.name,this.age);
  void printInfo() {
    print("${this.name}---${this.age}");  
  }
}

class Web extends Person{
  String sex;
  //实例化子类给父类构造函数传参
  Web(String name, num age) : super(name, age);
  
  //实例化子类给父类命名构造函数传参
  Web(String name, num age,String sex) :    super.xxx(name, age){
    this.sex=sex;
  }
  
  run(){
    print('run');
    super.work();  //子类调用父类的方法
  }
  
  //覆写父类的方法
  @override       //可以写也可以不写  建议在覆写父类方法的时候加上 @override 
  void printInfo(){
     print("姓名:${this.name}---年龄:${this.age}"); 
  }
}
抽象类、接口、mixins、枚举类型

Dart中抽象类: Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。

  1. 抽象类通过abstract关键字来定义
  2. Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
  3. 如果子类继承抽象类必须得实现里面的抽象方法
  4. 如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
  5. 抽象类不能被实例化,只有继承它的子类可以 (如果希望抽象类能够被实例化,那么可以通过定义一个 工厂构造函数 来实现)

extends抽象类 和 implements的区别:

  1. 如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extends继承抽象类
  2. 如果只是把抽象类当做标准的话我们就用implements实现抽象类
abstract class Animal{
  eat();   //抽象方法
  run();  //抽象方法  
  printInfo(){
    print('我是一个抽象类里面的普通方法');
  }
}

class Dog extends Animal{
  @override
  eat() {
     print('小狗在吃骨头');
  }

  @override
  run() {
    // TODO: implement run
    print('小狗在跑');
  }  
}
class Cat extends Animal{
  @override
  eat() {
    // TODO: implement eat
    print('小猫在吃老鼠');
  }

  @override
  run() {
    // TODO: implement run
    print('小猫在跑');
  }
}

和Java一样,dart也有接口,但是和Java还是有区别的。 首先,dart的接口没有interface关键字定义接口,而是普通类或抽象类都可以作为接口被实现。

同样使用implements关键字进行实现。

但是dart的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。

而因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像Java接口那样的方式,一般会使用抽象类。

建议使用抽象类定义接口

abstract class Db{   //当做接口   接口:就是约定 、规范
    String uri;      //数据库的链接地址
    add(String data);
    save();
    delete();
}

class Mysql implements Db{
  
  @override
  String uri;

  Mysql(this.uri);

  @override
  add(data) {
    // TODO: implement add
    print('这是mysql的add方法'+data);
  }

  @override
  delete() {
    // TODO: implement delete
    return null;
  }

  @override
  save() {
    // TODO: implement save
    return null;
  }

  remove(){
      
  } 
}

class Mongodb implements Db{
  @override
  String uri;
  @override
  add(String data) {
    print('这是Mongodb的add方法'+data);
  }

  @override
  delete() {
    // TODO: implement delete
    return null;
  }

  @override
  save() {
    // TODO: implement save
    return null;
  }
}

Mysql mysql=new Mysql('xxxxxx');
mysql.add('1243214');

mixins的中文意思是混入,就是在类中混入其他功能。

在Dart中可以使用mixins实现类似多继承的功能

因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Dart2.x中使用mixins的条件:

  1. 作为mixins的类只能继承自Object,不能继承其他类
  2. 作为mixins的类不能有构造函数
  3. 一个类可以mixins多个mixins类
  4. mixins绝不是继承,也不是接口,而是一种全新的特性
  5. 如果 Mixin 不希望作为常规类被使用,使用关键字 mixin 替换 class
class A {
  String info="this is A";
  void printA(){
    print("A");
  }
}

class B {
  void printB(){
    print("B");
  }
}

class C with A,B{
}

/*
mixins的实例类型是什么?
很简单,mixins的类型就是其超类的子类型。
*/
var c=new C();  
print(c is C);    //true
print(c is A);    //true
print(c is B);   //true

枚举类型也称为 enumerationsenums , 是一种特殊的类,用于表示数量固定的常量值。

使用枚举

enum Color { red, green, blue }

枚举中的每个值都有一个 index getter 方法, 该方法返回值所在枚举类型定义中的位置(从 0 开始)。 例如,第一个枚举值的索引是 0 , 第二个枚举值的索引是 1。

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

使用枚举的 values 常量, 获取所有枚举值列表( list )。

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);

枚举类型具有以下限制: 枚举不能被子类化,混合或实现。 枚举不能被显式实例化。

泛型、类型定义

泛型(参数化类型)就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)

String getData1(String value){
    return value;
}

int getData2(int value){
   return value;
}
  
//同时支持返回 string类型 和int类型
getData<T>(T value){
      return value;
}
print(getData<int>(12));

// 在实现泛型类型时,您可能希望限制其参数的类型。你可以使用extends。
class Foo<T extends SomeBaseClass> {
  // Implementation goes here...
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {...}

// 可以使用SomeBaseClass 或它的任何子类作为泛型参数:
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();

//也可以不指定泛型参数:
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'

//指定任何非somebaseclass类型都会导致错误:
var foo = Foo<Object>();

类型定义, 类型别名typedef,类似于 ts 的 type不过只能用于方法

typedef int Add(int x, int y);

main() {
  Add add = (x, y) => x + y;

  var sub1 = add(1, '2');  // Error: 不能将 String 类型的参数赋值给 int 类型的参数

  var sub2 =  add(1, 2);  // -> 3
  print(sub2 is int);     // -> true

}

上面定义了一个名为Add的方法类型别名,其作为方法 add 的类型,因为 sub1 传入的参数类型与 Add 定义的不符,所以报错了。 因为 Add 定义了 add 方法的返回值是 int 类型,所以 (sub2 is int) 为 true

在Dart中,库的使用时通过import关键字引入的。

library指令可以创建一个库,每个Dart文件都是一个库,即使没有使用library指令来指定。

Dart中的库主要有三种: 1.我们自定义的库

  import 'lib/xxx.dart';

2.系统内置库

  import 'dart:math';    
  import 'dart:io'; 
  import 'dart:convert';

3.Pub包管理系统中的库

pub.dev/packages pub.flutter-io.cn/packages pub.dartlang.org/flutter/

3.1 需要在自己想项目根目录新建一个pubspec.yaml 3.2 在pubspec.yaml文件 然后配置名称 、描述、依赖等信息 3.3 然后运行 pub get 获取包下载到本地
3.4 项目中引入库 import 'package:http/http.dart' as http; 看文档使用

// 命名冲突重命名 
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

Element element1 = new Element();           // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.

// 部分导入
import 'package:lib1/lib1.dart' show foo; // 只导入需要的部分,使用show关键字
import 'package:lib2/lib2.dart' hide foo; // 隐藏不需要的部分,使用hide关键字

// 延迟加载(懒加载),使用deferred as关键字
import 'package:deferred/hello.dart' deferred as hello;

// 当需要使用的时候,需要使用loadLibrary()方法来加载:
 greet() async {
      await hello.loadLibrary();
      hello.printGreeting();
 }

库的拆分,partpart of 参考 juejin.cn/post/684490…

异步支持

Future => 与ES6 Promise完全相同

Stream => 一般用于 文件上传、持续请求加载等操作

与JavaScript、TypeScript的异同

参考 Dart vs JavaScript vs TypeScript

Dart踩坑日志

待更新....