阅读 267

dart语言基础

新手入门

近日开始学习Flutter,自然也就免不了学习dart语言,作为一名C#转前端,时隔多年又用上了强类型语言,配合vscode使用,颇为亲切,仿佛又回到了快乐的winform开发时代。dart学习的话,本人是官网入门教程配合《Flutter 从0到1构建大前端应用》,dart官网的中文文档已经很完善,本文站在前端的角度上会对dart和javascript两种语言做一些对比,使前端同学可以更快的学习dart语言,话不多说,下边直奔主题。

概述

dart是一门单继承强类型语言,有一些细节点无处安放,讲在这里吧

  • 所有类都是Object类的子类
  • dart程序必须有一个主入口函数main(),这和C#是一致的
  • dart2支持省略new关键字,所以你经常会看到这种语句var gifts = Map()
  • 语句结尾不允许省略分号 “;” ,否则编译过程中会报错

基础类型

  • Number

Number类型分为int和double两个具体类型,int值可以赋值给声明为double的变量,反之,会抛出类型错误。

  • String

支持多行字符串,使用'''或"""包裹内容即可,支持变量与字符串连接,直接在变量前加$符号,变量名后空格隔开,对应js中的语法是使用``和${}配合使用

  • Boolean
  • List (也被称为 Array)
  • Map
  • Set
  • Rune (用于在字符串中表示 Unicode 字符)
  • Symbol

内建类型都可以被初始化为字面量,其中List对应js中的Array,但是数组的方法有所不同,具体请参照api文档

关键字

这里没啥可说的,直接截张图,需要具体了解的请移至官方文档, 传送门

变量声明

dart声明变量有以下几种方式:

  • 通过具体类型定义:声明类型在关键字前,变量类型声明后不可更改
String str = '我是字符串';
print(str); // 我是字符串
str = 1; // Error: A value of type 'int' can't be assigned to a variable of type 'String'.
复制代码
  • Object基类定义:可以赋值任何类型,这种做法是不推荐的,开发过程中我们需要尽量为变量确定一个类型
Object obj = '我是对象';
print(obj); // 我是对象
obj = 1;
print(obj); // 1
复制代码
  • var定义:var定义如果不赋值的话,默认值为null,js中默认为undefined。Dart使用var定义时,变量如果声明时被赋值,就会根据值的类型进行类型推断,此变量再次赋值时也必须是第一次值的类型,否则会抛出类型错误,js则可以任意类型赋值
var v;
print(v); // null
v = "v是字符串";
print(v); // v是字符串
v = 1;
print(v); // 1
var v1 = "类型推断";
v1 = 1; // Error A value of type 'int' can't be assigned to a variable of type 'String'
复制代码
  • dynamic类型定义:dynamic表示特殊类型,编译器对其不会做任何类型检查。类似于ts中的any,慎用
dynamic dy;
dy = 1;
print(dy);  // 1
dy = "string";
print(dy);  // string
复制代码
  • const和final定义:使用过程中不会被修改的变量.const的使用方式与js类似,稍有区别在于dart中的const只能赋值常量值,如果将其他定义好的变量赋值给const常量会抛出类型错误,js中因没有类型检查,是可以这样做的。final变量值只能被设置一次,使用过程中与const的区别在于,实例变量可以是final类型,但不能是const类型,话有点多,看🌰
var vc = 1;
const co = vc + 1;  // Not a constant expression. js可以这样做,co会是2
const cL = List(); // Error: Cannot invoke a non-'const' factory where a const expression is expected
final fL = List();
print(fL); // []  
复制代码

多写一个🌰,有关final类型定义问题,final如果直接卸载方法内定义变量,必须在声明时就进行赋值,否则报错,但如果作为class中的字段进行赋值时,可以只声明变量名称,在构造函数中去为该字段赋值

class FinalTest{
final String test;
FinalTest(this.test);
}

main() {
  var c = FinalTest('Final 字段再构造函数中初始化');
  print(c.test);
}
复制代码
  • 最后给一下官方文档变量部分传送门

函数

  • 函数在声明方式上与js有些区别,不需要加function关键字,=>箭头函数表示箭头后只执行一条语句,并返回该语句的返回值,这里与js中的箭头函数是不同的,也不存在js中箭头函数改变this指向等问题
void printString(String str) {
  print(str);
}
void printInt(int num) => print(num);
// 下面这种js写法语法是错误的
double printDouble(double num) => {
  print(num);
  return num;
}  // Expected '}' before this.
复制代码
  • 正常声明函数是的参数调用时是必填的,否则会报参数校验错误
 void printString(String str) {
  print(str);
}
printString();// Error: Too few positional arguments: 1 required, 0 given.
复制代码
  • 支持可选参数
func1({String str1, String str2}) {
  print('输入的值是$str1 $str2');
}
// 此处虚要注意定义函数参数的{}是写法,不是js中的对象
// 调用函数式不需要带{},否则会报错
func1(str1:'aaa'); // 输入的值是aaa null
复制代码
  • 部分参数必填,有两种方式
// @required标识的参数必填,其他选填
const Scrollbar({Key key, @required Widget child})

// []包裹的参数选填,前边两个参数必填
String say(String from, String msg, [String device])
复制代码
  • 支持参数默认值,官方示例,第二种写法不推荐,随时可能废弃,这里了解一下这个写法就可以了
/// 设置 [bold] 和 [hidden] 标志 ...
void enableFlags({bool bold = false, bool hidden = false}) {...}

// bold 值为 true; hidden 值为 false.
enableFlags(bold: true);
复制代码
  • 作用域和闭包:与js用法类似,先不做过多解释了
  • 返回值:与变量声明相似,函数内如果不定义返回值,默认返回null,js中默认返回undefined;

运算符

  • 算术运算符与js基本一致,想深入了解的同学参照官方文档传送门
  • 关系运算符:dart不存在===,使用==和!=进行判断,!取反标识符后必须跟boolean值。官方还提供了identical()方法,用于判断两个引用对象是否指向同一个对象实例。
main() {
  print(1 == '1');  // false
  print('' == 0);  // false
  print('' != null); // true
  print(1 === 1); // Error: The '===' operator is not supported.
}
复制代码
  • 逻辑和条件判断运算符:!expr取反后边表达式必须是bool值,if-else和condition ? expr1 : expr2三位运算符也是如此,条件部分的表达式必须是bool值,js中则可以放各种类型,前端代码写多了容易忽略这里。expr1 ?? expr2运算符,类似于js 中的 expr1 || expr2,expr1为non-null, 返回 expr1 的值; 否则, 执行并返回 expr2 的值。

  • 级联运算符 (..):可以实现对同一对象的一系列操作,除了调用函数,还可以做赋值操作

querySelector('#confirm') // 获取对象。
 ..text = 'Confirm' // 调用成员变量。
 ..classes.add('important')
 ..onClick.listen((e) => window.alert('Confirmed!'));
复制代码
  • ?.运算符,js当前很需要的可选取值运算符,a?.b?.c?.d?.d,避免Cannot read property'XXX'of undefined的出现

Dart的一切都是基于类,Dart中的类单继承,多接口实现,并支持mixin混入。

  1. 继承(extends)。子类继承父类后,可以通过@override重写父类中的方法。构造函数不能被继承。Dart没有私有访问修饰符private和protect,子类可以访问父类所有变量和方法。
  2. 接口(implements)。不支持interface关键字,而是每个类都隐式实现了一个接口,可以当做接口使用,实现一个接口,就要实现接口中所有的方法。
  3. mixin(with)。使用过vue的同学应该对mixin非常熟悉,dart中支持mixin,使得一个类可以使用另一个类中的方法,却不需要成为该类的子类,目前dart2.1引入了mixin关键字,这里的mixin必须继承自Object,且无构造函数,mixin之间的相互继承使用on关键字。
  4. 类本身和这三种方式如果出现同名函数时优先级是:类本身>mixins>extends>implements,综合示例如下
abstract class Animal {
  Animal();
  void move() {
    print('活的动物都能动');
  }
  void breath() {
    print('活的动物都喘气儿');
  }
}

class Person extends Animal {
  // 身份证号,不可变
  final int id;
  Person(this.id);
  @override
  void move() {
    print('两条腿走路');
  }
  void speak() {
    print('做个人吧');
  }
}

class Fish extends Animal {
  swimming() {
    print('我会游泳');
  }
  breath() {
    print('我可以在水里喘气儿');
  }
  void speak() {
    print('做条咸鱼吧');
  }
}

class Bird extends Animal {
  void fly() {
    print('我会飞');
  }
  void speak() {
    print('做个鸟吧');
  }
}

class Aquaman extends Person implements Fish {
  Aquaman(int id):super(id);
  @override
  void swimming() {
    print('我可以拿着叉子游泳');
  }
}

class FlyAquaman extends Aquaman with Bird {
  FlyAquaman(int id): super(id);
  
}

main() {
  Aquaman zhangsan = Aquaman(1234567);
  zhangsan
    ..swimming()
    ..breath()
    ..move()
    ..speak();
   /** 
   我可以拿着叉子游泳
   活的动物都喘气儿
   两条腿走路
   做个人吧
   **/
  
  FlyAquaman lisi = FlyAquaman(7654321);
  lisi
    ..fly()
    ..speak();
   /** 
   我会飞
   做个鸟吧
   **/
}
复制代码
  1. 构造函数:dart中的构造函数通常通过与其类同名的函数来声明,如不定义构造函数dart会隐式提供一个默认的构造函数。dart没有js中的constructor关键字,同时,dart还支持命名构造函数,下边看例子
// 类同名构造函数
class Point {
  num x, y;
  Point(num x, num y) {
    this.x = x;
    this.y = y;
  }
}

class Point {
  num x, y;
  // 语法糖
  Point(this.x, this.y);
  // 命名构造函数
  Point.origin() {
    x = 0;
    y = 0;
  }
}
// 工厂构造函数,当执行构造函数并不总是创建这个类的一个新实例时
// 想了半天没想出来有什么更简单的例子,还是官网这个吧
class Logger {
  final String name;
  bool mute = false;

  // 从命名的 _ 可以知,
  // _cache 是私有属性。
  // static静态变量,不用初始化类就可以调用,静态变量和方法会直接占用内存空间
  static final Map<String, Logger> _cache =
     <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

复制代码

泛型

强类型语言一般都存在泛型的概念,其主要目的是加强类型安全及减少类转换的次数,实现代码复用,可以把它理解为一个类型参数,使用时传入具体类型,在运行时泛型类型参数就已经确定。

var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
    'index.html': 'Homepage',
    'robots.txt': 'Hints for web robots',
    'humans.txt': 'We are people, not machines'
  };
复制代码

了解这些内容后,差不多就可以着手开发Flutter实例了。水平有限,如文章哪里有问题,欢迎评论指正,感谢。

参考

关注下面的标签,发现更多相似文章
评论