最近在學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的語法對比,歡迎你們補充,我會及時更新。