最近在学flutter,发现flutter的编程语言Dart和Javascript有诸多类似,对于前端开发者而已好处在于有JavaScript经验学习Dart会快一些,缺点在于容易搞混这两种语言。所以在学习的过程当中记录了一下Javascript和Dart的对比,方便记忆。javascript
Javascript:前端
JS不像不少语言有一个main()
函数做为程序入口,JS并无标准的程序入口,JS会从代码的第一行开始执行(在执行以前会有一些预处理的工做,好比变量提高和函数提高)。在实际项目中咱们一般会有一个index.js
这样的入口文件。java
Dart:express
Dart有一个标准的程序入口:编程
main(){ }
Javascript:json
JS 有 8 种内置数据类型,分别为:闭包
基本类型:app
true
和false
null
Undefined
类型-(2^53-1) ~ 2^53 - 1
,能够为整数和小数const x = 2983479827349701793697123n
1 + 2 = ${1+2}
`其中 7 个基本类型的值是不可变的(immutable value)。Object 用来定义复杂数据类型,JS内置了一些复杂类型好比:Function
、Date
、Array
、Map
、Set
等。异步
Dart:async
Dart 也有 8 种内置数据类型:
true
和false
Number:数字类型,又分为int和double类型
-2^63 ~ 2^63 - 1
'${expression}'
,当expression是一个标识符时可省略{}
var arr = [1, 2, 3]
var countries = {'china', 'usa', 'russia', 'german'}
Map:与 JavaScript 的 Map 相似,表示一组键值对的集合,其中键必须惟一,键和值均可觉得任意类型,例如:
var gifts = { 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' };
Runes:表示字符串的 UTF-32 编码值(Unicode 为世界上全部书写系统中使用的每一个字母,数字和符号定义了惟一的数值。因为 Dart 字符串是 UTF-16 编码的序列,所以在字符串中表示 32 位 Unicode 值须要特殊的语法),例如:
Runes input = new Runes('\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); print(new String.fromCharCodes(input)); // ♥ 😅 😎 👻 🖖 👍
a
, b
, c
代替原有 class、function、variable 的名称),依然能经过标识符的 Symbol 去访问相关的成员。与 JS 不一样的是 Dart 种全部类型都是 class,全部的值都是 class 的实例,而全部的 class 都继承于Object
类。
JavaScript:
JS中的变量为动态类型,定义变量不须要也没法指定类型。
var name = 'Javascript';
变量可任意赋值其余类型的值。
name = 123; // 正确
Dart:
Dart中的变量为静态类型,定义变量须要指定类型,或者由编译器进行类型推断。
String name = 'Dart'; // 指定类型 var name2 = 'flutter'; // 推断类型为String
变量类型一旦肯定则不能赋值其余类型的值。
name = 123; // 错误
若是不想指定类型或者类型未知,可使用dynamic
定义动态类型的变量:
dynamic name = 'something';
Javascript:
若变量未初始化,默认值为undefined
。
Dart:
无论何种类型,默认值都为null
。
Javascript:
在 Javascript 中有七种值会被断定为假值,除此以外都是真值,其中假值分别为:
false
:关键词false
0
:数字 00n
:BigInt 0''
, ""
, ``:空字符串null
:空值undefined
:初始值NaN
:not a numberDart:
只有布尔值true
是真值,其他都是假值。
Javascript:
ES6 中引入了const
来定义常量。
const name = 'JavaScript';
Dart:
Dart 中有两种方式定义常量:final
和const
。
区别在于:
final
定义的常量只在使用时才会初始化和分配内存const
用于定义编译时常量(compile-time constant),即在编译时就初始化,且值为不变值(constant value,好比基本数据类型和String
)。final
能够用于类实例的属性(instance variable)而const
不能够final pi = 3.1415926; const g = 9.8;
在JS和Dart中,函数都是 “first-class object”,意味着函数能够像普通对象同样赋值给变量、做为参数传递。
Javascipt:
function fn(a, b){ return a + b; }
Dart:
int sum(int a, int b){ return a + b; }
或者省略掉返回值(会下降可读性,不推荐):
sum(a, b){ return a + b; }
javascript:
JS中匿名函数有多种用途:
var sum = function(a, b) { return a + b };
setTimeout(function () { console.log('hello world'); }, 1000);
(function () { console.log('hello world'); })();
Dart:
Dart一样支持匿名函数,且用法与JS相似。
Function sum = (int a, int b) { return a + b; };
var list = ['apples', 'bananas', 'oranges']; list.forEach((item) { print('${list.indexOf(item)}: $item'); });
Javascript:
ES6 中引入了箭头函数:
(a, b) => { return a + b; }
或者简写为:
(a ,b) => a + b;
Dart:
Dart 中也有相似的语法:
int sum(int a, int b) => a + b;
或者,省略返回值和参数类型:
sum(a, b) => a + b;
Dart 和 JS 中箭头函数的区别在于:
int sum(int a, int b) => { int c = a + b; return c; }
JavaScript:
ES6 中引入了参数解构的特性(parameter destructuring)。经过传入一个对象,并对其进行解构赋值来实现命名参数的特性。示例以下:
function sum({ a, b }) { return a + b; } sum({ a: 3, b: 4 }); // 7
Dart:
Dart原生支持命名参数:
int sum({int a, int b}) { return a + b; } sum(a: 3, b: 4); // 7
JavaScript:
JS中全部的参数都是可选参数。这能够理解为JS的特性也能够说是设计缺陷,毕竟有时候漏传参数又不报错容易致使各类问题。
Dart:
在Dart中,常规的参数都是必传的,而命名参数和位置参数(positional parameter)均可以是可选参数。固然方法体中须要增长容错逻辑,已防止可选参数不传致使的报错。
int sum({int a, int b}) { if (b == null) b = 0; return a + b; } sum(a: 3); // 3 sum(a: 3, b: 4); // 7
int sum(int a, [int b, int c]) { if (b == null) b = 0; if (c == null) c = 0; return a + b + c; } sum(3); // 3 sum(3, 4); // 7 sum(3, 4, 5); // 12
JavaScript:
JS中实现参数默认有新旧两种方法:
function sum(a, b){ if(typeof a === 'undefined') a = 0; if(typeof b === 'undefined') b = 0; return a + b; } sum(); // 0 sum(3); // 3 sum(3,4); // 7
function sum(a = 0, b = 0){ return a + b; } sum(); // 0 sum(3); // 3 sum(3,4); // 7
Dart:
Dart中也支持默认参数,可是只有命名参数和位置参数能够设置默认值:
int sum({int a, int b = 0}) { return a + b; } sum(a: 3); // 3 sum(a: 3, b: 4); // 7
int sum(int a, [int b = 0, int c = 0]) { return a + b + c; } sum(3); // 3 sum(3, 4); // 7 sum(3, 4, 5); // 12
闭包本质上也是函数,放在和函数并列的位置讲是为了凸显闭包的重要性,也方便你们阅读。
JS和Dart都有闭包,本质上是由于它们都使用词法做用域(Lexical Scope)且能够在函数内部再定义函数。所谓的词法做用域又叫静态做用域(Static Scope),也是大部分编程语言采用的机制,即做用域仅由代码的本文结构肯定,好比内层大括号中能够访问外层大括号中定义的变量,而外层大括号不能访问内层大括号中定义的变量。与词法做用域相对的是动态做用域(Dynamic Scope),动态做用域不取决于代码的文本结构而是程序的执行状态、执行上下文。
当在函数内部再定义函数,而内部函数使用了外部函数的变量、参数,当外部函数返回后内部函数仍然保存了这些变量、参数。此时内部函数就成为了一个闭包。
JS和Dart中的闭包用法也几乎同样,示例:
JavaScript:
function makeAdder(addBy) { return (x) => addBy + x; } var add2 = makeAdder(2); var add4 = makeAdder(4); add2(3); // 5 add4(3); // 7
Dart:
Function makeAdder(num addBy) { return (num x) => addBy + x; } var add2 = makeAdder(2); var add4 = makeAdder(4); add2(3); // 5 add4(3); // 7
Class是面向对象编程语言的核心特性,JavaScript虽然是函数式语言,但在ES6中仍是引入了class。JavaScript的class本质上是基于原型继承的封装,是语法糖而已,并非真正的面向对象继承模型的实现。而Dart天生就是面向对象的语言,因此Dart的class相比JS更加的强大和完善。
本文只比较JS和Dart的class都有的特性,而Dart的其余特性你们看 官方文档 更合适。
JavaScript:
JS中定义一个class,有两种方式:类声明(class declaration)和类表达式(class expression)。咱们通常都会使用类声明。
类声明:
class Rectangle {}
类表达式:
var Rectangle = class {}
Dart:
而Dart中只支持类声明的方式,语法和JS一致:
class Rectangle {}
JavaScript:
JS中class的构造函数为统一的constructor
函数,每一个class只能定义一个构造函数。也能够不定义,这时会使用一个默认的构造函数。
class Rectangle { constructor(height, width) { this.height = height; this.width = width; } }
Dart:
Dart中,构造函数名称和类名相同,并且初始化成员变量以前须要先定义。
class Rectangle { num width, height; Rectangle(num height, num width) { this.height = height; this.width = width; } }
为了减小先定义再初始化成员变量的繁琐,Dart提供了语法糖来简化这个过程:
class Rectangle { num width, height; Rectangle(this.width, this.height); }
与JS不一样的是Dart的class能定义多个构造函数,被称为命名构造函数(named constructor),例以下面的Rectangle.square()
:
class Rectangle { num width, height; Rectangle(this.width, this.height); Rectangle.square() { width = 100; height = 100; } }
JavaScript:
JS中class的构造函数能够继承,当子类未定义构造函数时默认会使用父类的构造函数:
constructor(...args) { super(...args); }
而子类也能够经过调用父类的构造函数建立:
class Square extends Rectangle {} const square = new Square(100, 100);
Dart:
Dart中,构造函数是不能继承的! 这是Dart区别于其余不少高级语言的地方。可是当子类未定义任何构造函数时会默认使用父类的无参构造函数(no-argument constructor)。
若是要在子类中使用和父类同样的构造函数,必须在子类中再次定义,例如这样是不行的:
class Rectangle { num width, height; Rectangle(); Rectangle.size(width, height) { this.width = width; this.height = height; } } class Square extends Rectangle {} var square = Square.size(100, 100);
须要在子类中再定义一个.size()
构造函数:
class Square extends Rectangle { Square.size(size) { width = size; height = size; } } var square = Square.size(100);
或者也能够重定向到父类的构造函数:
class Square extends Rectangle { Square.size(size) : super.size(size, size); }
JavaScript:
JS中成员变量无需定义就能使用,可是为了类结构更清晰仍是推荐先定义。成员变量能够在定义时初始化,也能够只定义而不初始化,例如:
class Rectangle { height = 0; width; constructor(height, width) { this.height = height; this.width = width; } }
Private变量:
成员变量默认是公共的(public),也能够定义为私有(private),只需在变量名前面加#
:
class Rectangle { #height = 0; #width; constructor(height, width) { this.#height = height; this.#width = width; } }
Static变量:
JS的class还支持静态(static)变量,静态变量只会建立一份,由全部的实例公用,适合存放固定不变的值。例如:
class ClassWithStaticField { static staticField = 'static field'; }
Dart:
Dart中的成员变量定义方式和JS相似,能够只声明也能够声明+初始化:
class Rectangle { num width; num height = 100; }
Private变量:
一样Dart中也能够定义private变量,与JS在变量名以前加#
不一样,Dart在前面加_
。但Dart中的private是相对于library,而不是class。
Static变量:
Dart也支持定义静态变量:
class Queue { static const initialCapacity = 16; } print(Queue.initialCapacity); // 16
和成员变量相似,成员函数也分为public、private、static。
JavaScript:
JS中成员函数默认为public,好比:
class Rectangle { constructor(height, width) { this.height = height; this.width = width; } getArea() { return this.height * this.width; } }
Private方法:
定义private的成员函数也在函数名前面加#
就好了:
class Rectangle { #getArea() { return this.height * this.width; } }
Static方法:
定义Static的成员函数只需在函数名前面加static
关键词:
class DemoClass { static #privateStaticMethod() { return 42; } static publicStaticMethod() { return DemoClass.#privateStaticMethod(); } }
Dart:
Dart中class的成员函数定义和JS相似:
class Rectangle { num width; num height = 200; Rectangle(this.width, this.height); num getArea() { return width * height; } } var rect = Rectangle(30, 40); rect.getArea(); // 1200
Private方法:
与变量同样,方法名前面加_
也会被认定为私有方法。
Static方法:
静态方法的定义也是在前面加上static
关键词。
import 'dart:math'; class Point { num x, y; Point(this.x, this.y); static num distanceBetween(Point a, Point b) { var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } }
使用Dart进行异步编程总会有似曾相识感,和JS同样均可以使用回调函数、和Promise
一模一样的Future
还有async/await
语法。
Javascript:
function fn(callback) { setTimeout(callback, 1000); } fn(() => { console.log('hello world') });
Dart:
import 'dart:async'; fn(Function callback){ Timer(Duration(seconds: 1), callback); } fn((){ print('hello world'); });
和 Javascript 中的Promise
相似,Dart 提供了Future
用于表示异步操做最终完成的结果。
Javascript:
function getIP() { return fetch('https://httpbin.org/ip') .then(res => res.json()) .then(resJson => { const ip = resJson.origin; return ip; }); } getIP() .then(ip => console.log(`my ip is ${ip}`)) .catch(err => console.error(err));
Dart:
import 'dart:convert'; import 'package:http/http.dart' as http; Future<String> getIP() { return http.get('https://httpbin.org/ip').then((res) { String ip = jsonDecode(res.body)['origin']; return ip; }); } getIP().then((ip) => print('my ip is $ip')).catchError((error) => print(error));
ES2017中引入的async/await
语法进一步提高了异步编程的体验,用同步语法进行异步编程,好比:
JavaScript:
async function getIP() { const res = await fetch('https://httpbin.org/ip'); const resJson = await res.json(); const ip = resJson.origin; return ip; } async function main() { try { const ip = await getIP(); console.log(`my ip is ${ip}`); } catch (err) { console.error(err); } } main();
Dart:
Dart的async/await
语法几乎和JS相同,与JS的async方法返回Promise
对象相似,Dart的async方法返回一个Future
对象。
void main() async { Future<String> getIP() async { final res = await http.get('https://httpbin.org/ip'); String ip = jsonDecode(res.body)['origin']; return ip; } try { final ip = await getIP(); print('my ip is $ip'); } catch (err) { print(err); } }
有未涉及到的JS和Dart的语法对比,欢迎你们补充,我会及时更新。