Dart基础

217 阅读8分钟

一. 重要概念

  • 1.所有变量都是引用的对象,而对象都是某个类的实例.数字,函数,null皆为对象. 所有类都继承自Object.
  • 2.Dart可以类型推断, 也可以指定类型.
  • 3.Dart没有类似于Java的public,protected和private成员访问限定符, 而是采用下划线(_)标识私有.
  • 4.Dart中表达式和语句有区别.表达式有值而语句没有值.比如条件表达式 expression condition ? expr1 : expr2 中含有值 expr1 或 expr2。与 if-else 分支语句相比,if-else 分支语句则没有值.
    1. Dart中未初始化的变量,其默认值都是null.

二. 数据类型

1. final和const

const相对于final,更加严格.

  • 1.final和const都是声明处赋值, 但是final的赋值和var的赋值是一样的,const的赋值就必须是编译时常量,不是那么随意.
  • 2.final的引用对象内的属性是可变的,但是引用对象本身是不可以变的. Dart中const不能更改对象内的属性值;
  • 3.声明类成员变量的时候,const的前面必须要有static

2. 内置类型

1.Number类型

Number包含int, double类型;

int整数值;长度不超过 64位,具体取值范围依赖于不同的平台。在 DartVM 上其取值位于 -263 至 263 - 1 之间。编译成 JavaScript 的 Dart 使用 JavaScript 数字,其允许的取值范围在 -253 至 253 - 1 之间。

64位的双精度浮点数字,且符合 IEEE 754 标准。

2.String类型

Dart的字符串是UTF-16编码的字符串序列.

    1. 单引号和双引号都能创建字符串
    1. ${表达式}可以在字符串中插入值, 如果表达式是单个的标识符(比如一个单独的变量),就能省略{}
    1. +号可以拼接字符串, 三引号或者三个双引号能创建多行字符串
    1. 字符串前加r就能创建"raw"字符串(字符串不被做处理)
    1. 声明编译时常量的字符串时, 插值表达式需是编译时常量(null,String,Number,Boolean)
3.Boolean类型

Dart中应该显式的判断,不能再用js中比如
var str = '';
if (str) {xxx} else {xxx}
而是应该用if (str.isEmpty) {xxx} else {xxx}

Dart 使用 bool 关键字表示布尔类型,布尔类型只有两个对象 true 和 false,两者都是编译时常量。

4.List类型

类似于JavaScript的数组

  • 1.类型被推断后,再次加入新的类型数据,会报错.
  • 2.提供了扩展符(...)和null-aware(...?),类似于JavaScript的(...)运算符, null-aware是防止被解构变量为null
  • 3.提供了if或者for来构建集合.
var listOfInts = [1, 2, 3];
var listOfStrings = [
  '#0',
  for (var i in listOfInts) '#$i'
];

var nav = [
  'Home',
  'Furniture',
  'Plants',
  if (promoActive) 'Outlet'
];
5.Set类型

set 是一组特定元素的无序集合.也是不允许存在重复值. 声明Set:

// 字面量方式生成
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
// 类型+{}的形式创建Set。
var names = <String>{};
// 声明类型变量的形式创建Set
Set<String> names = {};
6.Map类型

类似于JavaScript的map.

声明Map的2种方式:

// map字面量的方式
var gifs = {
	// 键:    值
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};
// Map构造器的方式
var gifs = Map();
gifs['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
7.Runes类型

在 Dart 中,runes 公开了字符串的 Unicode 码位。 从Dart2.6开始,可以使用characters包来访问和操作runes

8. Symbol类型

一个Symbol对象表示一个dart声明的标识符或者运算符.平时写代码用的少,暂略.

三. 函数

1. 函数声明

Dart函数大体类似JavaScript函数; 属于Function类型;所有函数皆有返回值,如不写则会隐式的加上return null;声明方式如下:

    1. 函数返回值可以省略,但Dart是类型化的语言,返回值最好写上,无返回值可写void
    1. 参数前加@required, 则为必须参数
    1. []内为可选参数
    1. 可以用 "=" 号来提供函数的参数默认值.
    1. 匿名函数不同于JavaScript的声明, 省去了function关键字,或者箭头函数的箭头,如下所示
// 具名函数的声明
[返回值] 函数名(type param1 = xxx, @required type param2, [type param3, type param4...]) 
{
	//函数体
}

// 匿名函数的声明
([[Type] param1[, …]]) {
  codeBlock;
};
// 匿名函数例子
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

2.词法作用域

Dart是词法作用域,变量的作用域是固定的, 即在编写代码的时候, 变量的作用域就确定了.

3.闭包

闭包就是对一个函数对象,调用函数对象并未在其词法作用域内,但依然可以访问到函数对象词法作用域内的变量.

Function makeAdd(int n2) {
  return (int n) => n2 + n;
}

void main() {
  Function func1 = makeAdd(2);
  Function func2 = makeAdd(3);
  print(func1(4));// 6
  print(func2(8));// 11
}

四.运算符

1.算数运算符

那算符备注示例
~/取余整数print(5 ~/ 2);//结果为2
is类型判断var p1 = '123123';print(p1 is String); // 结果为true
is!类型判断同is相反
as强制类型转化var p1 = '123123';String p2 = p1 as String;print(p2 is String);// 结果为true
??=当被赋值变量为null时才赋值

五.控制流程语句

1. try catch finally

和 Java 有所不同, Dart 中的所有异常是非检查异常(运行时异常,编译阶段不会检查异常)。 方法不会声明它们抛出的异常, 也不要求捕获任何异常。 Dart 提供了 Exception 和 Error 类型, 以及一些子类型。 当然也可以定义自己的异常类型。 但是,此外 Dart 程序可以抛出任何非 null 对象, 不仅限 Exception 和 Error 对象。

catch可以指定类型,捕捉特定类型的错误,不指定类型的,捕捉所有类型的错误.

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // 一个特殊的异常
  buyMoreLlamas();
} on Exception catch (e) {
  // 其他任何异常
  print('Unknown exception: $e');
} catch (e) {
  // 没有指定的类型,处理所有异常
  print('Something really unknown: $e');
}

六.类

  • Dart是一种基于类和mixin继承机制的面向对象语言;
  • 所有的类都继承自Object;
  • 基于mixin继承机制, 每个类都只有一个超类

1. 构造函数声明

(1) 默认构造函数.类中没有声明构造函数的情况下, Dart默认提供一个默认的构造函数, 此默认构造函数无参且会默认调用父类无参构造函数; 但是,如果类中存在任何一个构造函数,Dart都不在提供默认的构造函数.
// 此类会存在一个默认的无参构造函数
class Point {
  num x, y;
}
// 此类不会存在默认构造函数, 因为类中存在命名的构造函数
class Point {
  num x, y;
  Point.origin() {
    x = 0;
    y = 0;
  }
}
(2) 命名构造函数.一个类可以实现多个命名的构造函数.有利于清晰的表达用途; 子类不能继承父类的构造方法;
// Point.origin即为命名的构造函数
class Point {
  num x, y;
  Point.origin() {
    x = 0;
    y = 0;
  }
}
(3) 调用父类命名构造函数.在当前类构造函数:号之后,函数体之前,调用父类的构造函数

:号右侧无法访问this

执行顺序如下:

  • 1.初始化参数列表
  • 2.父类构造函数
  • 3.子类构造函数
// 父类
class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('in Person');
    this.firstName = data['firstName'];
  }
}

// 子类
class Employee extends Person {
  String s;
  // 父类不存在默认构造函数,所以,必须显式的调用父类的命名构造函数
  Employee.fromJson(Map data, String s) : super.fromJson(data) {
    print('in Employee');
    this.s = s;
  }
}

void main(List<String> arguments) {
  // 先输出in Person, 后输出in Employee
  Employee e = new Employee.fromJson({'firstName': 'shen'}, '哈哈哈');
}
(5)初始化参数列表

方式一: 使用语法糖

class Point {
  num x, y;
  // 次处的this.x和this.y就相当于在构造函数体内进行赋值操作
  Point(this.x, this.y);
}

方式二: 在:号右侧初始化(:号右侧无法访问this)

class Point {
  num x, y;

  Point(this.x, this.y);

  // 为x和y赋值操作
  Point.fromJson(Map<String, num> json)
      : x = json['x'],
        y = json['y'] {
    print('In Point.fromJson(): ($x, $y)');
  }
}
(6) 重定向构造函数

有时构造函数的唯一目的是重定向到同一个类中的另一个构造函数。 重定向构造函数的函数体为空, 构造函数的调用在冒号 (:) 之后。

class Point {
  num x, y;

  // 类的主构造函数。
  Point(this.x, this.y);

  // 指向主构造函数
  Point.alongXAxis(num x) : this(x, 0);
}
(7) 常量构造函数

如果该类生成的对象是固定不变的, 那么就可以把这些对象定义为编译时常量。

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}
(8) 工厂构造函数

工厂构造函数无法访问 this。

当执行构造函数并不总是创建这个类的一个新实例时,则使用 factory 关键字。

class Logger {
  final String name;
  bool mute = false;

  // 从命名的 _ 可以知,
  // _cache 是私有属性。
  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);
  }
}

2. 构造函数使用

在 Dart 2 中 new 关键字变成了可选的。

通过构造函数创建对象. 构造函数的名字可以是类名(className)或者类名标识符(className.identifier).

class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

// 使用类名创建类对象
var p1 = Point(2, 2);
// 使用类标识符创建类对象
var p2 = Point.fromJson({'x': 1, 'y': 2});

3. 获取对象类型

使用对象的属性runtimeType可以在运行时返回对象的类型

/* ----------示例一 ------------------*/
var emp = new Employee.fromJson({});
  print('The type of emp is ${emp.runtimeType}'); // 返回Employee
  
/* ----------示例二 ------------------*/
var emp = new Employee.fromJson({});
emp.firstName = 'Bob';
print('The type of emp is ${emp.firstName.runtimeType}'); // 返回String

4. 类实例变量

类实例变量默认值为null, 非final的实例变量为默认声明隐式的getter和setter.
类实例变量如果已经在声明处赋值, 则赋值会在构造函数执行之前执行.

class Person {
  String firstName = 'shen';

  Person.fromJson(Map data) {
  	// 对属性的赋值会在构造函数执行之前执行
    print(this.firstName);
    print('in Person');
  }
}

main() {
  var emp = new Person.fromJson({});
}

5. 方法

方法是为对象提供行为的函数.

(1) 方法

class Point {
  num x, y;

  Point(this.x, this.y);

  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return (dx * dx + dy * dy);
  }
}

void  main() {
  Point p1 = new Point(1,2);
  Point p2 = new Point(2,4);
  print(p1.distanceTo(p2));
}

(2) set和get

声明get和set, 不能重复声明已经存在的属性.这个不同于java等语言
由get和set可以做计算属性的效果.

class Point {
  num x, y;
  Point(this.x, this.y);
  // 显式声明属性n的get方法
  num get n => x + 10;
  // 显式声明属性n的set方法
  set n (num value) {
    this.y += 100;
  }
}

void  main() {
  Point p1 = new Point(1,2);
  print(p1.n);
  p1.n = 200;
  print(p1.y);
}