Flutter快学快用01 Flutter Dart 语法:从 JavaScript 角度学习 Dart

287 篇文章 96 订阅

本课时我主要从 JavaScript 角度来讲解如何学习 Dart。

在学习本课时之前,你需要有一定的 JavaScript 基础,比如基础数据类型、函数、基础运算符、类、异步原理和文件库引入等,这也是 JavaScript 的核心知识点。接下来将通过对比与 JavaScript 的差异点来学习 Dart 语言。

基础数据类型

与 JavaScript 相比较,我们整体上看一下图 1 两种语言的对比情况,相似的部分这里就不介绍了,比如 Number 和 String,其使用方式基本一致。下面主要基于两者的差异点逐一讲解,避免混淆或错误使用。

image (4).png

图 1 Dart 与 JavaScript 基础数据类型对比

Symbol 的区别

在 JavaScript 中,Symbol 是将基础数据类型转换为唯一标识符,核心应用是可以将复杂引用数据类型转换为对象数据类型的键名。

在 Dart 中,Symbol 是不透明的动态字符串名称,用于反映库中的元数据。用 Symbol 可以获得或引用类的一个镜像,概念比较复杂,但其实和 JavaScript 的用法基本上是一致的。例如,下面代码首先 new 了一个 test 为 Map 数据类型,设置一个属性 #t(Symbol 类型),然后分别打印 test、test 的 #t、test 的 Symbol("t") 和 #t。

void main() {
  Map test = new Map();
  test[#t] = 'symbol test';
  print(test);
  print(test[#t]);
  print(test[Symbol('t')]);
  print(#t);
}

运行代码结果如下:

flutter: {Symbol("t"): symbol test}
flutter: symbol test
flutter: symbol test
flutter: Symbol("t")

其中,test 包含了一个有 Symbol 为对象的 Key,value 为 symbol test 字符串的对象。test 的 #t 与 Symbol("t") 打印结果一致,#t 则与 Symbol("t') 是同一形式。

在上面的代码示例中,两者的核心在使用上基本是一致的,只是在理解方面相对不一样。Symbol 在 Dart 中是一种反射概念,而在 JavaScript 中则是创建唯一标识的概念。

Undefined 和 Null

由于 Dart 是静态脚本语言,因此在 Dart 中如果没有定义一个变量是无法通过编译的;而 JavaScript 是动态脚本语言,因此存在脚本在运行期间未定义的情况。所以这一点的不同决定了 Dart 在 Undefined 类型上与 JavaScript 的差异。

null 在 Dart 中是的确存在的,官网上是这样解释的,null 是弱类型 object 的子类型,并非基础数据类型。所有数据类型,如果被初始化后没有赋值的话都将会被赋值 null 类型。

下面的代码,首先定义了一个弱类型 number,其次定义了 int 类型的 num2,number 类型的 num1 以及 double 类型的 num3 ,最后我们打印出这些只定义了未被赋值的值。

var number;
int num2;
num num1;
double num3;
print('number is var:$number,num2 is int:$num2,num2 is num:$num1,num3 is double:$num3');

可以看到运行结果如下:

flutter: number is var:null,num2 is int:null,num2 is num:null,num3 is double:null

从运行结果我们可以看到,代码中声明了变量,但未赋值的变量在运行时都会被赋值为 null,这就是 Dart 中 null 类型存在的目的。

Map 和 List

Map 和 List 与 JavaScript 中的 Array 和 Map 基本一致,但在 JavaScript 中不是基本数据类型,都属于引用数据类型。因此也就是分类不同,但在用法和类型上基本没有太大差异。

弱类型(var、object 和 dynamic)

相对 JavaScript 而言,Dart 也存在弱类型(可以使用 var、object 和 dynamic 来声明),不过在这方面为了避免弱类型导致的客户端(App)Crash 的异常,Dart 还是对弱类型加强了校验。

var 数据类型声明,第一次赋值时,将其数据类型绑定。下面代码使用 var 声明了一个弱类型 t,并赋值 String 类型 123,而接下来又对 t 进行其他类型的赋值。

var t = '123';
t = 123;

这样的代码在 Dart 编译前就会报错,因为 t 在一次 var 赋值时就已经被绑定为 String 类型了,再进行赋值 Number 类型时就会报错。

Assign value to new local variable

object 可以进行任何赋值,没有约束,这一点类似 JavaScript 中的 var 关键词赋值。在编译期,object 会对数据调用做一定的判断,并且报错。例如,声明时为 String 类型,但是在调用 length 时,编译期就会报错。如果数据来自接口层,则很容易导致运行时报错。因此这个要尽量减少使用,避免运行时报错导致客户端(App)Crash 的异常。

dynamic 也是动态的数据类型,但如果数据类型调用异常,则只会在运行时报错,这点是非常危险的,因此在使用 dynamic 时要非常慎重。

基础运算符

两种语言的基础运算符基本都一致。由于 Dart 是强数据类型,因此在 Dart 中没有 “=== ”的运算符。在 Dart 中有一些类型测试运算符,与 JavaScript 中的类型转换和 typeof 有点相似。

这里也介绍一些 Dart 中比较简洁的写法:

  • ?? 运算符,比如,t??'test' 是 t!= null ? t : 'test' 的缩写;

  • 级联操作,允许对同一对象或者同一函数进行一系列操作,例如下面代码的 testObj 对象中有三个方法 add()、delete() 和 show(),应用级联操作可以依次进行调用。

testObj.add('t')
..delete('d')
..show()

函数

从我的理解来说,两者区别不大。箭头函数、函数闭包、匿名函数、高阶函数、参数可选等基本上都一样。在 Dart 中由于是强类型,因此在声明函数的时候可以增加一个返回类型,这点在 TypeScript 中的用法是一致的,对于前端开发人员来说,没有太多的差异点。

类的概念在各种语言上大部分都是一致的,但在用法上可能存在差异,这里着重介绍一下 Dart 比较特殊的一些用法。

命名构造函数

Dart 支持一个函数有多个构造函数,并且在实例化的时候可以选择不同的构造函数。

下面的代码声明了一个 Dog 类,类中有一个 color 变量属性和两个构造函数。red 构造函数设置 Dog 类的 color 属性为 red,black 构造函数设置 Dog 类的 color 属性为 black。最后在 main 函数中分别用两个构造函数创建两个实例,并分别打印实例的 color 属性。

class Dog {
  String color;
  Dog.red(){
    this.color = 'red';
  }

Dog.black(){
this.color = ‘black’;
}
}
void main(List<String> args) {
Dog redDog = new Dog.red();
print(redDog.color);

Dog blackDog = new Dog.black();
print(blackDog.color);
}

运行代码后输出了两种颜色,即 red 和 black。就代码而言,我们可以应用同一个类不同的构造函数实现类不同场景下的实例化。

访问控制

默认情况下都是 public,如果需要设置为私有属性,则在方法或者属性前使用 “_”。

抽象类和泛型类

抽象类和其他语言的抽象类概念一样,这里在 JavaScript 中没有这种概念,因此这里稍微提及一下,主要是实现一个类被用于其他子类继承,抽象类是无法实例化的。

下面的代码使用关键词 abstract 声明了一个有攻击性的武器抽象类,包含一个攻击函数和一个伤害力获取函数,Gun 和 BowAndArrow 都是继承抽象类,并需要实现抽象类中的方法。

abstract class AggressiveArms {
  attack();
  hurt();
}
class Gun extends AggressiveArms {
  attack() {
    print("造成100点伤害");
  }
  hurt() {
    print("可以造成100点伤害");
  }
}
class BowAndArrow extends AggressiveArms {
  attack() {
    print("造成20点伤害");
  }
  hurt() {
    print("可以造成20点伤害");
  }
}

泛型类,主要在不确定返回数据结构时使用,这点与 TypeScript 中的泛型概念一样。

在下面的代码中,我们不确定数组中存储的类型是 int 还是 string,又或者是 bool,这时候可以使用泛型 来表示。在使用泛型类的时候可以将设定为自己需要的类型,比如下面的 string 调用和 int 调用。

class Array<T> {
  List _list = new List<T>();
  Array();
  void add<T>(T value) {
    this._list.add(value);
  }
  get value{
    return this._list;
  }
}
void main(List<String> args) {
  Array arr = new Array<String>();
  arr.add('aa');
  arr.add('bb');
  print(arr.value);

Array arr2 = new Array<int>();
arr2.add(1);
arr2.add(2);
print(arr2.value);
}

库与调用

Dart 库管理

Dart 和 JavaScript 一样,有一个库管理资源(pub.dev)。你可以在这里搜索找到你想要的一些库,接下来只要在 Dart 的配置文件 pubspec.yaml 中增加该库即可。这点类似于在 JavaScript 的 package.json 中增加声明一样,同样也有 dependencies 和 dev_dependencies。

增加类似的数据配置,如下代码:

dependencies:
  cupertino_icons: ^0.1.2
  dio: ^3.0.4
  image_test_utils: ^1.0.0
dev_dependencies:
  flutter_test:
    sdk: flutter
开发 Dart 库

Dart 也支持开发者自己开发一些库,并且发布到 pub.dev 上,这点基本上和 npm 管理一致,这里我只介绍 pub.dev 库的基本格式。

dart_string_manip
├── example
|  └── main.dart
├── lib
|  ├── dart_string_manip.dart
|  └── src
|     ├── classes.dart
|     └── functions.dart
├── .gitignore
├── .packages
├── LICENSE
├── README.md
├── pubspec.lock
└── pubspec.yaml

对于前端开发人员来说,这个结构和我们所看到的 npm 模块很相似,pubspec 和 package 很相似,核心是 lib 中的库名对应的库文件 .dart,该文件是一个 dart 类。类的概念上面已经介绍过了,将私有方法使用 "_" 保护,其他就可以被引用该库的模块调用,如果是自身库的一些实现逻辑,可以放在 src 中。

开发完成该库以后,如果需要发布到 pub.dev,则可以参照官网的说明,按步骤进行即可。

Dart 调用库

这里引入库的方式也与 ES6 的 import 语法很相似。先看看下面的一个例子,其目的是引入 pages 下的 homepage.dart 模块。

import 'package:startup_namer/pages/homepage.dart';

在上面的例子中,import 为关键词,package 为协议,可以使用 http 的方式,不过最好使用本地 package 方式,避免性能受影响。接下来的 startup_namer 为库名或者说是该项目名,pages 为 lib 下的一个文件夹,homepage.dart 则为具体需要引入的库文件名。

当然这里也可以使用相对路径的方式,不过建议使用 package 的方式,以保持整个项目代码的一致性,因为对于第三方模块则必须使用 package 的方式。

总结

本课时首先介绍了 Dart 基础数据类型、基础运算符、类以及库与调用。然后通过对比 JavaScript 的一些特殊差异性,来加深前端开发人员对 Dart 语言编程的理解。相信你通过本课时的学习,可以掌握 Dart 的编程,并且能够写一些 Dart 的第三方库。

下一课时,我将介绍 Dart 的事件循环机制,掌握了其核心运行机制原理,才能编写出更高效、更有质量的代码。

点击这里下载本课时源码,Flutter 专栏,源码地址:https://github.com/love-flutter/flutter-column


精选评论

**涛:

我是学习iOS的没有JavaScript的基础,请问大神们 class Array<T> {

  List _list = new List<T>();
  Array();
  void add<T>(T value) {
    this._list.add(value);
  }
  get value{
    return this._list;
  }
}
这段代码中 Array();  什么含义?  

    讲师回复:

    如果你是值括号里面的Array()的话,代表的是构造函数,我们定义了一个Array类,里面包含了一个构造函数。如果我们希望Array类有一些初始化的参数,可以在构造函数中设置初始值。例如这段代码:class Array {
List _list = new List();
Array(this._list);
void add(T value) {
this._list.add(value);
}
get value{
return this._list;
}
}
void main() {
Array arr = new Array([1, 2]);
arr.add(1);
print(arr.value);
}

*瑞:

打卡1

**柳:

老师讲的很好,👍

**0390:

用var声明的变量不能进行隐式转换类型,为什么还说它是弱类型?

    讲师回复:

    var 类型可赋予不同类型的值,例如我们可以 var fl = ‘a’; 也可以 var flu = 10; 也可以 var flut = []; 你所看到的 var 并不能准确的表明该变量的数据类型。只是说 var 定义初始化了类型以后是不可更改的。

**8755:

开始咯😇

**的阿科:

本课时首先介绍了 Dart 基础数据类型、基础运算符、类以及库与调用。然后通过对比 JavaScript 的一些特殊差异性,来加深前端开发人员对 Dart 语言编程的理解。相信你通过本课时的学习,可以掌握 Dart 的编程,并且能够写一些 Dart 的第三方库。

**博:

老师,flutter为啥不选javascript作为编译语言?

    讲师回复:

    从历史原因讲,Dart 是 google 推出的,而 Flutter 也是 google自家产品。从区别上讲,在某些方面Javascript和Dart还是有区别,比如Javascript是动态脚本语言,而Dart可以支持AOT和JIT两种模式,从而在性能上有一定的优化提升,其次Javascript是弱类型语言,而Dart是强类型,等等,所以还是存在一些区别。

*晗:

这里的所有new 关键字是不是也可以省略了

    讲师回复:

    很棒,涉及到new的这部分都可以省略,一般情况下用了analyzer都会提示去掉new。

**玲:

泛型中加个Array()是啥意思?

    讲师回复:

    这个已经有同学问过,Array()是构造函数,具体可以看下上面评论回复中,我举的例子。

**涛:

Dart中不是没有Array吗,而是用List代替,为什么在泛型那个例子中有class Array<T> {

}这样的代码

    讲师回复:

    这里的是意思是定义一个Array类,可以用List来定义一个Array类。

*斌:

打卡打卡

**0508:

Flutter和Dart是什么关系啊?

    讲师回复:

    类似于React和Javascript的关系,flutter是技术框架,而dart是flutter中的编程语言

*铎:

打卡

*颖:

Dart中symbol有哪些使用场景呢?

    讲师回复:

    通俗易懂一点说就是,symbol创建了一个唯一索引的内存地址,你可以通过内存地址访问到相应的。目前这个在开发应用过程基本不会使用到这个类型。在实际应用过程中,大家可以参考Javascript中的用法,核心还是在属性名的使用,就是使用Symbol类型作为属性,在需要重名属性名的情况下。其次在反射方面,在dart:mirrors必须传入Symbol类型,可以看下这个例子,https://www.tutorialspoint.com/dart_programming/dart_programming_symbol.htm。

**2833:

打卡

**民:

打卡

*涛:

从头学

**泽:

老师 ,这个不透明类型是指什么? 像oc也有好多opaque type ,

An opaque type that represents a method in a class definition
不明白什么

    讲师回复:

    不透明可以理解为,在计算机处理中是有一个内存地址,而如何将这个内存地址使用可见字符来描述,这就是Symbol做的一个桥梁。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

办公模板库 素材蛙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值