02 - Dart笔记-基础语法

基础语法

[TOC]

官方文档

中文文档

变量

dart是强类型

var

由类型推断确定变量类型

dynamic

可以动态改变变量类型

final

与java相同,只能赋值一次

const

声明为常量

  • 其值为常量值,new出来的对象实例不是常量值,所以const不能修饰对象实例
  • 顶层变量、方法中的临时变量用const修饰时不加static
  • 类的成员变量用const修饰时,必须加static

常量的值可以是全为常量的表达式,例如常量相加,常量字符串插值

内建类型

numbers

int

平台不同,不超过64位

double

64位(双精度)浮点数

字面值的方法

与kotlin一样,字面值也可以调用方法,例如:

var tmp = 3.14159.toStringAsFixed(2);

字符与number互转

int aInt = 10;
double aDouble = 3.1415;
String aIntStr = "123";
String aDoubleStr = "5.312";

var bInt = int.parse(aIntStr);
var bDouble = double.parse(aDoubleStr);
var stringFromInt = aInt.toString();
var stringAsFixed = aDouble.toStringAsFixed(2);

String

  • 可以用'', ""
  • 字符串中的单引号\'""中时,可以不转义直接写
  • 可以使用插值表达式$标识符${表达式}
  • 字符重复:插值表达式中字符、字符串可以使用*重复n次
  • 字符串拼接:用+拼接;相邻字符串字面量可以认为是拼接
  • 多行字符串:开头和结尾用三个单引号,或者三个双引号表示
  • 原始字符串:r开头的字符串,不转义
void useString() {
  // 1. 单引号 双引号
  var a = 'Single quotes';
  var b = 'Double quotes';
  var c = "I'm ok";
  var d = 'It\'s wrong';

  // 2. 插值表达式
  int x = -100;
  var e = "check i = $x";
  var f = "abs i = ${x.abs()}";

  // 3. 字符重复
  var g = "am${'a' * 10}zing";
  var h = "a${"ma" * 10}zing";

  // 4. 字符串拼接
  var i = "Welcome " 'to ' "China";
  var j = "I'm" + " " + "tired";

  // 5. 多行字符串
  var k = '''
Initializing gradle...
Resolving dependencies...
Gradle task 'assembleDebug'...''';
  var l = """
Performing hot reload...
Syncing files to device Android SDK built for x86...
Reloaded 3 of 422 libraries in 428ms.""";
  // 6. 原始字符串
  var m = r"will not \n start a new line";

  print(
      "use String:\n$a\n\n$b\n\n$c\n\n$d\n\n$e\n\n$f\n\n$g\n\n$h\n\n$i\n\n$j\n\n$k\n\n$l\n\n$m");
}

布尔类型bool

bool aBool = true;

非bool类型的值不能做为表达式的判断

// isEmpty查空串
var fullName = '';
assert(fullName.isEmpty);

// 检查数值
var hitPoints = 0;
assert(hitPoints <= 0);

// null判空
var unicorn;
assert(unicorn == null);

// isNaN算术 NaN
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);

列表/数组类型List<E>

  • 类型是List<E>
  • 编译时常量列表:在列表字面量前使用const修饰符
void useList() {
  List<int> list = [1, 2, 3];
  list[0] = list.length;
  print("list's 0 item = ${list[0]}");

  var constList = const [9,8,7];
  print(constList);
}

字典/映射表类型Map<K,V>

  • 类型是Map<K,V>
  • 编译时常量字典:在字典字面量前使用const修饰符
Map<int, String> map = {1: "1", 4: "4"};
map[10] = "10";
var tmp = map[4];
assert(null == map[7]);
map[1] = map.length.toString();
var constMap = const {1: 2, 3: 4};
print(map);

Runes

  • Runes是String 字符串中字符的32位Unicode编码的Iterable<int>
  • String.codeUnits返回的是字符串的utf16编码
  • String与Rune互转
void useRunes() {
  String unicodeStr = '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  '
      '\u{1f44d}';

  // String 转 runes
  var runes = unicodeStr.runes;
  runes = new Runes(unicodeStr);
  // runes 转 String
  var string = String.fromCharCodes(runes);


  print("unicode string: ${unicodeStr}");
  print("unicode string in utf16: ${unicodeStr.codeUnits}");
  print("unicode string in utf32: ${unicodeStr.runes.toList()}");

}

symbols

函数

=>单表达式函数

=>expr 等价于 {return expr;}

参数列表

  • 参数类型可以省略,但是不推荐

  • 不能传与使用不符的类型,即使用了dynamic方式声明也不行

    print(testParamNoType(2));
    //  print(testParamNoType('r'));
    //  print(testParamNoTypeCompare('r'));
    //  print(testParamNoType(null));
    
    testParamNoType(val) {
      return 10 * val;
    }
    
    testParamNoTypeCompare(dynamic val) {
      return 10 * val;
    }
    
    The following assertion was thrown while handling a gesture:
    type 'String' is not a subtype of type 'num'
    
  • 参数列表必须先声明必传参数,再声明可选参数

可选参数

与Java的可变参类似。

给一个函数定义可选参数,只能选择位置参数或者命名参数中的一种,不能同时使用两者

参数默认值
位置参数
  • 参数列表中被[]包裹的参数
  • 可以不传只传部分传全部
  • 位置参数必须按照声明顺序入参,多个位置参数时,使用时,可以只传递部分参数,但是不可以跳过中间的某些参数
命名参数
  • 参数列表中被{}包裹的参数
  • 命名参数可以不传只传部分传全部,或者改变顺序传,但是必须带上参数的命名
testVA() {
  testParamPos(1);
  // 位置参数使用时不能带上参数名
  // The named parameter 'c' isn't defined.
//  testParamPos(2, c:"e");
  // 位置参数不能跳过中间的参数
  // The argument type 'int' can't be assigned to the parameter type 'String'
//  testParamPos(2, 1);
  testParamPos(2, "a");
  testParamPos(3, "b", 4);

  testParamName(1);
  testParamName(2, d: 6);
  // 命名参数必须带上参数名
  // too many positional arugments ...
//  testParamName(2,5,8);
  testParamName(3, c: 7);
  testParamName(4, c: 8, d: 0);
  testParamName(5, d: 9, c: 0);
}

//testParam(int req,[int c],{int a= 100,int b=300}){}
//testParam(int req,{int a= 100,int b=300},[int c]){}
testParamPos(int req, [String c = "h", int d]) {}

testParamName(int req, {int c = 8, int d}) {}

返回值

没有指定返回值,则返回值为null,类似js undefined

main

应用程序入口是一个顶层函数main

void main(){}
void main(List<String> args){}

函数也是对象

  • 可以将函数作为参数传递给另一个函数,类似c的函数指针
  • 可以将函数赋值给变量
int sum = 0;

countSum(int val) {
  sum += val;
}

testFunction() {
  var list = [32, 123, 43, 31, 55, 346, 786];
  sum = 0;
  list.forEach(countSum);
  print(sum);

  var addFunc = (int a, int b) {
    return a + b;
  };
  var minusFunc = (int a, int b) => a - b;

  print(addFunc(4, 3));
  print(minusFunc(4, 3));
}

判断函数引用是否相等

  • 引用同一个顶层函数的变量相等
  • 引用同一个类的静态方法的变量相等
  • 引用同一个类同一个实例的成员方法的变量相等
  • 引用同一个类不同实例的成员方法的变量不等

转载一个demo看看就行了

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);
}

匿名函数/lambda/闭包

  • 没有名字的函数,有参数列表,方法体
  • 与kotlin不同,没有默认的变量it
  • 单表达式可以用=>简写
testAnonymousFunction() {
  var list = [1, 5, 31, 57, 231, 6, 234, 7];
//  list.forEach({
//    print(it);
//  });
//  list.forEach(
//    print(it);
//  );
  list.forEach((it) => print(it));

  list.forEach((it) {
    print(it);
  });
}

函数中变量的作用域

  • 嵌套函数内层函数可以访问外层直到顶层的变量;
  • 外层函数访问不到内层函数的变量;
testNestedFunction() {
  int a = 12;
  void myNested() {
    int b = 43;

    void nest() {
      int c = 55;
      // 内层函数能够访问到外层函数的变量
      a = 10;
    }
  }

  // 外层函数访问不到内层函数的变量
  // undefined name 'b'
//  b = 10;
}

闭包传递的参数

闭包会捕获定义在其声明位置的作用域内的变量。即使闭包函数在其声明位置之外的作用域使用,也会记住被捕获的变量

A closure is a function object that has access to variables in its lexical scope, even when the function is used outside of its original scope.

Functions can close over variables defined in surrounding scopes. In the following example, makeAdder()captures the variable addBy. Wherever the returned function goes, it remembers addBy.

testClosureArgument() {
  makeClosure(int a) {
    return (num val) => val + a;
  }

  Function addClosure2 = makeClosure(2);
  Function addClosure3 = makeClosure(3);

  print(addClosure2(5));
  print(addClosure3(5));

  Function minusClosure2;
  Function minusClosure3;

  makeClosure2(int b) {
    b = 2;
    minusClosure2 = (num val) => val - b;
    b = 3;
    minusClosure3 = (num val) => val - b;
  }

  makeClosure2(9);

  print(minusClosure2(5));
  print(minusClosure3(5));

  makeClosure3(int c) {
    Function build(int i) {
      return (num val) => val * i;
    }

    var list = new List(c);
    for (int j = 0; j < c; j++) {
      list[j] = build(j);
    }
    return list;
  }

  var funcList = makeClosure3(5);

  funcList.forEach((func) => print(func(2)));

  makeClosure4(int c) {

    var list = new List(c);
    for (int j = 0; j < c; j++) {
      list[j] = (num val) => val * j;
    }
    return list;
  }

  var funcList2 = makeClosure4(4);

  funcList2.forEach((func) => print(func(3)));


  print("next>>>>>");
  makeClosure5(int c) {

    int len  = c;
    var list = new List(c);
    for (int j = 0; j < len; j++) {
      c = j;
      list[j] = (num val) => val * c;
    }
    return list;
  }

  makeClosure5(6).forEach((func) => print(func(4)));


}

此例中,makeClosure2和makeClosure5中,传入闭包的并不是在其声明位置的函数中定义的变量,而是形参。结果就失效,闭包只能记住形参最后一次的值。

运算符

按照优先级排列

描述 操作符
一元后置操作符 expr++    expr--    ()    []    .    ?.
一元前置操作符 -expr    !expr    ~expr    ++expr    --expr
乘除运算 *    /    %    ~/
加减运算 +    -
移位运算 <<    >>
按位与 &
按位异或 ^
按位或 |
关系和类型测试 >=    >    <=    <    as    is    is!
相等 ==    !=
逻辑与 &&
逻辑或 ||
是否为null ??
天健判断(三元运算) expr1 ? expr2 : expr3
级联 ..
赋值 =    *=    /=    ~/=    %=    +=    -=    <<=    >>=    &=    ^=

要注意的运算符

  • 除法运算符/~/,前者会返回小数部分,后者只返回整数部分

  • 后缀递增递减a++,a--赋值时传递的是原值

    var a = 0;
    var b = a++;
    assert(b==0);
    
  • 类型测试操作符,isis!用于判断实例是否是某个类型;as用于强转,失败则抛出异常;若实例先用is命中,则在其判断分支的代码里可以直接使用其真实类型的成员

    testType(){
      print("ad" is String);
      print(1 is! String);
    
      Object child = Child(10);
    
      if(child is Child){
        // is 检查命中后,实例可以直接使用真实类型的成员
        var b = child.b;
      }
    
      // 没有使用is检查前,可以使用as强转指定类型,
      // 但是如果为null或者类型不符则抛出异常
      var b2 = (child as Child).b;
    
    }
    
  • 空值安全处理:??=如果变量为null才赋值;e1??e2若e1结果为null,则返回e2的结构;obj?.member如果obj不为null,则使用其成员,否则则放弃;

    testNull() {
      var child;
    
      print(child?.b);
    
      var aChild = child ?? Child(15);
    
      print(aChild.b);
    
      child ??= Child(12);
      print(child.b);
    }
    
  • 位与位移操作符:&按位与;|按位或;^按位异或;~按位取反;<<左移;>>右移

级联运算

  • 级联(..)允许您在同一个对象上创建一个操作序列。除了函数调用之外,您还可以访问同一对象上的字段。
  • 级联可以给一个对象的set赋值
  • 级联操作只能从实例对象或者一个返回实例对象的方法开始
  • 级联可以操作对象的所有成员方法
  • 级联操作相当于调用了成员方法后,又返回了实例对象交由下一个方法使用
  • 如果将连续的级联操作结果赋值给另一个变量,则实际上是将级联的起始对象赋值给变量,而不是最后一个带返回值的方法的返回值。
  • 类的静态方法,不论是正常调用还是级联运算,都不能通过实例对象调用
testCascade() {
  var user = User()
    ..age = 10
    ..name = "rexih"
    ..id = 1021
    ..setAlias("Lisa")
    ..sex = 1;
  print(user);
  // 类的静态方法,不论是正常调用还是级联运算,都不能通过实例对象调用
  // Static method 'setNice' can't be accessed through an instance
//  user.setNice(false);
//  user..setNice(false);

  StringBuffer sb = new StringBuffer();
  sb.write("aa");
  sb..write("a")..write("b");
  print(sb.toString());

  // 级联操作只能从实例对象或者一个返回实例对象的方法开始,
  // 后续操作都是起始对象可以调用的成员方法
  // 级联操作相当于调用了成员方法后,又返回了实例对象交由下一个方法使用
  // The expression here has a type of 'void', and therefor cannot be used
//  sb.write("v")
//  ..write("c");

  var name = User().findName();
  // 级联操作可以从实例变量开始
  var s = name
    ..substring(2);
  // s的值没有变,因为级联操作只是为了处理被操作的对象,
  // 赋值给另一个变量的不是方法的返回值,而是级联起始的对象
  print(s);
  String findName = User().findName()
    ..substring(1)
    ..toUpperCase();
  print(findName);
}

class User {
  int id;
  String name = "anonymous";
  int sex;
  int age;

  void setAlias(String alias) {
    name = alias;
  }

  String findName() => name;

  @override
  String toString() {
    return 'User{id: $id, name: $name, sex: $sex, age: $age}';
  }
}

流程控制

for循环

  1. 正常for循环

  2. forEach循环

  3. for in循环

      var list = [4, 5, 2];
      for (var a in list) {
        print(a);
      }
    
  4. 关于官方文档中说可以避免js的闭包问题,跟之前说的传入闭包的参数一样,看传入的变量是否是在闭包声明位置的作用域内定义,如果不是,仍然捕获不到

    void testFor() {
      var callbacks = [];
      for (var j = 0; j < 5; j++) {
        callbacks.add(() => print(j));
      }
      callbacks.forEach((c) => c());
    
      callbacks.clear();
      print("show error");
      int i = 0;
      for (; i < 9; i++) {
        callbacks.add(() => print(i));
      }
      callbacks.forEach((c) => c());
    }
    

    运行结果

    I/flutter (30393): 0
    I/flutter (30393): 1
    I/flutter (30393): 2
    I/flutter (30393): 3
    I/flutter (30393): 4
    I/flutter (30393): show error
    I/flutter (30393): 9
    I/chatty  (30393): uid=10099(cn.rexih.flutter.flutterapp2) Thread-2 identical 7 lines
    I/flutter (30393): 9
    

switch

dart的switch支持字符串,整数和编译时常量,但是一个switch中只能比较一种类型

assert

  • 条件表达式不为true才中断抛出异常
  • 只在测试环境生效
  • 可以在第二个参数传入断言信息
assert(urlString.startsWith('https'),
    'URL ($urlString) should start with "https".');

异常

  • 异常类型为Exception和Error
  • throw抛出异常
  • rethrow 在捕获中重新抛出
  • finally做资源释放和清理操作

异常捕获

  • 只捕获异常,不处理异常对象
  • 处理异常对象
  • 处理StackTrace堆栈跟踪对象
try {
  breedMoreLlamas();
} on OutOfLlamasException {
  //只捕获异常,不处理异常对象
  buyMoreLlamas();
} on Exception catch (e) {
  //处理异常对象
  print('Unknown exception: $e');
  //在捕获中重新抛出
  rethrow;
} catch (e, s) {
  //处理StackTrace堆栈跟踪对象
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
} finally {
  cleanLlamaStalls();
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容