代码练习:DartPad
对比语言:JavaScript
1、程序入口
JavaScript:没有入口函数。
Dart:必须有一个main()函数作为程序入口。
2、控制台输出
JavaScript:console.log("hello,world!");
Dart:print('hello,world!');
3、dart数据类型
//int (一般占8个字节)
var count = 1; or int count = 1;
//double (一般占8个字节)
var a = 0.1; or double a =0.1;
//num(int和double的父类型,一般占8个字节)
//String
var s = "dart"; or String s = 'dart';
//bool
var find = true; or bool find = true;
//List
var arr = [1,2,3,4,5];
List
List flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
//Map
var image = new Map();
var image = { 'tags': ['saturn'], 'url': '//path/to/xxx.jpg'};
var map={"a":"1","b":2,"c":3,4:5};
Map image = { 'tags': ['saturn'], 'url': '//path/to/xxx.jpg'};
注意:dart没有long和float类型
4、变量
JavaScript:无法定义变量类型,js没有这种声明方式:int count = 1。
Dart:是类型安全的,你可以明确指定某个变量的类型,如int bool String,也可以用var或 dynamic来声明一个变量。尽管类型是必须的,但是dart的某些类型注释是可选的,因为dart会执行类型推断。Dart 没有 public、private、protected 这些关键字,变量名以"_"开头意味着对它的 lib 是私有的。
例如:
//JavaScript
var name = "JavaScript";
//Dart
String name1 = 'Dart'; 或者 var name2 = 'dart';都是可以接受的,其中name2进行了类型推断。
5、默认值
JavaScript:未初始化的变量默认值是undefined。
Dart:未初始化的变量默认值是null。
注意,数字在dart语言中也被当成对象,所以即使是带有数字类型的对象没有给出初始化默认值,那么该对象值就是“null”。
//JavaScript
var name; // ==undefined
//Dart
String name; // ==null
int a; // ==null
6、检查null或零
JavaScript:1或者任何非null对象的值被视为true。
Dart:只有布尔值为true才被视为true。
// is运算符用于判断一个变量是不是某个类型的数据
// is!则是判断变量不是某个类型的数据
var s ="hello";
print(s is String);// true
var num =6;
print(num is! String);// true
// ~/是取整运算符,如果使用/则是除法运算,不取整
int k =1; int j =2;
print(k / j);// 0.5
print(k ~/ j);// 0
// as运算符类似于Java中的cast操作,将一个对象强制类型转换
(empasPerson).teach();
// ??=运算符 如果 ??= 运算符前面的变量为null,则赋值,否则不赋值
var param1 ="hello", param2 =null;
param1 ??="world";
param2 ??="world";
print("param1 = $param1");// param1 = hello
print("param2 = $param2");// param2 = world
// ?.运算符在左边为null的情况下会阻断右边函数的调用
var str1 ="hello world";
var str2 =null;
print(str1?.length);// 11
print(str2?.length);// null
print(str2.length);// 报错
//??运算符在左侧表达式为null的时候为其设置右边为默认值
print(null??false);//false
print(false??11);//false
print(true??false);//true
7、final和const
如果你绝不想改变一个变量,使用final或const,不要使用var或其他类型,一个被final修饰的变量只能被赋值一次,一个被const修饰的变量是一个编译时常量(const常量毫无疑问也是final常量)。可以这么理解:final修饰的变量是不可改变的,而const修饰的表示一个常量。
注意:实例变量可以是final的但不能是const的
说明:
var count =10;
final Num = count;// final 只能赋值一次
const Num1 =10;// const赋值必须是编译时常量
final和const的区别:
区别一:final 要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而 const 要求在声明时初始化,并且赋值必需为编译时常量。
区别二:final 是惰性初始化,即在运行时第一次使用前才初始化。而 const 是在编译时就确定值了。
8、function
//JavaScript
function fn(){
return true;
}
//Dart
// 声明返回值
int add(int a,int b){
return a + b;
}
// 不声明返回值
add2(int a,int b) {
return a + b;
}
// =>是return语句的简写
add3(a, b) => a + b;
main() {
print(add(1,2));// 3
print(add2(2,3));// 5
print(add3(1,2));// 3
}
9、命名参数、位置参数、参数默认值
命名参数
定义命名参数时,你可以以 {type paramName} 或者 {paramName: type} 两种方式声明参数,而调用命名参数时,需要以 funcName(paramName: paramValue) 的形式调用
注意:命名参数被{ }括了起来,类似于map的形式,调用的时候需要 参数名:参数值
sayHello({String name}) {
print("hello, my name is $name");
}
sayHello2({name: String}) {
print("hello, my name is $name");
}
main() {
// 打印 hello, my name is zhangsan
sayHello(name:'zhangsan');
// 打印 hello, my name is wangwu
sayHello2(name:'wangwu');}
位置参数
使用中括号[]括起来的参数是函数的位置参数,代表该参数可传可不传,位置参数只能放在函数的参数列表的最后面,如下代码所示:
sayHello(String name, int age, [String hobby]) {// 位置参数可以有多个,比如[String a, int b]
StringBuffer sb =new StringBuffer();
sb.write("hello, this is $name and I am $age years old");
if(hobby !=null) {
sb.write(", my hobby is $hobby");
}
print(sb.toString());
}
main() {
// hello, this is zhangsan and I am 20 years old
sayHello("zhangsan",20);
// hello, this is zhangsan and I am 20 years old, my hobby is play football
sayHello("zhangsan",20,"play football");
}
参数默认值
// 命名参数的默认值
int add({int a, int b =3}){// 不能写成:int add({a: int, b: int = 3})
return a + b;
}
// 位置参数的默认值
int sum(int a,int b, [int c =3]){
return a + b + c;
}
10、类
类的定义与构造方法
Dart中的类没有访问控制,所以你不需要用private, protected, public等修饰成员变量或成员函数,一个简单的类如下代码所示:
class Person{
String name;
int age;
String gender;
Person(this.name,this.age,this.gender);
sayHello() {
print("hello, this is $name, I am $age years old, I am a $gender");
}
}
上面的Person类中有3个成员变量,一个构造方法和一个成员方法,看起来比较奇怪的是Person的构造方法,里面传入的3个参数都是this.xxx,而且是没有大括号{}包裹的方法体,这种语法是Dart比较独特而简洁的构造方法声明方式,它等同于下面的代码:
Person(String name, int age,String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
要调用Person类的成员变量或成员方法,可以用下面的代码:
var p =new Person("zhangsan",20,"male");
p.sayHello();// hello, this is zhangsan, I am 20 years old, I am a male
p.age =50;
p.gender ="female";
p.sayHello();// hello, this is zhangsan, I am 50 years old, I am a female
类除了有跟类名相同的构造方法外,还可以添加命名的构造方法,如下代码所示:
class Point{
num x, y;
Point(this.x,this.y);
// 类的命名构造方法
Point.origin() {
x =0;
y =0;
}
}
main() {
// 调用Point类的命名构造方法origin()
var p =new Point.origin();
var p2 =new Point(1,2);
}
Dart中使用extends关键字做类的继承,如果一个类只有命名的构造方法,在继承时需要注意,如下代码:
class Human{
String name;
Human.fromJson(Map data) {
print("Human's fromJson constructor");
}
}
class Man extends Human{
Man.fromJson(Map data) :super.fromJson(data) {
print("Man's fromJson constructor");
}
}
由于Human类没有默认构造方法,只有一个命名构造方法fromJson,所以在Man类继承Human类时,需要调用父类的fromJson方法做初始化,而且必须使用Man.fromJson(Map data) : super.fromJson(data)这种写法,而不是像Java那样将super写到花括号中。
有时候你仅仅只是在某个类的构造方法中,调用这个类的另一个构造方法,你可以这么写:
class Point{
num x, y;
Point(this.x,this.y);
// 命名构造方法调用了默认的构造方法
Point.alongXAxis(num x) :this(x,0);}
类的成员方法
一个类的成员方法是一个函数,为这个类提供某些行为。上面的代码中已经有了一些类的成员方法的定义,这些定义方式跟Java很类似,你可以为某个类的成员变量提供getter/setter方法,如下代码:
class Rectangle{
num left, top, width, height;//
构造方法传入left, top, width, height几个参数
Rectangle(this.left,this.top,this.width,this.height);
// right, bottom两个成员变量提供getter/setter方法
num get right => left + width;
set right(num value)=> left = value - width;
num get bottom => top + height;
set bottom(num value)=> top = value - height;
}
抽象类和抽象方法
使用abstract修饰一个类,则这个类是抽象类,抽象类中可以有抽象方法和非抽象方法,抽象方法没有方法体,需要子类去实现,如下代码:
abstract class Doer{
// 抽象方法,没有方法体,需要子类去实现
void doSomething();
// 普通的方法
void greet(){
print("hello world!");
}}
class EffectiveDoer extends Doer{
// 实现了父类的抽象方法
void doSomething(){
print("I'm doing something...");
}}
运算符重载
Dart中有类似于C++中的运算符重载语法,比如下面的代码定义了一个向量类,重载了向量的+ -运算:
class Vector{
num x, y;
Vector(this.x,this.y);
Vector operator +(Vector v) =>new Vector(x + v.x, y + v.y);
Vector operator -(Vector v) =>new Vector(x - v.x, y - v.y);
printVec() {
print("x: $x, y: $y"); }}
main() {
Vector v1 =newVector(1,2);
Vector v2 =new Vector(3,4);
(v1 - v2).printVec(); // -2, -2
(v1 + v2).printVec(); // 4, 6
}
枚举类
使用enum关键字定义一个枚举类,这个语法跟Java类似,如下代码:
enum Color { red, green, blue }
mixins
mixins是一个重复使用类中代码的方式,比如下面的代码:
classA{
a() {
print("A's a()");
}
}
classB{
b() {
print("B's b()");
}
}
// 使用with关键字,表示类C是由类A和类B混合而构成
classC= A with B;
main() {
C c =new C();
c.a(); // A's a()
c.b(); // B's b()
}
静态成员变量和静态成员方法
// 类的静态成员变量和静态成员方法
class Cons{
static const name ="zhangsan";
static sayHello() {
print("hello, this is ${Cons.name}");
}
}
main() {
Cons.sayHello(); // hello, this is zhangsan
print(Cons.name); // zhangsan
}
11、异步编程
Dart和JavaScript一样,都支持单线程执行
JavaScript:promise对象表示异步操作的最终完成(或失败)及其结果值
Dart:future来表示异步操作
12、async和await
async函数声明定义了一个异步函数。
JavaScript:async函数返回一个promise,await运算符用来等待promise。
async _getIPAdress(){
const url = "https://httpbin.org/ip";
const response = await fetch(url);
const json = await respons.json();
const data = await json.origin;
console.log(data);
}
Dart:async函数返回一个future,函数的主体是稍后执行,await运算符用来等待future。
_getIPAdress() async{
final url = "https://httpbin.org/ip";
var request = await HttpRequest.request(url);
String ip = json.decode(request.response.responseText)['origin'];
print(ip);
}