Dart语言笔记(一)

Dart语言笔记(二)

1.重要概念

  • 一切变量都是对象,包括数字、函数和null
  • 对象是类的实例,所有类都继承于Object
  • 强类型但支持类型推断
  • 如果不想指定具体类型可以使用dynamic
  • 支持泛型,比如List<int>List<daynamic>
  • 支持顶层函数、类方法、实例方法和嵌套函数
  • 支持顶层变量、类变量、实例变量和局部变量
  • 以下划线_打头的变量为私有,只能在库(Library)内部使用
  • 标识符只能以字母或下划线打头,后面跟任意多个字符、数字或下划线字符
  • 表达式在运行时可以求值,而语句不行
  • 代码问题分为warning和error两种,warning不会阻止程序运行,但error会
  • error分为编译时错误和运行时错误两种,运行时错误会导致抛出异常

2.变量

  • 变量类型可自动推断,或者显示指定
  • 未初始化的变量其值都是null,包括数字
  • 如果某个变量的值在定义后不会再改变,那就声明为finalconst
  • finalconst变量只能赋值一次,但const变量的值需要在编译时就能确定
  • finalconst变量必须在其定义时就完成初始化
  • const变量的值须为编译时常量,包括数字和字符串字面量、其他常量或算术运算结果
  • const除了用来声明变量,也可用来声明值,也就是常值
  • 初始化const变量时可以省去值前面的const,因为const变量的值肯定得是一个常值
  • 常值用来防止内容变化,常量用来避免变量寻址,final变量用来提升代码安全性

3.内置类型

  • 数字(num)
    • num是int和double的基类
    • int为64位整数,double为64位浮点数
  • 字符串(String)
    • 字符串是UTF-16码元(Code Unit)序列,每个Unicode字符映射为1个或2个码元
    • 字符串字面量可以使用单引号或双引号
    • 使用${expression}来插入变量值,如果expression是个合法的标识符,则可以省去{}
    • 使用毗连字符串常量或者+操作符来拼接字符串
    • 使用三重引号(三重单引号或者三重双引号都可以)来定义多行字符串
    • 使用r前缀来声明原始字符串,其内不作字符转义
    • 只要内部插入的表达式为编译时常量,则字符串字面量为编译时常量
  • 布尔(bool)
    • 只有两个值true和false,都是编译时常量
    • 在需要布尔类型的地方必须使用布尔值,没有非零即真的概念
  • 列表(List)
    • 列表就是其他语言里的数组,用来表示对象序列
    • 列表索引序号从0开始,直到list.length-1
    • 在列表字面量前使用const来声明常值
    • 使用[]操作符来操作列表元素
  • 哈希表(Map)
    • 哈希表用来关联键和值,键和值可以是任意类型的对象,但键必须唯一
    • 使用[]操作符来操作哈希表的值
    • 在哈希表字面量前使用const来声明常量
  • 符文(Rune)
    • Rune是UTF-32码点(Code Point)组成的字符串,一个Rune字符对应一个Unicode字符
    • 码点一般使用4个十六进制字符\uXXXX来表示,如果超过4个,则需要使用{}将十六进制字符括起来
    • 字符串和Rune可以直接互相转换
  • 符号(Symbol)
void main() {
  var x = 1;
  var hex = 0xDEADBE;

  var y = 1.1;
  var exponents = 1.42e5; //科学计数法

  double z = 1;

  var addIntAndDouble = x + y; //int可以直接和double计算,会隐式转换为double
  assert(addIntAndDouble == 2.1);

  var one = int.parse('1'); //使用parse解析字符串
  assert(one == 1);

  var onePintOne = double.parse('1.1');
  assert(onePintOne == 1.1);

  String oneAsString = 1.toString(); //使用toString转换为字符串
  assert(oneAsString == '1');

  String piAsString = 3.1415926.toStringAsFixed(2); //转换为string并保留几位小数
  assert(piAsString == '3.14');

  assert((3 << 1) == 6);
  assert((3 >> 1) == 1);
  assert((3 | 4) == 7);

  //定义常量
  const msPerSecond = 1000;
  const secondsUntilRetry = 5;
  const msUntilRetry = secondsUntilRetry * msPerSecond;

  var s1 = 'Single string'; //单引号
  var s2 = "Double string"; //双引号
  var s3 = 'It\'s easy'; //转义字符
  var s4 = "It's easy"; //双引号中单引号就不用加转移字符

  var s = 'string';
  var uppeers = s.toUpperCase(); //转化为大写
  assert(uppeers == 'STRING');
  assert(s == uppeers.toLowerCase()); //转化为小写

  //使用毗连字符串拼接
  s1 = 'String '
      'concat '
      'work';
  assert(s1 == 'String concat work');

  //使用+连接字符串
  s2 = 'String ' + 'concat ' + 'work';
  assert(s2 == 'String concat work');

  //使用'''或者"""定义多行字符串
  s2 = '''String
       concat
       work''';

  //在字符串前加r定义不转义的字符串
  s = r'In a raw string,not even \n gets special treatment';

  const aConstNum = 0;
  const aConstBool = true;
  const aConstString = 'a const string';

  var aNum = 0;
  var aBool = true;
  var aString = 'a string';
  //定义一个List常量
  const aConstList = [1, 2, 3];

  //后面都是常量,而且表达式编译时可确定值,所以可以赋值给一个常量字符串
  const validConstString = '$aConstNum $aConstBool $aConstString';
  // const invalidConstString = '$aNum $aConstNum $aConstBool $aConstString';

  var fullName = '';
  assert(fullName.isEmpty); //使用isEmpty来判空

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

  //未初始化默认都是null
  var unicorn;
  assert(unicorn == null);

  //这个不会抛出异常,类型是double,但是值是NaN
  var iMeantToDoThis = 0 / 0;
  print(iMeantToDoThis);
  print(iMeantToDoThis.runtimeType);
  assert(iMeantToDoThis.isNaN);

  var list = [1, 2, 3];
  assert(list.length == 3);
  assert(list[1] == 2);
  list[1] = 22;
  assert(list[1] == 22);

  //给list赋常值
  var constList = const [1, 2, 3];
  //常值的内容不能被改变
  // constList[1] = 22;

  //Map定义
  var gifts = {
    'first': 'part',
    'seconed': 'tue',
    'fifth': 'golden',
  };
  print(gifts.runtimeType); //JsLinkedHashMap<String, String>

  var nobleGases = {2: 'helium', 10: 'neon', 18: 'argon'};
  print(nobleGases.runtimeType); //JsLinkedHashMap<int, String>

  //构造函数方式创建Map
  gifts = Map();
  gifts['first'] = 'part1';
  gifts['seconed'] = 'tue1';
  gifts['fifth'] = 'golden1';

  gifts = {'first': 'part1'};
  assert(gifts['fifth'] == null);

  gifts['seconed'] = 'tue';
  assert(gifts.length == 2);

  //常值Map
  final constMap = const {
    2: 'helium',
    10: 'neon',
    18: 'argon',
  };
  //final定义的只能一次赋值
  // constMap = Map();
  //常量不能修改内容
  // constMap[20] = 'tw';

  //每两个十六进制占一个字节,这里占3个字节,所以需要大括号括起来
  var clapping = '\u{1f44f}';
  print(clapping); //👏
  // 打印码云序列,需要两个码云
  print(clapping.codeUnits); //[55357, 56399]
  // 转化为一个runes,得到runes的码点序列
  print(clapping.runes.toList()); //[128079]

  //小于等于2个字节不用加大括号
  Runes input =
      Runes('\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
  //fromCharCodes:根据Unicode编码序列来得到字符串
  print(String.fromCharCodes(input)); //♥ 😅 😎 👻 🖖 👍
}

4.函数

  • 函数也是一种对象,类型为Function
  • 函数参数和返回值类型可省略,以支持动态类型
  • 如果函数体只包含一个表达式,可使用箭头语法来定义
  • 可选参数
    • 可选命令参数使用{}来指定,并且可使用注解@required标注为必需
    • 可选位置参数使用[]来指定
    • 可选参数默认值使用=来指定,如未指定则默认值为null
  • 每个dart程序都应该有个位于顶层的main函数,它是程序的入口
  • 函数可作为函数参数值,也可以赋值给变量
  • 可定义匿名函数,一般作为函数参数值或赋值给变量
  • 变量作用域静态确定,也就是同代码布局,每对大括号定义一个作用域,嵌套大括号定义嵌套作用域
  • 闭包是一个能访问其外层作用域变量的函数,即便该函数在其他地方被调用
  • 如果函数没有指定返回值,则默认返回null,如果确实不想返回任何值,则可指定返回类型为void
//全局变量 是否顶层
bool topLevel = true;
void main() {
  var _nobleGases = {
    2: 'helium',
    10: 'neon',
    18: 'argon',
  };

  //最普通的函数定义
  bool isNoble(int atomicNumber) {
    return _nobleGases[atomicNumber] != null;
  }

  //dart是一门兼具动态性和静态性的语言
  //未指定参数类型,那么参数类型就是dynamic
  //返回值为bool类型可以推断出来
  //所以返回值类型和参数类型都可以省略
  isNobleDynamic(atomicNumber) {
    return _nobleGases[atomicNumber] != null;
  }

  //单行表达式可以使用箭头
  bool isNobleArrow(int atomicNumber) => _nobleGases[atomicNumber] != null;

  //可选命名参数需要放在{}中
  void enableFlags({bool? bold, bool? hidden}) {}
  //可选命令参数调用时需显式指定参数名
  enableFlags(bold: true, hidden: false);

  //位置参数放在[]中
  String say(String from, String msg, [String? device]) {
    var result = '$from says $msg';
    if (device != null) {
      result = '$result with a $device';
    }
    return result;
  }

  assert(say('Bob', 'Howdy') == 'Bob says Howdy');
  assert(say('Bob', 'Howdy', 'smoke signal') ==
      'Bob says Howdy with a smoke signal');

  //可选参数设置默认值
  void enableFlagsDefault({bool? bold = false, bool? hidden = false}) {}
  enableFlagsDefault(bold: true);

  String sayDefault(String from, String msg,
      [String? device = 'carrier pigeon', String? mood]) {
    var result = '$from says $msg';
    if (device != null) {
      result = '$result with a $device';
    }
    if (mood != null) {
      result = '$result (in a $mood mood)';
    }
    return result;
  }

  assert(sayDefault('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');

  void doStuff(
      {List<int> list = const [1, 2, 3],
      Map<String, String> gifts = const {
        'first': 'paper',
        'seconed': 'cotton',
        'third': 'leather'
      }}) {
    print('list: $list'); //list: [1, 2, 3]
    print(
        'gifts: $gifts'); //gifts: {first: paper, seconed: cotton, third: leather}
  }

  doStuff();

  void printElement(int element) {
    print(element);
  }

  var list1 = [1, 2, 3];
  list1.forEach(printElement);

  var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
  print(loudify('hello')); //!!! HELLO !!!

  var list2 = ['apples', 'bananas', 'oranges'];
  list2.forEach((item) {
    print('${list2.indexOf(item)}: $item');
  });
  //单行表达式可以换为箭头函数返回
  list2.forEach((item) => print('${list2.indexOf(item)}: $item'));

  //作用域嵌套
  var insideMain = true;
  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;
      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }

  //闭包,匿名函数
  Function makeAdder(num addBy) {
    return (num i) => addBy + i;
  }

  var add2 = makeAdder(2);
  var add4 = makeAdder(4);
  assert(add2(3) == 5);
  assert(add4(3) == 7);

  //无返回值默认返回值为null
  foo() {}
  assert(foo() == null);
}

5.操作符

  • 操作符有优先级,从高到低为一元后缀、一元前缀、乘除、加减、移位、位运算、关系运算和类型测试、相等性运算、逻辑运算、null判断、三元表达式、级联调用和赋值
  • 在优先级判断比较模糊的地方,使用()来提升可读性
  • 算术运算
    • +-*/-expr~/(整除)、%(求余)
    • ++varvar++--varvar--
  • 相等性和关系运算
    • ==!=><>=<=
    • ==判断值是否相等,如果要判断是否为同一个对象,使用identical()函数
  • 类型测试
    • as(类型转换)、is(判断是否指定类型)、is!(判断是否非指定类型)
  • 赋值
    • =??=(null则赋值,否则不赋值;一般用于初始化时防止重复初始化)
    • -=/=%=>>=^=+=*=~/=<<=&=|=
  • 逻辑运算
    • !expr(逻辑非)、||&&
  • 位运算
    • &|^(异或)、~expr(按位取反)、<<>>
  • 条件表达式
    • 三元表达式condition?expr1:expr2
    • null判断expr1 ?? expr2expr1为非null则取expr1的值,否则取expr2的值
  • 级联调用
    • ..,严格来说是dart独有的语法糖,一般用于在一个对象上连续的调用,比如同时设置多个成员的值。..会强制返回调用者对象,而忽略掉当前调用方法的返回值。
    • ?..,若调用者可能为null,则使用这个操作符,如果调用者对象为null,则不会触发访问,非null则继续访问。这个操作符后面继续级联调用可以继续使用..,因为..都是返回调用者对象。
  • 函数调用()
  • 下标访问[]
  • 成员访问.?.
  • 条件式成员访问?.: 如果调用者对象是null,,则不会访问成员,如果是非null,则访问成员
void main() {
  var a = 1;
  var b = 2;
  var c = true;
  print(a++);
  print(a + b);
  print(a = b); //赋值表达式的值就是赋值后变量的值
  print(a == b);
  print(c ? a : b);
  assert(a is int);

  var n = 4;
  var d = 6;
  var i = 2;
  assert((n % i == 0) && (d % i == 0));
  assert(n % i == 0 && d % i == 0);

  assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');

  a = 0;
  //前缀表达式,先自增/自减 再返回值
  b = ++a;
  print(b); //1

  a = 0;
  //后缀表达式 先返回变量值 再自增/自减
  b = a++;
  print(b); //0

  a = 0;
  b = --a;
  print(b); //-1

  a = 0;
  b = a--;
  print(b); //0

  List<dynamic> list = [1, 'a', 2, 'b'];
  if (list[1] is String) {
    print((list[1] as String).toUpperCase()); //A
  }

  a = 1;
  //b未赋值才执行赋值
  b ??= 2;
  print(b); //0

  var done = false;
  var col = 3;
  print(!done && (col == 0 || col == 3)); //true

  final value = 0x22;
  final bitmask = 0x0f;

  //转化为16进制打印输出
  print((value & bitmask).toRadixString(16)); //2
  print((value & ~bitmask).toRadixString(16)); //20
  print((value | bitmask).toRadixString(16)); //2f
  print((value ^ bitmask).toRadixString(16)); //2d
  print((value << 4).toRadixString(16)); //220
  print((value >> 4).toRadixString(16)); //2

  var isPublic = false;
  var visibility = isPublic ? 'public' : 'private';
  print(visibility); //private

  String playerName(String? name) => name ?? 'Guest';
  print(playerName(null)); //Guest

  String playerNameArrow(String? name) => name != null ? name : 'Guest';
  print(playerNameArrow(null)); //Guest

  String playerNameIf(String? name) {
    if (name != null) {
      return name;
    } else {
      return 'Guest';
    }
  }

  print(playerNameIf(null)); //Guest
  //级联操作符
  var sb = StringBuffer()
    ..write('foo')
    ..write('bar')
    ..write('test');
  print(sb);//foobartest
}

6.控制流程

  • If-else
    • 条件必须为布尔值
  • For loop
    • 循环内的闭包会捕获循环变量的当前值
    • 可迭代对象可使用for-in来迭代其内部元素
  • While、do-while
    • While先判断条件,满足后再执行循环体
    • do-while先执行一次再判断条件
  • Break、continue
    • Break终止循环,continue跳过本次循环剩余代码,直接进入下一次循环
  • Switch-case
    • 使用==操作符来比较整数、字符串或编译时常量,包括枚举类型
    • 非空case语句正常情况下应以break结束,也可使用continue、throw或return来结束
    • 使用default语句来匹配其他情况
    • 空case语句会落入下方case语句
    • 可结合使用continue和label来跳转到其它case语句
    • 一般用于解释器和扫描器,编写应用时尽量少用
  • Assert
    • 只在开发模式下有效,生产模式下会被忽略
    • 条件判断失败时会抛出AssertionError异常,可通过第二个参数指定错误消息
void main() {
  var isRaining = false;
  var isSnowing = true;
  //条件必须是bool类型
  if (isRaining) {
    print('bring rain coat');
  } else if (isSnowing) {
    print('wear jacket');
  } else {
    print('put top down');
  }

  //和C一样的for循环
  var message = StringBuffer('Dart is fun');
  for (var i = 0; i < 5; i++) {
    message.write('!');
  }

  //闭包捕获值
  var callbacks = [];
  for (var i = 0; i < 2; i++) {
    callbacks.add(() => print(i));
  }
  callbacks.forEach((c) => c());

  //for-in
  var collection = [0, 1, 2];
  for (var item in collection) {
    print(item);
  }

  //while
  var isDone = false;
  while (!isDone) {
    print('Do sth');
    isDone = true;
  }

  //do-while
  var atEndOfPage = true;
  do {
    print('do sth');
  } while (atEndOfPage == false);

  //break退出循环
  var shutDownRequested = false;
  while (true) {
    if (shutDownRequested) break;
    print('process incoming requests');
    shutDownRequested = true;
  }

  //continue进入下一次循环
  var candidates = [1, 3, 5, 2, 4, 7];
  for (var i = 0; i < candidates.length; i++) {
    var candidate = candidates[i];
    if (candidate < 5) {
      continue;
    }
    print('interview');
  }

  //switch case
  var command = 'OPEN';
  switch (command) {
    case 'CLOSED':
      print('execute closed');
      //continue配合label
      continue openCase;
    case 'PENDING':
      print('execute pending');
      break;
    //空的case语句,可以fall-through到下一个case
    case 'APPROVED':
    // print('execute approved');
    // break;
    case 'DENIED':
      print('execute denied');
      break;
    //定义了label
    openCase:
    case 'OPEN':
      print('execute open');
      break;
    default:
      print('execute unknown');
  }

  var urlString = 'https://www.google.om/';
  //assert两个参数,第一个是bool表达式,第二个是可选的描述字符串
  assert(urlString.startsWith('https'),
      'URL ($urlString) should start with "https".');
}

7.异常

  • 异常表示有一些预料之外的错误发生了
  • 如果异常未被捕捉,则程序将终止运行
  • Dart内置了Exception和Error两种类型,Exception核心库异常,Error用于应用代码异常
  • 可使用throw抛出任何非null对象作为异常
  • throw语句是一个表达式
  • 使用try-catch语句来捕捉异常,以防止异常扩散,同时可使用on语句来处理特定类型的异常
  • 使用rethrow来再次抛出异常
  • 使用finally语句来执行无论是否出现异常都要运行的代码
void main() {
  void foo() => throw UnimplementedError();

  try {
    foo();
  } on UnimplementedError catch (e) {
    // on表示明确捕捉某种异常
    print('Unknown exception: $e'); //Unknown exception: UnimplementedError
  } catch (e, s) {
    // 第一个参数e是异常参数,第二个参数s异常调用栈
    print('Exception details:\n $e');
    print('Stack trace:\n $s');
  }

  void misbehave() {
    try {
      dynamic foo = true;
      print(foo++);
      //布尔值无法执行++操作
    } catch (e) {
      print(
          'misbehave() partinally handled ${e.runtimeType}'); //misbehave() partinally handled JsNoSuchMethodError
      //这里还没处理完,还需要外层继续处理,rethrow继续抛出
      rethrow;
    }
  }

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

推荐阅读更多精彩内容