一、前言
学习前端快三年了,一直不喜欢做笔记,某块知识忘了以后就去重复刷视频查资料。但是自从实习以后,发现重复看视频特别浪费时间,而且学了新的知识很快就忘掉了,再加上平时工作比较忙,所以感觉特别吃力.
最近一直在学习关于跨端的东西,准备尝试做做笔记。第一可以增加自己学的印象,第二方便自己复习。
二、Dart介绍
Dart是一门由谷歌开发的计算机编程语言,可以被用于web、移动应用、服务器和物联网等领域的开发。
Dart诞生于2011年,号称要取代JavaScript,当时在过去的几年中一直不温不火,知道2015年Sky框架的出现(后更名为Flutter),才被人们重新重视。
所以我们要学习Flutter,就必须学会Dart,就像我们想学习React,就一定要会JavaScript一样。
三、安装SDK
JavaScript的SDK生来就存在与计算机中,所以在学习JavaScript的时候没有安装SDK这一步。但是计算机中默认是没有Dart的SDK的,所以我们需要先安装Dart的SDK。
官方安装教程:dart.dev/get-dart
windows建议下载安装包进行安装,mac直接使用brew安装即可。
安装成功后打开命令行工具,执行dart --version查看dart版本号,没报错的话就是安装成功了。
四、开发工具
开发工具有很多,VSCode、WebStorm、Atom,选择一个自己喜欢的就行,我使用的是VSCode进行开发。
VSCode官网:code.visualstudio.com/
推荐插件:
-
Dart: 代码提示工具。
-
Code Runner: 可以方便地运行Dart代码。
五、Hello World
通过上面的操作,我们的Dart环境就算准备好了,接下来我们开始愉快的使用dart进行编程吧!
首先从一个简单的Hello World开始。
新建文件夹与文件:
mkdir dart;
cd dart;
touch index.dart;
在index.dart中书写以下代码:
void main() {
print('hello world');
}
右键选择Run Code运行代码,可以在控制台看到打印hello world。
main是一个入口函数,所有的代码都要在这个函数里面运行。
六、注释
dart中的注释和JavaScript中的注释差不多,有以下三种:
-
单行注释:
// xxx(cmd + /) -
多行注释:
/* xxx */(shift + option + A) -
文档注释:
/// xxx(暂时没发现有快捷键,可以连续按三次/😂)
七、数据类型
Dart中的数据类型有:int(整型)、double(浮点型)、num(数值)、String(字符串)、bool(布尔)、List(数组)、Map(对象)
Dart中对象的键和值可以是任何类型,相同的键只能出现一次,相同的值可以出现多次,类似于ES6中的Map
八、变量声明
dart声明变量分为两种:预先定义类型、不预先定义类型;
- 预先定义类型:使用数据类型声明变量
举个栗子🌰:
int a = 123;
double b = 123.4;
String c = 'ancd';
bool d = true;
List<int> e = [1, 2, 3];
List <String> f = ['a', 'b', 'c'];
Map<String, int> e = {'a': 1, 'b': 2};
Map<int, String> f = {1: 'a', 2, 'b'};
基本就是上面这样👆🏻,把类型写在变量名的前面,变量的值要符合对应的类型,否则会报错。可以对变量重新赋值。
- 不预先定义类型:使用var、dynamic、const、final声明变量
举个栗子🌰:
// 赋值为123后,a的类型便确定为int,后面对a进行赋值时,只能赋值int类型的数据
// 注意:dart中的var和js里的var并不一样,dart中的var没有变量提升的问题
var a = 123;
// 赋值后可以重新赋任何类型的值(不建议使用)
dynamic b = 123;
b = 'abc';
// 赋值后c不可更改
const c = 123;
const int d = 123;
// 赋值后d不可更改
final e = 123;
const和final的区别:
- const
-
声明的时候就需要立即赋值。
-
只能赋已知的值,不可以赋通过函数运行得到的值(编译时常量)。如
const num = getNum();是错误的❌。
- final
-
声明的时候不需要立即赋值,但只能赋值一次。
-
可以赋已知的值,也可以赋通过函数运行得到的值(运行时常量)。如
final num = getNum();是正确的✅。
九、类型判断
const a = 123;
print(a is int); // true
const b = 'abc';
print(b is String); // true
十、List类型
常用属性
length 长度
reversed 翻转
isEmpty 是否为空
isNotEmpty 是否不为空
常用方法
add 增加
addAll 拼接数组(改变原数组)
indexOf 查找下标(不存在则返回-1)
remove 删除(出入具体的值)
removeAt 删除(传入索引)
inset(index, value) 在指定位置插入
insertAll(index, list) 在指定位置拼接一个数组
toList 其他类型转换成List
join List转换成字符串
split 字符串转换成List
forEach
map 和js的map一样
where 和js的filter一样
十一、Map类型
Map是无序的键值对
常用属性
keys 获取所有的key值
values 获取所有的value值
isEmpty 是否为空
isNotEmpty 是否不为空
常用方法
remove(key) 删除指定key的键值对(改变原数组)
addAll 合并映射
ForEach
十二、方法定义
自定义方法基本格式:
返回类型 方法名称 (参数1, 参数2, ...) {
...
return 返回值;
}
位置可选参数
参数3与参数4为可选参数,想要传递参数4就需要先把参数3传进去,所以叫位置可选参数
返回类型 方法名称 (参数1, 参数2, [参数3, 参数4]) {
...
return 返回值;
}
命名可选参数
参数3、参数4为命名可选参数(flutter中的Widget大部分都是这种命名可选参数)
返回类型 方法名称 (参数1, 参数2m, { 参数3, 参数4 }) {
...
return 返回值;
}
参数默认值
可选参数可以拥有默认值
返回类型 方法名称 (参数1, 参数2, { String 参数3 = 'abc', 参数4 }) {
...
return 返回值;
}
箭头函数
部分函数可以简写为箭头函数
/* 打印数组的值 */
List list = ['苹果', '香蕉', '西瓜'];
list.forEach((value){
print(value);
});
list.forEach((value) => print(value));
/* 将数组进行映射 */
List list = [4, 1, 2, 3, 4];
List newList = list.map((value){
if(value > 2){
return value * 2;
}
return value;
}).toList();
List newList = list.map((value) => value > 2 ? value * 2 : value).toList();
十三、面向对象三大特性
封装
封装是把客观的事物封装成抽象的类,并把自己的部分属性和方法提供给其他对象调用, 而一部分属性和方法则隐藏。
继承
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
多态
允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
一个类通常由属性和方法组成
List list = new List();
list.isEmpty;
list.add('香蕉');
list.add('香蕉1');
Map m = new Map();
m["username"] = "张三";
m.isEmpty;
m.addAll({"age":20});
十四、自定义类
默认构造函数
class Person {
late String name;
late int age;
// 构造函数,构造实例的时候触发
Person (String name, int age) {
this.name = name;
this.age = age;
}
void sayName () {
print(this.name);
}
}
class Person {
late String name;
late int age;
// 构造函数简写
Person (this.name, this.age);
sayName () {
print(this.name);
}
}
命名构造函数
一个类中可以有多个构造函数
class Person {
late String name;
late int age;
Person (this.name, this.age);
Person.now)( {
print('我是自定义命名构造函数');
}
sayName () {
print(this.name);
}
}
final now = new Person.now(); // 调用命名构造函数
final datetime = new Datetime.now();
pirnt(datetime)
私有化属性
dart不想其他语言有public、private、protected这些修饰符,但是我们可以使用_将一个属性或方法定义成私有,含有私有属性的类必须单独抽成一个文件
// person.dart
class Person {
late String _name;
late int age;
Person(this._name, this.age);
sayName() {
print(this._name);
}
}
// index.dart
import './person.dart';
void main() {
final p = new Person('Bob', 22);
p.sayName();
}
getter 和 setter 修饰符
void main() {
final result = Rect(10, 20);
print(result.area); // 调用getter
result.setWidth = 20; // 调用setter
}
class Rect {
late int width;
late int height;
Rect(this.width, this.height);
get area {
return this.height * this.width;
}
set setWidth (int value) {
print('设置了宽度');
this.width = value;
}
set setHeight(int value) {
print('设置了高度');
this.height = value;
}
}
静态成员
dart中的静态成员:
-
使用static关键字定义静态属性和静态方法
-
静态方法不能访问非静态成员,非静态方法可以访问静态成员
void main() {
Person.printAge();
}
class Person {
late String name;
static int age = 22;
Person(this.name);
static printAge() {
print(age);
}
}
类的继承
-
子类使用extends关键字继承父类
-
子类会继承父类中所有可见的属性和方法,但是不会继承构造函数
-
子类能复写父类的方法
void main() {
final son = new Son();
print(son.lastName);
son.sayLastName();
}
class Person {
late String lastName = 'feng';
void sayLastName() {
print(this.lastName);
}
}
class Son extends Person{
// 重写父类方法
@override
void sayLastName() {
print('my lastName is feng');
}
}
super关键字的使用
当父类构造函数需要接收参数时,子类需要使用super关键词来给父类传参
void main() {
final son = new Son(22);
son.sayLastName();
}
class Person {
late String lastName = 'feng';
late int age;
Person(this.age);
void sayLastName() {
print(this.lastName);
}
}
class Son extends Person {
Son(int age) : super(age);
void sayLastName() {
print('my lastName is feng');
}
}
实例化子类,给父类默认构造函数传参
void main() {
final son = new Son('feng', 22, '女');
print(son.sex);
}
class Person {
late String lastName;
late int age;
Person(this.lastName, this.age);
void sayLastName() {
print(this.lastName);
}
}
class Son extends Person {
late String sex;
Son(String lastName, int age, String sex) : super(lastName, age){
this.sex = sex;
}
void sayLastName() {
print('my lastName is feng');
}
}
实例化子类,给父类命名构造函数传参
void main() {
final son = new Son('feng', 22, '女');
print(son.sex);
}
class Person {
late String lastName;
late int age;
// 默认构造函数
Person(this.lastName, this.age);
// 命名构造函数
Person.xxx(this.lastName, this.age);
void sayLastName() {
print(this.lastName);
}
}
class Son extends Person {
late String sex;
Son(String lastName, int age, String sex) : super.xxx(lastName, age){
this.sex = sex;
}
void sayLastName() {
print('my lastName is feng');
}
}
重写父类方法
class Person {
late String lastName = 'feng';
void sayLastName() {
print(this.lastName);
}
}
class Son extends Person{
// 可以不加@override,但是建议加上,可以增加可读性,同时编辑器可以帮助该方法是否在父类中存在
@override
void sayLastName() {
print('my lastName is feng');
}
}
子类里面调用父类方法
使用super.xxx();
十五、抽象类
抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口
-
抽象类通过abstract关键字来定义
-
抽象方法不能用abstract声明,没有方法体的方法,我们称之为抽象方法
-
如果子类继承抽象方法,就必须实现抽象方法
-
如果把抽象类当做接口事项,就必须实现抽象类里面的所有属性和方法
-
抽象类不能被实例化,只有继承它的子类可以。
extends抽象类 和 implements 的区别:
-
如果要复用抽象类里的方法,并且要用抽象方法约束子类的话我们就用extends继承抽象类。(只需要实现抽象类里的所有抽象方法)
-
如果只是把抽象类当做标准的话,我们就使用implements实现抽象类。(需要实现抽象类里的所有抽象方法与普通方法)
案例:定义一个Animal类,要求它的子类必须包含eat方法
// 抽象类
abstract class Animal {
eat(); // 抽象方法
run() { // 普通方法
print('在跑');
}
}
// 小狗类(extends)
class Dog extends Animal {
@override
eat() {
print('小狗吃骨头');
}
}
// 小猫类(implements)
class Cat implements Animal {
@override
eat() {
print('小猫吃鱼');
}
@override
run() {
print('小猫在跑');
}
}
十六、多态
多态就是父类定义一个方法不去实现,让继承它的子类去实现,每个子类有不同的表现
// 动物抽象类
abstract class Animal {
eat();
late String name;
}
// 小狗类
class Dog extends Animal {
@override
eat() {
print('小狗吃骨头');
}
}
十七、implements实现多个借口
abstract class Pet {
late String name;
}
class Dog implements Animal, Pet {
@override
String name = '狗子';
@override
eat() {
print('小狗吃骨头');
}
}
十八、extends继承多个类
class Animal {
eat() {
print('吃');
}
}
class Pet {
late String name = '名字';
}
class Dog extends Animal with Pet {
}
泛型
Dart中也有泛型,解决了方法、类、接口的复用性
在方法中使用泛型
案例:在一个数组中取出前n项,返回一个新的数组
void main() {
List<int> arr = getNewList<int>([1, 2, 3, 4], 3);
print(arr);
}
List<T> getNewList<T>(List<T> list, int n) {
if (n >= list.length) {
return [...list];
}
List<T> newList = [];
for (int i = 0; i < n; i++) {
newList.add(list[i]);
}
return newList;
}
在类中使用泛型
案例:定义一个类,该类的实例上有一个list数组,可以调用实例上的add方法在list中添加数据,但每个实例中添加的数据要一致。
void main() {
final l = new CreateList<String>();
l.add('a');
l.add('b');
print(l.list);
}
class CreateList<T> {
List<T> list = [];
void add (T value) {
this.list.add(value);
}
}
在接口中使用泛型
Dart中没有interface关键字来定义接口,一般我们会使用 abstract 定义一个抽象类作为接口被实现,然后使用implements实现抽象类,所以在dart中接口泛型的定义方式和类一样。
其他
Dart中的定时器
相当于js中的setTimeout:
import 'dart:async';
void main() {
int n = 0;
final timer = Timer(Duration(seconds: 2), () {
print('定时器执行了');
});
// timer.cancel() 清除定时器
}
相当于js中的setInterval:
import 'dart:async';
void main() {
int n = 0;
final timer = Timer.periodic(Duration(seconds: 1), (timer) {
n++;
print(n);
// n等于5的时候清除定时器
if (n == 5) {
timer.cancel();
}
});
}
参考资料