Dorado客戶端JS代碼編寫規(guī)范

2024-03-07 18:37 更新

JavaScript和Java雖然在語法上有些相近,但其實(shí)是差別很大的兩種編程語言!

Dorado是全新的事物,也必然會(huì)帶來很多全新的使用方法和最佳實(shí)踐。當(dāng)然,最佳實(shí)踐這種東西原本是需要一些時(shí)間和應(yīng)用來積累和驗(yàn)證的,在目前階段我只能根據(jù)個(gè)人的經(jīng)驗(yàn)來大體總結(jié)出一些開發(fā)建議。 本文列舉了一些我近期在各項(xiàng)目中看到的不夠好的JavaScript代碼,既有關(guān)于JavaScript本身的,也有關(guān)于Dorado的。

了解JavaScript中的邏輯判斷

JavaScript不是Java!JavaScript是弱類型語言!所有類型的數(shù)值都可以用于邏輯判斷和邏輯運(yùn)算。 所以要搞清楚String、Number、Boolean、Object這些類型在用于邏輯判斷時(shí)都會(huì)出現(xiàn)什么樣的結(jié)果。 例如不要編寫如下這么啰嗦的代碼來判斷一個(gè)字符串是否有效:

if (str != null && str != "") {
    ... ...
}

你完全可以這樣寫:

if (str) {
    ... ...
}

也可以進(jìn)行邏輯運(yùn)算:

if (!str) {
    ... ...
}

其他類型的變量同樣如此。

熟練運(yùn)用Dorado對(duì)象的get和set

Dorado對(duì)象的get和set方法提供了很多實(shí)用的小技巧,善用這些技巧可以事半功倍。并且以下的這些技巧可以自由的搭配使用,以便于讓get和set在各種復(fù)雜的場景中發(fā)揮出最大的功效。 使用批量set 當(dāng)需要對(duì)一個(gè)對(duì)象的多個(gè)屬性進(jìn)行連續(xù)的賦值時(shí),應(yīng)盡可能使下面的語法:

foo.set({
    readOnly: true,
    value: "dorado"
})

而下面這種等效的寫法是不被推薦的,既羅嗦又低效。

foo.set("readOnly", true);
foo.set("value", "dorado");

使用迭代式的get和set get和set方法都支持迭代式的屬性訪問,即通過"."來分割一組屬性名,交由此方法一層層向下挖掘并返回最終結(jié)果或設(shè)置目標(biāo)屬性。 當(dāng)進(jìn)行迭代式的讀取時(shí),系統(tǒng)會(huì)自動(dòng)判斷前一個(gè)屬性返回的對(duì)象是dorado.AttributeSupport的實(shí)例還是普通JSON對(duì)象,并藉此決定如何進(jìn)一步執(zhí)行讀取操作:

  • 如果碰到的中間對(duì)象dorado.AttributeSupport的實(shí)例,系統(tǒng)會(huì)自動(dòng)讀取它的Attribute
  • 如果碰到的中間對(duì)象是普通的JSON對(duì)象,系統(tǒng)會(huì)直接讀取它的屬性。

oop.get("address.postCode"); // 迭代式的屬性讀取
// 如果address的屬性值是一個(gè)dorado.AttributeSupport的實(shí)例,那么此行命令的效果相當(dāng)于oop.get("address").get("postCode")。
// 如果address的屬性值是一個(gè)JSON對(duì)象,那么此行命令的效果相當(dāng)于oop.get("address").postCode

 
oop.set("address.postCode", 54733226); // 迭代式的屬性賦值
// 相當(dāng)于oop.get("address").set("postCode",54733226) 或oop.get("address").postCode = 54733226

運(yùn)用某些對(duì)象為get方法提供的特殊擴(kuò)展 Dorado中的某些對(duì)象為get方法提供了特殊的擴(kuò)展,通過這些擴(kuò)展我們甚至可以訪問一些對(duì)象的Attribute之外的功能,這寫擴(kuò)展進(jìn)一步Dorado的開發(fā)體驗(yàn)。 View

  • 當(dāng)屬性名以'#'開頭時(shí),Dorado會(huì)將其后的內(nèi)容識(shí)別為控件的id。表示根據(jù)此id獲取相應(yīng)的控件。
  • 當(dāng)屬性名以'^'開頭時(shí),Dorado會(huì)將其后的內(nèi)容識(shí)別為對(duì)象的tag。表示根據(jù)此tag獲取相應(yīng)的對(duì)象。
  • 當(dāng)屬性名以'@'開頭時(shí),Dorado會(huì)將其后的內(nèi)容識(shí)別為DataType的name。表示根據(jù)此name獲取相應(yīng)的DataType。

DataType

  • 當(dāng)屬性名以'@'開頭時(shí),Dorado會(huì)將其后的內(nèi)容識(shí)別為其中某個(gè)PropertyDef的name。表示根據(jù)此name獲取相應(yīng)的PropertyDef。

DataSet

  • 當(dāng)屬性名以'data:'開頭時(shí),Dorado會(huì)將其后的內(nèi)容識(shí)別為datapath。表示利用該datapath調(diào)用此DataSet的queryData()方法。以上這些特殊的擴(kuò)展并不妨礙迭代式get和set調(diào)用。例如下面的用法是有效的。

// 首先獲得id為editorType的控件,然后返回其value屬性。
var value = view.get("#editorType.value");

// 首先獲得View中擁有advanceButton標(biāo)簽的所有對(duì)象,然后批量設(shè)置它們的disabled屬性。
view.set("^advanceButton.disabled", true);

// 首先獲得id為dataSetDepts的DataSet,然后返回其中"#.employees"代表的數(shù)據(jù)。
var employees = view.get("#dataSetDepts.data:#.employees");

利用set為對(duì)象綁定事件 set方法不但可以用于為對(duì)象的屬性賦值,同時(shí)也可以用于為對(duì)象中的事件添加事件監(jiān)聽器。

// 使用上文中提及的第一種方法為label屬性賦值,同時(shí)為onClick事件綁定一個(gè)監(jiān)聽器。
oop.set({
    label : "Sample Text",
    onClick : function(self, arg) {
        ... ...
    }
});

學(xué)會(huì)使用控件的tag屬性

通過設(shè)置tag屬性以便于批量的查找和操作一組控件或?qū)ο?,這是Dorado中非常有特色的一種功能。該功能可以在很多場景下極大的簡化代碼。 在利用tag查找對(duì)象時(shí)Dorado會(huì)將所有匹配的對(duì)象封裝成一個(gè)ObjectGroup并返回。通過ObjectGroup,我們可以對(duì)其中的所有對(duì)象進(jìn)行批量的屬性設(shè)置或方法調(diào)用。 在使用tag功能時(shí),建議利用View對(duì)象對(duì)get和set方法所作的"^"前綴擴(kuò)展,這可以讓代碼進(jìn)一步的得到簡化。

// 首先獲得View中擁有advanceButton標(biāo)簽的所有對(duì)象,然后批量設(shè)置它們的disabled屬性。
view.set("^advanceButton.disabled", true);

盡可能使用異步的數(shù)據(jù)通訊

  • 盡可能使用dataSet.flushAsync()而不是dataSet.flush()
  • 盡可能使用盡可能不要將action的async屬性設(shè)置為false盡可能使用ajax.request()而不是ajax.requestSync()
  • 盡管異步的數(shù)據(jù)通訊會(huì)帶來一定的復(fù)雜度,但是出于改變用戶界面整體操作效率和提高界面友好度的角度出發(fā)。我仍然強(qiáng)烈建議使用異步的通訊機(jī)制。要使用異步的通訊機(jī)制必須掌握閉包(回調(diào)函數(shù)的基本用法)。
  • 使用異步的通訊機(jī)制至少可以帶來以下兩方面的好處:
  • 在異步的通訊機(jī)制的執(zhí)行過程中瀏覽器不會(huì)出現(xiàn)假死現(xiàn)象,甚至對(duì)于某些異步通訊我們可以運(yùn)行用戶在執(zhí)行過程中進(jìn)行其他的頁面操作。
  • Dorado中提供了異步通訊的自動(dòng)合并功能,可以將短時(shí)間發(fā)出的多次異步通信合并成一個(gè)物理請求,降低網(wǎng)絡(luò)和服務(wù)器開銷。

減少無謂的dataSet.flush()

我在很多系統(tǒng)都見到過在頁面初始化階段執(zhí)行一到多次的dataSet.flush(),其實(shí)這些操作理論上都是可以避免的。 出現(xiàn)此類操作的主要原因是開發(fā)人員在頁面初始化階段通過JavaScript確定DataSet的parameter屬性的值,然后再利用dataSet.flush()從服務(wù)端提取相應(yīng)的數(shù)據(jù)。而這里所說的parameter在很多情況下是可以在服務(wù)端的頁面準(zhǔn)備階段直接獲得的。例如通過在DataSet的parameter屬性中填寫EL表達(dá)式的方式。

善用Argument和EL表達(dá)式簡化代碼

假設(shè)你需要根據(jù)外部條件來設(shè)置界面上的10個(gè)TextEditor的readOnly屬性,并且這種操作是一次性的,不會(huì)在用戶的操作過程中再次改變。那么利用控件的tag屬性在View的onReady事件中設(shè)置并不是最好的選項(xiàng)。這會(huì)讓代碼的可讀性變得稍差,更重要的是這會(huì)讓這10個(gè)控件在初始階段經(jīng)歷兩次刷新,對(duì)性能造成不顯著的損害。 對(duì)于這種情況,我們應(yīng)該考慮直接在服務(wù)端設(shè)定好這些readOnly屬性。例如直接在這10個(gè)?控件的readOnly屬性中編寫 ${req.operation=="edit"} 這樣的EL表達(dá)式。可是一旦未來此處的判斷邏輯面臨調(diào)整,你又必須再一次把這10個(gè)TextEditor全部找出來并一一調(diào)整其中的EL表達(dá)式。 為了避免這種尷尬,最好的辦法是在View中聲明一個(gè)Argument,將上面的EL表達(dá)式定義在這里并且讓10個(gè)TextEditor的readOnly屬性引用該Argument。例如:我們聲明一個(gè)名為為readOnly的Argument的,其值為 ${req.operation=="edit"},然后在TextEditor的readOnly屬性中通過 ${argument.readOnly}來引用它。

掌握each迭代

Dorado中的很多對(duì)象都支持each迭代,each迭代可以另代碼看起來更加簡潔明了。目前支持each迭代的對(duì)象包括:

  • Array(即標(biāo)準(zhǔn)的JavaScript數(shù)組,Dorado修改了JavaScript中Array的prototype)
  • dorado.EntityList
  • dorado.ObjectGroup
  • dorado.util.KeyedArray
  • dorado.util.KeyedList 例如以下是對(duì)Array的each迭代:

var s = '';
['A', 'B', 'C'].each(function(item) {
    s += item;
});
// s == "ABC"

不要總是使用JavaScript注釋

JavaScript是解釋執(zhí)行的語言,這一點(diǎn)不同與于Java,所以其對(duì)代碼注釋的處理也有所不同。在Java中,被注釋掉的代碼不會(huì)在運(yùn)行時(shí)產(chǎn)生影響。而在JavaScript中,被注釋掉的代碼仍然會(huì)被輸出到客戶端。這意味這著這些注釋仍然會(huì)產(chǎn)生網(wǎng)絡(luò)開銷。 在實(shí)際應(yīng)用的場景中,我甚至見到過很多Client端事件中除了大段的代碼注釋外沒有任何一行有效代碼的情況。這意味著Dorado仍會(huì)為這些注釋生成一個(gè)無用的事件監(jiān)聽器。 所以,對(duì)于那些已經(jīng)不再需要的代碼,請果斷刪除而不是使用注釋。你可能會(huì)覺得的這些代碼未來或還會(huì)用的到,但實(shí)際的情況是,99%的情況下你再也不會(huì)用到他們了,而且一個(gè)小時(shí)你就會(huì)把這些注釋忘到一干二凈。

美化你的JavaScript

編程就像寫作。要把一件事情講清楚,你既可以寫一篇流水帳也可以寫一篇文章,要怎樣做取決于你對(duì)待這件事情的態(tài)度。 這原本不應(yīng)該是本文應(yīng)該討論的話題,然而我在日常的工作中卻見到了太多類似下面這樣的糟糕代碼:

var sum = this.id("dataSet").get("#.price") * this.id("dataSet").get("#.num");
this.id("dataSet").get("#").set("sum", sum);
... ... ...
if (this.id("checkboxDetail").get("checked") == true) {
    this.id("textEditorHomePage").set("visible", true);
    this.id("textEditorEmail").set("visible", true);
    this.id("textEditorDesc").set("visible", true);
}
else {
    this.id("textEditorHomePage").set("visible", false);
    this.id("textEditorEmail").set("visible", false);
    this.id("textEditorDesc").set("visible", false);
}

如果以寫作水平的角度來評(píng)價(jià)這段代碼,大概還處在小學(xué)二年級(jí)水平。而實(shí)際的情況往往比這例子中的更加令人作嘔(幸虧瀏覽器不會(huì)吐)!代碼里充斥著無用的注釋片段、沒有基本的縮進(jìn)格式、定義之后又被廢棄的變量...... 我相信寫出類似水準(zhǔn)代碼的同事是知道好的代碼應(yīng)該是什么樣子的,之所以仍然會(huì)寫出這些丑陋的代碼,關(guān)鍵還在于態(tài)度。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)