Dart基础语法(一)

一个最基础的Dart程序

1
2
3
4
5
6
7
8
9
10
11

// 定义一个函数
printInterger(int aNumber, int bNumber) {
print('the number is $aNumber and ${bNumber}'); // $aNumber or ${bNumber} 都可以在字符串中使用变量
}

// 程序初始运行的函数
main()
var number = 42; // 声明与初始化变量
printInterger(number);
}

重要概念:

  • 所有变量都可以看成是一个object,所有的object都是通过Objectclass实例化出来的。这点和js一样
  • Dart是强类型语言,累心注释是可选的,因为Dart可以推断类型,如果想要定义一个没有类型的可以使用dynamic类型。如- Dart是强类型语言,累心注释是可选的,因为Dart可以推断类型,如果想要定义一个没有类型的可以使用`dynamic`类型。如- Dart是强类型语言,累心注释是可选的,因为Dart可以推断类型,如果想要定义一个没有类型的可以使用dynamic类型。如- Dart是强类型语言,累心注释是可选的,因为Dart可以推断类型,如果想要定义一个没有类型的可以使用dynamic类型。如var list= <dynamic>[1]
  • Dart的私有方法,都是以_下划线开头的,不像java直接写关键字private.

变量

声明变量

1
2
3
4
5
6
// 声明与初始化一个String类型的变量
var name = 'Bob';
// 如果想要任意类型,可以用 dynamic
dynamic name2 = 'Bob';
// 显式声明字符串类型
String name3 = 'Bob';

变量默认值

js不一样的是,如果在Dart里面声明一个变量,但是不赋值的话,他的默认值是null而不是undefined.

这也从另一个角度说明在Dart中,万物皆对象.

1
2
3
4
int lineCount;
var line;
assert(lineCount == null);
assert(line == null);

final && const

Dart里面的常量用 finalconst声明。

final表示单个赋值,一旦赋值,就不能被改变。
const表示编译时确定的值,并且对象会被冻结而完全不可变,比如DateTime.now()就可以用const去声明。

stackoverflow

1
2
const a = 1+2;
final b = DateTime.now();

内置类型

  • numbers
  • strings
  • booleans
  • lists(Array)
  • sets
  • maps
  • runes (Unicode characters in a string)
  • symbols

可以通过x.runtimeType来获取对象的类型

number

Dart的number有两种类型: intdouble

简单的区分,小的整数用int,浮点数用double。一般直接用var声明的话,会根据你声明时的类型做推断,所以如果声明和后面使用不一样的话,建议显式声明.例

1
2
3
4
5
6
var x = 1; // 这里Dart会推断是int类型
x = 2.0; // 报错

// 可以换成这两种
var x = 1.0;
double x = 1; // 相当于 double x = 1.0;

字符串转数字

在Dart里面,如果字符串和数字相加,会报错,这一点和js隐式转换不同。

1
2
3
4
var one = int.parse('1'); // String 2 int
var onePointOne = double.parse('1.1'); // string 2 double
String oneAsString = 1.toString(); // int 2 String
String piAsString = 3.141592654.toStringAsFixed(2); // double 2 string 3.14

Strings

可以用'"来表示字符串。

字符串内部的变量可以用$value或者${value},一般来说如果是变量,直接用$value表示,表达式用${value},例:

1
2
3
var a = 'String1';
var b = "String2";
assert('String1 STRING2' == '$a ${b.toUpperCase()}');

如果要创建多行的字符串,则可以用'''""",

1
2
3
4
5
var s1 = '''
hi
I'm Jsonz
'''
var s2 = '

Booleans

Dart里面ifassert都只会对 bool做判断,这点同样与js的隐式转换不同。

1
2
3
4
5
6
7
8
9
var fullName = '';
assert(fullName); // error
assert(fullName.isEmpty);

var hitPoints = 0;
assert(hitPoints <= 0);

var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);

Lists (既js里面的数组)

1
2
3
4
5
6
7
8
9
10
11
12
13
var list = [1,2,3, '4']; // 普通list
var typeList = <int>[1,2,3]; // 带类型的list
assert(list.length == 4);

typeList[0] = 2;
assert(typeList[0] == 2);

// 创建const类型的list
var constantList = const [1,2,3];
constantList[1] = 1; // error

// 也可以这样创建
List<int> list = const [1,2,3];

Sets

Sets会根据初始化推断类型,比如下面的例子halogens会判断为String类型,这时候添加其他类型的值就会报错。

1
2
3
4
5
6
7
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
halogens.add(1); // error

// 要创建一个空的set,比如要有类型前缀,不然会被判断为创建的是Maps而不是Sets
var name = {}; // map, not a set
var names = <String>{}; // String set
Set<String> name2 = {}; // Srting set

Set通过add或者addAll添加item
可以通过const关键字创建编译常量

1
2
3
4
5
6
7
8
9
10
11
12

// add 与 addAll
var el = <String>{};
var el2 = {'a', 'b'};
el.add('c');
el.addAll(el2);
assert(el.length == 3);

// const
final constantSet = const {
'a', 'b',
}

Maps(相当于js的对象)

DartMap不限制key的类型,意味着如果是number类型,不需要和js一样,用[]包着。

Map也会推断keyvalue的类型,比如下面的 map1 就是keyint类型,valuestring类型的map.

如果获取某个map没有的value,则value默认是null。这点和jsundefined不同,可以对比变量初始化的情况类推。

map也有length属性,类比jsObject.keys(obj).length.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// map的key可以是任意类型
var gif = {
'a': 'b',
1: 2,
3: '4',
}

// <int>: <String> 类型的map
var map1 = {
1: '2',
}

// 编译时常量map
final constantMap = const {
2: '2',
}
constantMap[2] = '3'; // error

Runes

emmm 这个我的理解是UTF-32编码的字符串,简单理解就是可以支持更多类型的字符串,比如emoji,工作用到再去查。
比如'\u{1f44f}';👏,\u2665

Symbols (类比js的Symbol)

Symbol的理解可以直接看阮一峰的ES6-Symbol,基本的作用是一样的。

DartSymbolcompoile-time constants,就是编译时常量。
使用也很简单,直接在标识前面加个#

1
const symbol = #symbol;

Function

Dart是面向对象的语言,所以函数也是一种Function类型的对象。

参数的默认值都为null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 基本用法
bool isNaN(int number) {
return number.isNaN;
}

isNaN2(number) {
return number.isNaN;
}

bool isNaN3(int number)=> number.isNaN;

// 参数使用
void enableFlags({
bool bold,
bool hidden
}) {
print(bold);
print(hidden);
}
enableFlags(bold: true, hidden: false); // true false
enableFlags(hidden: false, bold: true); // true false
enableFlags(hidden: false); // null false

// 这种对参数的顺序有严格要求, []表示可选参数
void enableFlags2(bool bold, bool hidden, [String device]) {

}
enableFlags2(true, false, 'device');

// 默认参数
void enableFlags({
bool bold= false,
bool hidden= false,
}) {}
enableFlags(hidden: true); // false true

// 你也可以用 list 和 map作为默认参数
void doStuff({
List<int> list = const [1,2,3],
Map<String, String> gifts = const {
'first': 'paper',
'second': 'cotton',
'third': 'leather'
}
}) {
print('list: $list');
print('gifts: $gifts');
}

main 函数

每个app都需要在最顶层提供一个main函数用于入口函数,main函数提供一个void返回值与List<String>参数(参数指的是命令行传入的参数)。

函数为第一公民

和js一样,Dart也支持将函数作为参数传给另一个函数(高阶函数)。

1
2
3
4
5
6
void printE(int e) {
print(e);
}

var list = [1,2,3];
list.forEach(printE);

匿名函数、作用域与闭包

这几个和js是一样的,没啥好说

对词法作用域和动态作用域有疑问的可以看这篇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 匿名函数
var list = [1,2,3];
list.forEach((item)=> print(item));
list.forEach((item) {print(item);});

// 词法作用域
a() {
var a1 = true;
b() {
var b1 = true;
c() {
var c1 = true;
assert(a1);
assert(b1);
assert(c1);
}
}
}

// 闭包
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}

void main() {
// Create a function that adds 2.
var add2 = makeAdder(2);

// Create a function that adds 4.
var add4 = makeAdder(4);

assert(add2(3) == 5);
assert(add4(3) == 7);
}

函数类型(Testing functions for equality)

看着自己理解一下就好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void foo() {} // A top-level function

class A {
static void bar() {} // A static method
void baz() {} // An instance method
}

void main() {
var x;

// Comparing top-level functions.
x = foo;
assert(foo == x);

// Comparing static methods.
x = A.bar;
assert(A.bar == x);

// Comparing instance methods.
var v = A(); // Instance #1 of A
var w = A(); // Instance #2 of A
var y = w;
x = w.baz;

// These closures refer to the same instance (#2),
// so they're equal.
assert(y.baz == x);

// These closures refer to different instances,
// so they're unequal.
assert(v.baz != w.baz);
}

函数返回值

函数都会返回一个值,如果没有指定的话,则会返回null。Dart里面没有undefined的概念,目前看来所有对于js的undefined都可以替换成null

1
2
foo() {}
assert(foo() == null);

运算符

类型测试运算符

asisis!类似 instanceof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person {
var firstName= '2';
}

var a = new Person();
var b;

if (a is Person) { // true
a.firstName = 'b';
}
if (b is! Person) { // true
print('b is not person');
}

// 相当于 if (a is Person) xxx 的缩写
// 如果 a 是 Person的实例,就会执行 firstName = 'Bob'
// 如果 a 不是 Person的实例,会抛错!
(a as Person).firstName = 'Bob';

分配运算符

1
2
a = value; // 直接把value分配给a
b ??= value; // 如果b为null,则分配value给b,否则不改变。这里经常是函数里面的默认值处理,类似js的 var obj = obj || {};

三目运算符

1
2
3
4
5
// condition? expr1: expr2
var vis = isPublic? 'public': 'private';

// expr1?? pxpr2 如果expr1为Null,就返回expr2,否则是expr1。有点类似加强版的js里面的 a=> a || 'test'
String playName(String name)=> name?? 'guest';

Cascade notation

Cascade(..) 有点像链式写法的语法糖,允许您对同一对象进行一系列操作,不得不说这些语法糖真骚…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// .. 写法
querySelect('#confirm')
..text = 'Confirm'
..classes.add('important')
..onClick.listen((e)=> window.alter('Confirmed!'));

// 相当于
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e)=> window.alter('Confirmed!'));

// 你还可以随意嵌套
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jsonz@qq.com'
..phone = (
PhoneNumberBuilder()
..number = '13560042631'
..label = 'phone'
.build()
)
.build();
)

其他操作符

() 函数调用
[] list
. 属性
?. 一般用于属性赋值,比如 person?.name = 'jsonz'(如果 person.name 不为null则设置为jsonz)。

控制流程语句

这里只会挑与js不同的来讲

if..else

不同点是Dart不会隐式转换,所以判断的内容只能是布尔值

1
2
3
4
5
6
7
if (false) {

} else if (false) {

} else {

}

loop

for

for in

1
2
3
4
var collection = ['a', 'b', 'c'];
for (var x in collection) {
print(x); // 'a' 'b' 'c'
}

while && do-while

break && continue

break 停止循环
continue跳过当前次,进行下一次循环

Switch && case

switch 可以对比 stringnumberbool但是不能混着用。

Assert(断言)

写过单元测试的应该对assert不陌生,如果布尔条件为falseassert会中断执行。
不过只会在开发模式起作用,生产环境不会执行。

Other

Throw 抛错

1
2
throw FormatException('expected at least 1 section');
throw 'out of llamas!';

catch

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e, s) {
// No specified type, handles all
print('Something really unknown: $e');
print('Stack trace: \n $s');
}

finally

1
2
3
4
5
6
7
try {
someThing()
} catch(e) {
print('Error: $e');
} finally {
clean();
}