Dart 语言对比


Dart官网
DartPad 在线编写和运行Dart的平台 学习 Dart  这个平台就够了
The Dart SDK has the libraries and command-line tools that you need to develop Dart web,
command-line, and server apps. To get the Dart SDK, see Get Dart.
If you’re developing only mobile apps, then you don’t need the Dart SDK;
just install Flutter.
To learn about other tools you can use for Dart development, see the Dart tools page.

Dart变量声明

var 接收任何类型的变量  Dart中var变量一旦赋值 类型确定 不能改变
var t;
t = "hi world";
t = 1000;// dart报错 变量t的类型 确定为String 不能 更改其类型
Dart 强类型语言 任何变量都 有确定类型
Dart var声明变量后 Dart在编译时 根据第一次赋值数据的类型 推断其类型
编译后其类型就被确定 而JavaScript弱类型语言 var只是变量的声明方式

dynamic和Object

Object是Dart所有对象的根基类 所有类型都是Object的子类(包括Function和Null)
任何类型的数据都可以赋值给Object声明的对象.
dynamic与var一样是关键词 声明的变量 赋值任意对象
dynamic与Object相同之处在 声明的变量 可改变赋值类型
dynamic t;
Object x;
t = "hi world";
x = 'Hello Object';//下面代码没有问题
t = 1000;
x = 1000;

dynamic与Object不同
dynamic 声明的对象编译器会提供所有可能的组合
Object声明的对象只能使用Object的属性与方法
dynamic a;
Object b;
main() {
 a = "";
 b = "";
 printLengths();
}
printLengths() { // no warning
 print(a.length); // warning:
 // The getter 'length' is not defined for the class 'Object'
 print(b.length);
}
变量a不会报错 变量b编译器会报错
dynamic的这个特性与Objective-C中的id作用很像
dynamic的这个特点使得 容易引入一个运行时错误
final和const
未打算更改一个变量 使用 final 或 const  不是var 也不是一个类型
final 变量只能被设置一次
区别
const 变量是编译时常量
final  变量在第一次使用时被初始化
被final或者const修饰的变量 变量类型可以省略
//可省略String这个类型声明
final str = "hi world";//final String str = "hi world";
const str1 = "hi world";//const String str1 = "hi world";

Dart函数

Dart是真正的面向对象的语言
Dart函数也是对象 且有一个类型Function
函数可赋值给变量或作为参数传递给其他函数 函数式编程的典型特征
函数声明
bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}
Dart函数声明
如果没有显式声明返回值类型 默认当做dynamic处理
函数返回值没有类型推断
typedef bool CALLBACK();//不指定返回类型 默认 dynamic 不是bool
isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}
void test(CALLBACK cb){
   print(cb());
}//报错 isNoble不是bool类型
test(isNoble);

只包含一个表达式的函数 简写语法
bool isNoble(int atomicNumber) => _nobleGases [ atomicNumber ] != null ;
函数作为变量
var say = (str){
  print(str);
};
say("hi world");
函数作为参数传递
void execute(var callback) {
    callback();
}
execute(() => print("xxx"))
可选的位置参数包装一组函数参数
用[]标记为可选的位置参数 并放在参数列表的最后面
String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}
一个不带可选参数调用这个函数
say('Bob', 'Howdy'); //结果是  Bob says Howdy
用第三个参数调用这个函数
say('Bob', 'Howdy', 'smoke signal'); //结果是 Bob says Howdy with a smoke signal
可选的命名参数
定义函数时 使用{param1, param2, …} 放在参数列表的最后面 用于指定命名参数
//设置[bold]和[hidden]标志
void enableFlags({bool bold, bool hidden}) {    // ...
}
调用函数时 可以使用指定命名参数
paramName: value
enableFlags(bold: true, hidden: false);
可选命名参数在Flutter中使用非常多
不能同时使用可选的位置参数和可选的命名参数

Dart异步支持

Dart类库 非常多的返回Future或者Stream对象的函数
这些函数被称为异步函数
异步函数只会在设置好耗时操作之后返回 比如IO操作
而不是等到这个操作完成

async和await关键词支持异步编程
允许 写出和同步代码很像的异步代码

Future
Future与JavaScript中的Promise相似 表示一个异步操作的最终完成(或失败)及其结果值的表示
用于处理异步操作
异步处理成功了就执行成功的操作
异步处理失败了就捕获错误或者停止后续操作
Future只会对应一个结果 要么成功 要么失败
Future所有API的返回值 仍然是一个Future对象 所以可以链式调用

Future.then
使用Future.delayed 创建一个延时任务
实际场景是一个耗时任务 比如一次网络请求
即2秒后返回结果字符串"hi world!" 然后 在then中接收异步结果并打印结果
Future.delayed(new Duration(seconds: 2),(){
    return "hi world!";
}).then((data){
    print(data);
});

Future.catchError
如果异步任务发生错误 可以在catchError中捕获错误
Future.delayed(new Duration(seconds: 2),(){//return "hi world!";
    throw AssertionError("Error");  
}).then((data){//执行成功会走到这里  
    print("success");
}).catchError((e){//执行失败会走到这里  
    print(e);
});
异步任务中抛出一个异常
then回调函数将不会被执行
取而代之的是 catchError回调函数被调用

并不是只有 catchError回调才能捕获错误
then方法还有一个可选参数onError 也可以捕获异常
Future.delayed(new Duration(seconds: 2), () {//return "hi world!";
    throw AssertionError("Error");
}).then((data) {
    print("success");
}, onError: (e) {
    print(e);
});

Future.whenComplete
异步任务执行成功或失败都需要做一些事的场景
比如在网络请求前弹出加载对话框
在请求结束后关闭对话框
有两种方法
第一种是分别在then或catch中关闭对话框
第二种是用Future的whenComplete回调
Future.delayed(new Duration(seconds: 2),(){//return "hi world!";
    throw AssertionError("Error");
}).then((data){ //执行成功会走到这里
    print(data);
}).catchError((e){ //执行失败会走到这里   
    print(e);
}).whenComplete((){
    //无论成功或失败都会走到这里
});

Future.wait

等待多个异步任务都执行结束后才进行操作
比如 界面 需要先分别从两个网络接口获取数据 获取成功后 需要将两个接口数据进行特定的处理后再显示到UI界面上
Future.wait 接受一个 Future 数组参数
只有数组中所有Future执行成功后
才会触发then的成功回调
只要有一个Future执行失败 就会触发错误回调
模拟Future.delayed 来模拟两个数据获取的异步任务
等两个异步任务都执行成功时
将两个异步任务的结果拼接打印出来
Future.wait([// 2秒后返回结果  
    Future.delayed(new Duration(seconds: 2), () {
        return "hello";
    }),// 4秒后返回结果  
    Future.delayed(new Duration(seconds: 4), () {
        return " world";
    })
]).then((results){
    print(results[0]+results[1]);
}).catchError((e){
    print(e);
});执行上面代码 4秒后 会在控制台中看到hello world

Async/await

Dart的async/await 和JavaScript中的async/await功能和用法一样
回调地狱(Callback Hell)
如果代码中有大量异步逻辑 且大量异步任务依赖其它异步任务的结果时
会出现Future.then回调中套回调
比如有个需求场景是用户先登录
登录成功后获得用户ID 然后通过用户ID 再去请求用户个人信息
获取到用户个人信息后 为了使用方便 需要将其缓存在本地文件系统
//先分别定义各个异步任务
Future<String> login(String userName, String pwd){//用户登录
};
Future<String> getUserInfo(String id){...//获取用户信息
};
Future saveUserInfo(String userInfo){...// 保存用户信息
};
执行整个任务流
login("alice","******").then((id){//登录成功后通过 id获取用户信息    
    getUserInfo(id).then((userInfo){//获取用户信息后保存
        saveUserInfo(userInfo).then((){ //保存用户信息 接下来执行其它操作
            ...
        });
    });
})
业务逻辑有大量异步依赖将会出现 回调中套回调
过多的嵌套导致代码可读性下降 出错率提高 难维护
被称为回调地狱(Callback Hell)
JavaScript回调地狱 是JavaScript被吐槽最多的点
但ECMAScript6和ECMAScript7标准发布后 得到了解决
而解决回调地狱的两大神器正是ECMAScript6引入了Promise
及ECMAScript7中引入的async/await

Dart完全平移了JavaScript的这两者
Future相当于Promise
而async/await连名字都没改
通过Future和async/await 消除上面示例中的嵌套问题
使用Future消除Callback Hell
login("alice","******").then((id){
  return getUserInfo(id);
}).then((userInfo){
    return saveUserInfo(userInfo);
}).then((e){//执行接下来的操作
}).catchError((e){//错误处理  
    print(e);
});
Future 的所有API的返回值仍然是一个Future对象 可进行链式调用
如果在then中返回的是一个Future的话
该future会执行 执行结束后会触发后面的then回调
这样依次向下 就避免了层层嵌套
使用async/await消除callback hell
通过Future回调中再返回Future的方式虽然能避免层层嵌套
但是还是有一层回调 有没有一种方式让 可以像写同步代码那样来执行异步任务
而不使用回调的方式?
答案是肯定的 这就要使用async/await了
task() async {
    try{
        String id = await login("alice","******");
        String userInfo = await getUserInfo(id);
        await saveUserInfo(userInfo); //执行接下来的操作   
    } catch(e){    //错误处理   
        print(e);   
    }  
}
async 用来表示函数是异步的 定义的函数会返回一个Future对象 可以使用then方法添加回调函数
await 后面是一个Future 表示等待该异步任务完成 异步完成后才会往下走
await必须出现在 async 函数内部
通过async/await将一个异步流用同步的代码表示出来了
无论是在JavaScript还是Dart中 async/await都只是一个语法糖
编译器或解释器最终都会将其转化为一个Promise(Future)的调用链

Stream接收异步事件数据

和Future 不同的是 接收多个异步操作的结果(成功或失败)
在执行异步任务时 通过多次触发成功或失败事件来传递结果数据或错误异常
Stream 常用于会多次读取数据的异步任务场景 如网络内容下载、文件读写等
Stream.fromFutures([// 1秒后返回结果
    Future.delayed(new Duration(seconds: 1), () {
        return "hello 1";
    }),    // 抛出一个异常
    Future.delayed(new Duration(seconds: 2),(){
        throw AssertionError("Error");
    }),    // 3秒后返回结果
    Future.delayed(new Duration(seconds: 3), () {
        return "hello 3";
    })
]).listen((data){
    print(data);
}, onError: (e){
    print(e.message);
},onDone: (){

});
依次会输出
I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3
既然Stream可以接收多次事件 那能不能用Stream来实现一个订阅者模式的事件总线

Dart和Java及JavaScript对比

强类型语言和弱类型语言的典型代表 且Dart 语法 借鉴了Java和JavaScript
Dart vs Java

语法层面
Dart比Java更有表现力
在VM层面 Dart VM在内存回收和吞吐量都进行了反复的优化
只要Dart语言能流行 VM的性能就不用担心
Google在Go(没用VM但有GC)、JavaScript(v8)、Dalvik(Android上的Java VM)上已经有了很多技术积淀
Dart在Flutter中已经可以将GC做到10ms以内 所以Dart和Java相比 决胜因素并不在性能方面
最重要的是Dart对函数式编程支持要远强于Java(目前只停留在Lambda表达式)
Dart目前真正的不足是生态

Dart vs JavaScript
JavaScript弱类型一直被抓短 所以TypeScript、CoffeeScript甚至是Facebook的flow(不算JavaScript的一个超集
但也通过标注和打包工具提供了静态类型检查)才有市场

JavaScript是动态化支持最好的脚本语言
在JavaScript中 可以给任何对象在任何时候动态扩展属性

JavaScript的强大的动态化特性是把双刃剑
太过灵活导致代码很难预期 无法限制不被期望的修改
在Flutter中 Dart几乎放弃了脚本语言动态化的特性
不支持反射 也不支持动态创建函数等
Dart在2.0强制开启了类型检查(h2 Mode)
类型安全层面
Dart和TypeScript、CoffeeScript是差不多的
综合起来看
Dart既能进行服务端脚本、APP开发、web开发 这就有优势了!