V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
cmdOptionKana
V2EX  ›  Dart

最近学习 Dart 语言,分享一下心得 (入门级)

  •  
  •   cmdOptionKana · 2020-03-21 22:42:57 +08:00 · 4902 次点击
    这是一个创建于 1490 天前的主题,其中的信息可能已经有所发展或是发生改变。

    每隔一段时间,总有一种新(或较新)的语言进入大家的视线,比如 Kotlin, Rust, Go 等,我查了一下它们的 “出生日期”:

    • Rust (2011 自举, 2015 v1.0)
    • Go (2012, v1.0)
    • Kotlin (2012 开源, 2016 v1.0)
    • TypeScript (2014, v1.0)
    • Swift (2014, v1.0)
    • Dart/Flutter (2018, Dart v2.0)

    由于 Dart 1 实质上已经被放弃,Dart 2 改动很大几乎就是一种新的语言,因此这里只列出 v2.0 的日期。

    一个现象:这些新出的热门语言,都是静态类型语言,其原因可能是现代 IDE 发展起来了。 动态语言最大的优势是 “写起来省键盘”,但在类型推导与现代 IDE 的加持之下, 静态语言写起来不再繁琐,反而动态语言由于更难进行静态分析而难以享受 IDE 提供的更多好处。

    言归正传,从上面各语言的日期可见,其中 Dart 2 非常新,这使得它有机会尽情吸收其它语言的经验, 反映出现代编程语言的很多新思想。下面开始说说 Dart 语言的特点。

    一切皆对象

    甚至连数字都是对象,比如下面的例子,数字可直接调用方法:

    // int -> String
    String oneAsString = 1.toString();
    assert(oneAsString == '1');
    

    由于一切皆对象,所以任何变量的默认值都是 null,比如:

    int n;
    String s;
    assert(n == null);
    assert(s == null);
    

    代码内测试 (inline testing)

    直接在代码内写 assert(), 这个函数只在开发环境中有效,而在生产环境中会被忽略,非常适合用来做一些简单但重要的测试。

    类型推导

    以前的静态类型语言一般要求每次定义变量时,都明确写明类型,这非常繁琐。 而现在自动推导变量类型这个特性几乎成为了各种新语言的标配,不少老牌语言也在新版中加入这个特性。

    var s = 'abc';
    assert(s.runtimeType.toString() == 'String');
    

    简化访问权限

    不通过 public, protected, private 等关键词来表明成员是否对外公开,而是通过首字母来表示。 类似于 Golang, Golang 是用首字母大写表示 public, 但这被很多开发者诟病,确实会带来一些不便。 而 Dart 则是用首字母为下划线来表示 private, 这就比 Golang 那种设定舒服多了,也很符合直觉。

    // 这个例子只是为了说明访问权限,并非最佳实践。
    class MyClass {
      int _count;
      
      int get count => _count;
      set count(value) => _count = value;
    }
    

    是不是有点像 Python? 但与 Python 不同的是,_count 不是看起来像 private, 而是真的 private !

    用问号语法来简化 null 相关操作

    这也是新语言中比较流行的一种特性,确实非常好用。

    int n;
    
    // 此时 n == null, 可以赋值。
    n ??= 3;
    assert(n == 3);
    
    // 此时 n != null,保留原来的值。
    n ??= 5;
    assert(n == 3);
    
    print(1 ?? 3); // 结果为 1.
    print(null ?? 5); // 结果为 5.
    
    myObject?.someProperty
    
    // 如果没有问号语法,就会麻烦很多:
    (myObject != null) ? myObject.someProperty : null
    

    关于 null 有一个故事,他的发明者曾说这是自己犯下的 “十亿美元错误”(billion-dollar mistake)。 但这么多年过去了,新语言明明有机会采用 “无 null” 设计,却还是选择了有 null, 侧面说明这个发明还是好处多过坏处。

    处理列表的常用函数(流式处理)

    比如 filter(在 Dart 里是 where), map, any, fold 等等,以前常见于动态类型语言,但由于这些函数确实很方便, 现在很多静态语言里也是标配特性了。

      numbers
        .where((n) => n % 2 == 0)
        .forEach((n) => print(n));
    

    自动提供 getter, setter

    在下面的例子中,xy 拥有默认的 getter 和 setter,非常方便。

    class Point {
      num x, y;
      Point(this.x, this.y);
    }
    
    var p = Point(1.2, 3);
    p.y = 2.5;
    print('${p.x}, ${p.y}'); // "1.2, 2.5"
    

    Kotlin 也有类似的特性。为什么在已经有了那么多编程语言的情况下,还要不断发明新的语言呢, 原因之一就是这些新特性非常优雅:写起来更方便,读起来更清晰。

    异步编程

    • 据说是微软 C# 最先发明的 async/await 语法(我没有查证),用过都说好,甚至连 JavaScript 和 Python 都在版本升级中加入了这种语法。

    • 在本文开头列出的多种语言之中,Go 由于拥有独特的 goroutine 而不需要 async/await, 另外 Swift 虽然暂时没有这种语法,但开发者呼声很高,后续大概率会加上,列表中的其他语言全都已经具备这种语法了。

    Future main() async {
      var ver = await getVersion();
      print('In main: version is $ver');
    }
    

    胖箭头函数

    如果你曾经主要使用 JavaScript, 后来转到别的语言,那么你可能会很怀念那个可爱又实用的胖箭头。 在 Dart 里你可以再次愉快过使用它。

    class MyClass {
      int value = 0;  
      void increase() => value++; 
    }
    

    串联语法 (Cascade)

    Dart 自称是专为客户端(约等于前端)而设计的语言,而这个新颖、高效的串联语法很好地体现了什么叫做 客户端语言,这是 Dart 带给我最大的惊喜!

    querySelector('#confirm')
      ..text = 'Confirm'
      ..classes.add('important')
      ..onClick.listen((e) => window.alert('Confirmed!'));
    

    总结

    以上列举了 Dart 的一部分特性,可以看出它吸收了很多新特性,同时考虑了别的语言的使用习惯, 学起来会有一种“似曾相识”的熟悉感,用起来会有一种“要啥有啥”的爽感。

    再结合它的强大生命力:通过 Flutter 在手机端生存,通过编译为高质量的 JavaScript 代码在网页端生存, 通过编译为二进制可执行文件在桌面电脑端生存,同时也已经被 Google 官方确定为 Fuchsia 的主要编程语言之一。 因此,有兴趣的同学不妨学习一下。

    第 1 条附言  ·  2020-03-22 10:30:43 +08:00

    (漏了一个重要特性没有讲……)

    扩展方法 (extension methods)

    这是一个神奇的功能,Kotlin 里有,Rust 里也有,没有在其他语言里见过了。

    假如我们想对第三方库、或者标准库里的类增加方法,但又不能(或不想)修改其源码,没有问题,在我们自己的代码里这样写:

    extension NumberParsing on String {
      // 在这里可以当作在 String 类里面一样写代码
      int parseInt() {
        return int.parse(this);
      }
    }
    

    这样扩展之后,只要在引入了 NumberParsing 在地方,任何字符串都具有 parseInt 方法了。

    '42'.parseInt()
    
    17 条回复    2020-04-09 16:35:54 +08:00
    chinvo
        1
    chinvo  
       2020-03-21 23:13:14 +08:00 via iPhone
    最后还是要编译成 js,和 ts 比也没有太大优势
    cmdOptionKana
        2
    cmdOptionKana  
    OP
       2020-03-21 23:28:17 +08:00
    @chinvo 网页端不是 Dart 的主战场(曾经是,但早就完败了,借助 Flutter 还魂)
    itning
        3
    itning  
       2020-03-21 23:45:54 +08:00 via Android
    刚出 1.0 的时候学过,写了个 app,嵌套的无法想象
    cmdOptionKana
        4
    cmdOptionKana  
    OP
       2020-03-22 00:00:12 +08:00
    @itning 是指 Flutter 1.0 吧? Flutter 我还没学不太了解,据说嵌套是特色,但看起来 Flutter 发展得挺快的,不知道现在有什么变化没。
    fan123199
        5
    fan123199  
       2020-03-22 00:41:00 +08:00
    说起这些特性,我还是想起了 kotlin 。kotlin 的语法糖还多一些。
    fan123199
        6
    fan123199  
       2020-03-22 00:41:42 +08:00
    不过作为 Android 开发,后面还是要继续学 kotlin 。😀
    cmdOptionKana
        7
    cmdOptionKana  
    OP
       2020-03-22 01:07:34 +08:00
    @fan123199 Kotlin 添加语法特性简直疯狂,非常激进,好处是魔法多、表现力强大,缺点是需要更多时间去熟悉。Dart 还是比较克制的,学起来更轻松一些。
    laike9m
        8
    laike9m  
       2020-03-22 02:26:54 +08:00 via Android
    你列的很多特性在 Dart 1 里就有了。cascade 确实好,我也很喜欢。
    WebKit
        9
    WebKit  
       2020-03-22 03:16:25 +08:00 via Android
    @chinvo 优势还是有的,比如我这种不会 web 开发的人,做个简单的 app 然后编译一下,全平台都走了。有点类似 rn,不过流畅性上比 rn 强很多
    sagaxu
        10
    sagaxu  
       2020-03-22 08:43:18 +08:00 via Android
    干不了服务端
    secondwtq
        11
    secondwtq  
       2020-03-22 10:34:30 +08:00
    有几点我觉得楼主有必要提一下(如果有的话):
    * 继承(单继承?多继承?能不能继承 int 等类型?)
    * 有没有类似 Java/Go Interface 的机制?
    * Operator Overloading/Ad-hoc Polymorphism
    * 有没有泛型?或者干脆 大 道 至 简?
    mrcn
        12
    mrcn  
       2020-03-22 10:45:07 +08:00 via Android
    Append 的那个 ,C#也有
    mxalbert1996
        13
    mxalbert1996  
       2020-03-22 11:50:19 +08:00 via Android
    扩展方法我见过的最强大的还是 Swift
    cmdOptionKana
        14
    cmdOptionKana  
    OP
       2020-03-22 12:38:14 +08:00   ❤️ 1
    @secondwtq

    Dart 是单继承的,然后提供 Mixin 来解决一些原本需要多继承的场景(这好像也是目前比较流行的方案,因为多继承用起来不容易处理好)。

    不能继承 int, double, bool, Null 和 String,但可以扩展方法。

    Dart 的每一个 class 都会自动成为 interface (即每一个 class 都可以被 implement ),但一般不建议 implement 普通类,官方建议需要 interface 的时候就用 abstract class 。

    有 Operator Overloading 。(那个 ad-hoc Polymorphism 我不懂…)

    有泛型的,除了 Go 谁还敢玩大道至简(狗头)。
    cmdOptionKana
        15
    cmdOptionKana  
    OP
       2020-03-22 12:41:02 +08:00
    @mrcn
    @mxalbert1996 原来扩展方法也不罕见啊,第一次见的时候感觉特别强大,也很实用。

    (现在各种主流语言单说语法的话还真的非常共通)
    Tecrafter
        16
    Tecrafter  
       2020-03-22 13:08:40 +08:00
    有预感使用 Dart 的 Flutter 会一统大前端。
    w4mxl
        17
    w4mxl  
       2020-04-09 16:35:54 +08:00
    @cmdOptionKana #7 很同意~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2798 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 14:42 · PVG 22:42 · LAX 07:42 · JFK 10:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.