新聞中心
對(duì)于其它語(yǔ)言,特別是Java和Javascript的開(kāi)發(fā)者,Dart語(yǔ)言設(shè)計(jì)得看起來(lái)很熟悉。如果你足夠努力,你可以使 Dart 就像是那些語(yǔ)言一樣。如果你非常努力,你甚至可以把Dart變?yōu)镕ortran,但是這樣你將錯(cuò)過(guò)Dart中獨(dú)特的、有趣的部分。

創(chuàng)新互聯(lián)為企業(yè)級(jí)客戶提高一站式互聯(lián)網(wǎng)+設(shè)計(jì)服務(wù),主要包括網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、App定制開(kāi)發(fā)、微信小程序定制開(kāi)發(fā)、宣傳片制作、LOGO設(shè)計(jì)等,幫助客戶快速提升營(yíng)銷能力和企業(yè)形象,創(chuàng)新互聯(lián)各部門(mén)都有經(jīng)驗(yàn)豐富的經(jīng)驗(yàn),可以確保每一個(gè)作品的質(zhì)量和創(chuàng)作周期,同時(shí)每年都有很多新員工加入,為我們帶來(lái)大量新的創(chuàng)意。
推薦專題:Google Dart新結(jié)構(gòu)化編程語(yǔ)言
本文將幫助您寫(xiě)出適用于Dart的獨(dú)特代碼。因?yàn)檎Z(yǔ)言仍在演進(jìn)中,所以這里的許多慣用語(yǔ)也在變化。語(yǔ)言中的有些地方我們?nèi)圆淮_定用什么***實(shí)踐好。(也許你能幫助我們。)但這里是一些要點(diǎn),有助于把你帶出Java或Javascript的思維慣式,進(jìn)入Dart。
構(gòu)造函數(shù)
本文將從每個(gè)對(duì)象生命周期的起始: 使用構(gòu)造函數(shù)開(kāi)始。每個(gè)對(duì)象都將在某一個(gè)時(shí)刻被構(gòu)造出來(lái),定義構(gòu)造函數(shù)是創(chuàng)建一個(gè)類的重要組成部分。這里Dart有些有趣的想法。
自動(dòng)初始化字段
首先是擺脫一些單調(diào)乏味的部分。許多構(gòu)造函數(shù)僅僅是簡(jiǎn)單地把參數(shù)賦值給字段,如:
- class Point {
- num x, y;
- Point(num x, num y) {
- this.x = x;
- this.y = y;
- }
- }
這樣我們這里不得不輸入4次x,僅僅是初始化一個(gè)字段。太爛了。我們可以做的更好:
- class Point {
- num x, y;
- Point(this.x, this.y);
- }
在參數(shù)列表中,如果參數(shù)前使用 this. ,那么這個(gè)名字的字段將自動(dòng)使用該參數(shù)值做初始化。這個(gè)例子也展示了另外一個(gè)小特性:如果一個(gè)構(gòu)造函數(shù)體完全是空的,那么可以只使用一個(gè)分號(hào)(;)替代 { }。
命名構(gòu)造函數(shù)
就像大部分動(dòng)態(tài)類型語(yǔ)言一樣,Dart不支持重載。對(duì)于方法而言,這沒(méi)有多少限制,因?yàn)槟憧偸强梢允褂貌煌拿郑菢?gòu)造函數(shù)就沒(méi)有這么幸運(yùn)了。為了緩和這種情況,Dart允許你定義命名構(gòu)造函數(shù):
- class Point {
- num x, y;
- Point(this.x, this.y);
- Point.zero() : x = 0, y = 0;
- Point.polar(num theta, num radius) {
- x = Math.cos(theta) * radius;
- y = Math.sin(theta) * radius;
- }
- }
這里這個(gè) Point 類有三個(gè)構(gòu)造函數(shù),一個(gè)標(biāo)準(zhǔn)的和二個(gè)命名的。你可以像下面這樣使用它們:
- var a = new Point(1, 2);
- var b = new Point.zero();
- var c = new Point.polar(Math.PI, 4.0);
注意我們這里在調(diào)用命名構(gòu)造函數(shù)的時(shí)候仍然使用了 new 。這樣它就不是一個(gè)靜態(tài)方法了。
工廠構(gòu)造函數(shù)
有一些和工廠(factory)相關(guān)的設(shè)計(jì)模式。當(dāng)你需要一些類的實(shí)例,但是想以更靈活一點(diǎn)的方式,而不是直接用硬編碼的方式調(diào)用具體類型的構(gòu)造函數(shù)的話,這時(shí)這些工廠模式開(kāi)始發(fā)揮作用了。如果你已經(jīng)有了一個(gè)實(shí)例也許會(huì)想返回之前已經(jīng)緩存的實(shí)例,或者也許你想返回一個(gè)不同類型的對(duì)象。
Dart支持這種模式,但不要求你改變創(chuàng)建對(duì)象時(shí)的樣子。而是讓你定義一個(gè)工廠構(gòu)造函數(shù)。當(dāng)你調(diào)用它的時(shí)候看起來(lái)像一個(gè)普通的構(gòu)造函數(shù)。但是實(shí)現(xiàn)可以做任何它想做的事情。例如:
- class Symbol {
- final String name;
- static Map
_cache; - factory Symbol(String name) {
- if (_cache == null) {
- _cache = {};
- }
- if (_cache.containsKey(name)) {
- return _cache[name];
- } else {
- final symbol = new Symbol._internal(name);
- _cache[name] = symbol;
- return symbol;
- }
- }
- Symbol._internal(this.name);
- }
這里我們有一個(gè)表示符號(hào)的類。一個(gè)符號(hào)就像一個(gè)字符串,但是我們保證在任意時(shí)間點(diǎn)上一個(gè)給定的名字只會(huì)有一個(gè)符號(hào)對(duì)象。這讓你能安全地比較兩個(gè)對(duì)象的相等性僅僅通過(guò)測(cè)試他們是同一個(gè)對(duì)象。
這里的默認(rèn)(未命名)構(gòu)造函數(shù)前加上了 factory 前綴。它告訴Dart這是一個(gè)工廠構(gòu)造函數(shù)。當(dāng)它被調(diào)用時(shí),它不會(huì)創(chuàng)建一個(gè)新對(duì)象。(工廠構(gòu)造函數(shù)中沒(méi)有 this 對(duì)象)。相反,期望你創(chuàng)建一個(gè)實(shí)例并明確地返回它。這里我們用給定的名字查找之前緩存的對(duì)象,如果找到了就重用它。
最酷的是調(diào)用者根本看不到這點(diǎn)。它們只需要:
- var a = new Symbol('something');
- var b = new Symbol('something');
第二個(gè) new 將返回之前緩存的對(duì)象。這很好,因?yàn)檫@意味著如果我們起初不需要工廠構(gòu)造函數(shù)但之后又認(rèn)識(shí)到需要時(shí),我們將不必把所有之前使用new的地方都改為使用靜態(tài)方法調(diào)用。
函數(shù)
像大部分現(xiàn)代語(yǔ)言一樣,函數(shù)是Dart中的頭等公民(first-class),帶有完整的閉包和輕量型語(yǔ)法支持。函數(shù)就像任何其它對(duì)象一樣,你應(yīng)毫不猶豫地自由使用它們。特別是,在Dart團(tuán)隊(duì)中我們大量使用函數(shù)用作事件處理器(event handler)。
Dart有三種創(chuàng)建函數(shù)的表示法:一個(gè)是命名函數(shù),一個(gè)是帶函數(shù)體的匿名函數(shù)和一個(gè)表達(dá)式語(yǔ)句函數(shù)。命名形式看起來(lái)像這樣:
- void sayGreeting(String salutation, String name) {
- final greeting = '$salutation $name';
- print(greeting);
- }
這看起來(lái)像是一個(gè)普通的C語(yǔ)言函數(shù)或者Java、Javascript中的方法。和C、C++不同的是,這些可以嵌入到另一個(gè)函數(shù)的中間。如果你不需要給出函數(shù)的名字,也可以使用匿名形式。和上面代碼類似,但沒(méi)有名字或返回類型,像這樣:
- window.on.click.add((event) {
- print('You clicked the window.');
- })
這里我們傳遞一個(gè)函數(shù)到add()方法注冊(cè)一個(gè)事件處理器。***,如果你需要一個(gè)真正的輕量型函數(shù),僅僅對(duì)單一表達(dá)式求值并返回,使用 => :
- var items = [1, 2, 3, 4, 5];
- var odd = items.filter((i) => i % 2 == 1);
- print(odd); // [1, 3, 5]
一個(gè)括號(hào)的參數(shù)列表,跟著一個(gè) => 和一個(gè)單一表達(dá)式就創(chuàng)建了一個(gè)帶參數(shù)并返回表達(dá)式結(jié)果的函數(shù)。
實(shí)際上,只要有可能我們自己更喜歡用這種箭頭函數(shù),因?yàn)樗?jiǎn)潔且容易識(shí)別,感謝 => 。我們經(jīng)常使用匿名函數(shù)作為事件處理器和回調(diào)函數(shù)。命名函數(shù)反而使用很少。
Dart 還有一個(gè)技巧,這是我最喜歡的語(yǔ)言特性之一:可以使用 => 定義成員。當(dāng)然,你可以這樣做:
- class Rectangle {
- num width, height;
- bool contains(num x, num y) {
- return (x < width) && (y < height);
- }
- num area() {
- return width * height;
- }
- }
但是當(dāng)你僅需要下面這樣做的時(shí)候,為什么還需要上面那樣呢:
- class Rectangle {
- num width, height;
- bool contains(num x, num y) => (x < width) && (y < height);
- num area() => width * height;
- }
我們發(fā)現(xiàn)箭頭函數(shù)非常適用于定義簡(jiǎn)單的getter和其它單行方法來(lái)實(shí)現(xiàn)計(jì)算或訪問(wèn)對(duì)象屬性。
#p#
字段,getters 和 setters
說(shuō)到屬性,Dart使用標(biāo)準(zhǔn)的 object.someProperty 語(yǔ)法使用它們。 當(dāng)屬性是類中一個(gè)真實(shí)的字段時(shí),大部分語(yǔ)言就是這樣做的。但是Dart 還允許你定義一些方法,它們看起來(lái)像是訪問(wèn)屬性,但實(shí)際上可以執(zhí)行任意你想要的代碼。在其它語(yǔ)言中,這些被稱為getters 和 setters。看這個(gè)例子:
- class Rectangle {
- num left, top, width, height;
- num get right() => left + width;
- set right(num value) => left = value - width;
- num get bottom() => top + height;
- set bottom(num value) => top = value - height;
- Rectangle(this.left, this.top, this.width, this.height);
- }
這里我們定義了一個(gè)Rectangle 類,它有四個(gè)真實(shí)的字段:left, top, width, 和 height。它還有兩對(duì)getters 和 setters方法定義了兩個(gè)額外的邏輯屬性:right 和 bottom。在你使用這個(gè)類時(shí),真實(shí)字段與 getters和setters 沒(méi)有明顯的區(qū)別:
- var rect = new Rectangle(3, 4, 20, 15);
- print(rect.left);
- print(rect.bottom);
- rect.top = 6;
- rect.right = 12;
字段和getters/setters間的模糊化是語(yǔ)言的基本原則??创钋宄姆绞骄褪钦J(rèn)為字段僅僅是魔法實(shí)現(xiàn)的getters 和 setters。這意味著你可以做些有趣的事情,比如用字段覆蓋繼承的getter方法,或反之。如果接口定義了一個(gè)getter,你可以簡(jiǎn)單地用一個(gè)同名、同類型的字段實(shí)現(xiàn)它。如果字段是可變的(非final),那么它也實(shí)現(xiàn)了接口要求的setter。
實(shí)際上,這意味著你不必防御性地把字段隱藏到getter、setter樣板方法里來(lái)隔離它們,就像你在Java或C#中所做的。如果你有需要暴露的屬性,只需用一個(gè)public的字段。如果你不想它們被修改,只需加上final。
稍后,如果你需要做一些驗(yàn)證或什么其它事情,你隨時(shí)可以用getter和setter代替這個(gè)字段。比如我們想確保 Rectangle類總是有非負(fù)的大小,我們可以把它改為這樣:
- class Rectangle {
- num left, top;
- num _width, _height;
- num get width() => _width;
- set width(num value) {
- if (value < 0) throw 'Width cannot be negative.';
- _width = value;
- }
- num get height() => _height;
- set height(num value) {
- if (value < 0) throw 'Height cannot be negative.';
- _height = value;
- }
- num get right() => left + width;
- set right(num value) => left = value - width;
- num get bottom() => top + height;
- set bottom(num value) => top = value - height;
- Rectangle(this.left, this.top, this._width, this._height);
- }
現(xiàn)在我們修改這個(gè)類增加了一些驗(yàn)證,但根本不影響那些已經(jīng)使用了它的代碼。
頂層定義
Dart是“純”面向?qū)ο笳Z(yǔ)言,變量中的任何東西都是一個(gè)真正的對(duì)象(沒(méi)有突變的原始類型),并且每個(gè)對(duì)象都是某個(gè)類的實(shí)例。然而它不是教條式的OOP語(yǔ)言。它不要求你把每個(gè)東西都定義在類里。相反,如果你愿意,你可以在頂層自由地定義函數(shù)、變量甚至是getters和setters。
- num abs(num value) => value < 0 ? -value : value;
- final TWO_PI = Math.PI * 2.0;
- int get today() {
- final date = new Date.now();
- return date.day;
- }
即使是那些不要求你把所有東西都放在類或?qū)ο笾械恼Z(yǔ)言,如Javascript,它們一般仍然是用一種命名空間的形式:相同名字的頂層定義會(huì)導(dǎo)致不經(jīng)意的沖突。為解決這個(gè)問(wèn)題,Dart使用一種 library 系統(tǒng),允許你用一個(gè)前綴導(dǎo)入其它庫(kù)中的定義來(lái)消除歧義。這意味著你不應(yīng)該需要防御式地把定義放到類中。
我們?nèi)栽谔剿鬟@實(shí)際意味著我們應(yīng)如何定義庫(kù)。我們的大部分代碼是把定義放到類中的,如Math。很難說(shuō)這僅是我們?cè)谄渌Z(yǔ)言中根深蒂固的習(xí)慣還是說(shuō)對(duì)Dart而言這也是一種好的實(shí)踐方式。我們期待這方面的反饋。
我們確實(shí)有一些使用頂層定義的例子。首先你需要運(yùn)行的main()函數(shù)就是要在頂層定義的。如果你使用DOM,熟悉的document 和 window “變量”實(shí)際上是Dart中頂層定義的getters 。
字符串和插值
Dart有幾種字符串字面值。你可以用單引號(hào)或雙引號(hào),也可以用三引號(hào)的多行字符串:
- 'I am a "string"'
- "I'm one too"
- '''I'm
- on multiple lines
- '''
- """
- As
- am
- I
- """
為了構(gòu)造更大的字符串,使用+ 連接它們即可:
- var name = 'Fred';
- var salutation = 'Hi';
- var greeting = salutation + ', ' + name;
但是使用字符串插值會(huì)更快更清晰:
- var name = 'Fred';
- var salutation = 'Hi';
- var greeting = '$salutation, $name';
在字符串中,一個(gè)美元符號(hào)($)跟著一個(gè)變量將被擴(kuò)展為該變量的值。(如果變量不是字符串將調(diào)用它的toString()方法)。你也可以在大括號(hào)里插入表達(dá)式:
- var r = 2;
- print('The area of a circle with radius $r is ${Math.PI * r * r}');
操作符
Dart使用你熟悉的C、Java語(yǔ)言里一樣的操作符和優(yōu)先級(jí)。它們會(huì)按你期望的方式工作。而在背后,它們有點(diǎn)特殊。在Dart中,使用操作符的表達(dá)式如1+2,實(shí)際上僅是調(diào)用方法的語(yǔ)法糖。對(duì)于語(yǔ)言,這個(gè)例子看起來(lái)更像是1.+(2) 。
這意味著你也可以為你自己的類型重載(多數(shù))操作符。例如這是一個(gè)Vector 類:
- class Vector {
- num x, y;
- Vector(this.x, this.y);
- operator +(Vector other) => new Vector(x + other.x, y + other.y);
- }
這樣,我們可以使用熟悉的語(yǔ)法形式做向量加法:
- var position = new Vector(3, 4);
- var velocity = new Vector(1, 2);
- var newPosition = position + velocity;
話雖如此,請(qǐng)不要過(guò)度濫用。我們給你汽車的鑰匙,并且相信你不會(huì)掉頭把車開(kāi)到客廳里。
在實(shí)踐上,如果你定義的類型在“現(xiàn)實(shí)世界”中(在黑板上?)經(jīng)常使用操作符,那么它可能是一個(gè)好的操作符重載候選者,如:復(fù)數(shù)、向量、矩陣等。另外,也不一定。自定義操作符的類型一般也應(yīng)該是不可變類型。
注意因?yàn)椴僮鞣{(diào)用實(shí)際上僅僅是方法調(diào)用,它們具有固有的不對(duì)稱性。方法總是在左邊的參數(shù)上獲取。所以當(dāng)你做a+b的時(shí)候,是根據(jù) a 的類型決定其意義的。
相等性
有一類操作符需要特別注意。Dart有兩類相等運(yùn)算符:== 和 !=, 與 === 和 !== 。Javascript開(kāi)發(fā)者應(yīng)該很熟悉,但是這里有點(diǎn)區(qū)別。
== 和 != 做等價(jià)測(cè)試。它們應(yīng)該是你99%的時(shí)候使用的。和Javascript不同,它們不做任何隱式轉(zhuǎn)換,所以它們應(yīng)該像你所期待的那樣的行為。別害怕使用它們。和Java不同,它們適用于任何具有等價(jià)關(guān)系定義的類型。不再需要 someString.equals("something") 這樣了。
你可以為你自己的類型重載 == ,只要它們有意義。你不必重載 != ,Dart 自動(dòng)根據(jù)你的 == 定義做推斷。
其它操作符, === 和 !== 用來(lái)測(cè)試身份。a === b 僅當(dāng) a 和 b 是內(nèi)存中完全相同的對(duì)象時(shí)才返回 true 。默認(rèn)情況下,如果類型沒(méi)有定義有意義的相等操作符,那么 == 調(diào)用將退回到 === 。所以你唯一需要用這個(gè)的時(shí)候是你明確地想要繞過(guò)任何用戶定義的 == 操作符。
原文:http://han.guokai.blog.163.com/blog/static/1367182712011925112734306/
文章標(biāo)題:Dart語(yǔ)言慣用語(yǔ)——Dart中特有的代碼味道
分享鏈接:http://www.dlmjj.cn/article/djgsjsh.html


咨詢
建站咨詢
